1 Commits

Author SHA1 Message Date
John Alanbrook
ba78eb632f initial attempt 2025-06-14 17:59:33 -05:00
2 changed files with 22 additions and 534 deletions

View File

@@ -154,8 +154,6 @@ enum {
JS_CLASS_BIG_INT, /* u.object_data */ JS_CLASS_BIG_INT, /* u.object_data */
JS_CLASS_MAP, /* u.map_state */ JS_CLASS_MAP, /* u.map_state */
JS_CLASS_SET, /* u.map_state */ JS_CLASS_SET, /* u.map_state */
JS_CLASS_WEAKMAP, /* u.map_state */
JS_CLASS_WEAKSET, /* u.map_state */
JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */ JS_CLASS_MAP_ITERATOR, /* u.map_iterator_data */
JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */ JS_CLASS_SET_ITERATOR, /* u.map_iterator_data */
JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */ JS_CLASS_ARRAY_ITERATOR, /* u.array_iterator_data */
@@ -172,7 +170,6 @@ enum {
JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */ JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */ JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */ JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
JS_CLASS_WEAK_REF,
JS_CLASS_FINALIZATION_REGISTRY, JS_CLASS_FINALIZATION_REGISTRY,
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */ JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
@@ -254,7 +251,6 @@ struct JSRuntime {
struct list_head tmp_obj_list; /* used during GC */ struct list_head tmp_obj_list; /* used during GC */
JSGCPhaseEnum gc_phase : 8; JSGCPhaseEnum gc_phase : 8;
size_t malloc_gc_threshold; size_t malloc_gc_threshold;
struct list_head weakref_list; /* list of JSWeakRefHeader.link */
#ifdef DUMP_LEAKS #ifdef DUMP_LEAKS
struct list_head string_list; /* list of JSString.link */ struct list_head string_list; /* list of JSString.link */
#endif #endif
@@ -354,17 +350,6 @@ struct JSGCObjectHeader {
struct list_head link; struct list_head link;
}; };
typedef enum {
JS_WEAKREF_TYPE_MAP,
JS_WEAKREF_TYPE_WEAKREF,
JS_WEAKREF_TYPE_FINREC,
} JSWeakRefHeaderTypeEnum;
typedef struct {
struct list_head link;
JSWeakRefHeaderTypeEnum weakref_type;
} JSWeakRefHeader;
typedef struct JSVarRef { typedef struct JSVarRef {
union { union {
JSGCObjectHeader header; /* must come first */ JSGCObjectHeader header; /* must come first */
@@ -934,10 +919,7 @@ struct JSObject {
uint16_t class_id; /* see JS_CLASS_x */ uint16_t class_id; /* see JS_CLASS_x */
}; };
}; };
/* count the number of weak references to this object. The object
structure is freed only if header.ref_count = 0 and
weakref_count = 0 */
uint32_t weakref_count;
JSShape *shape; /* prototype and property names + flag */ JSShape *shape; /* prototype and property names + flag */
JSProperty *prop; /* array of properties */ JSProperty *prop; /* array of properties */
union { union {
@@ -1009,7 +991,6 @@ typedef struct JSMapRecord {
} JSMapRecord; } JSMapRecord;
typedef struct JSMapState { typedef struct JSMapState {
BOOL is_weak; /* TRUE if WeakSet/WeakMap */
struct list_head records; /* list of JSMapRecord.link */ struct list_head records; /* list of JSMapRecord.link */
uint32_t record_count; uint32_t record_count;
JSMapRecord **hash_table; JSMapRecord **hash_table;
@@ -1017,7 +998,6 @@ typedef struct JSMapState {
uint32_t hash_size; /* = 2 ^ hash_bits */ uint32_t hash_size; /* = 2 ^ hash_bits */
uint32_t record_count_threshold; /* count at which a hash table uint32_t record_count_threshold; /* count at which a hash table
resize is needed */ resize is needed */
JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */
} JSMapState; } JSMapState;
enum { enum {
@@ -1288,10 +1268,7 @@ static JSValue JS_InstantiateFunctionListItem2(JSContext *ctx, JSObject *p,
JSAtom atom, void *opaque); JSAtom atom, void *opaque);
static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val, static JSValue js_object_groupBy(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int is_map); int argc, JSValueConst *argv, int is_map);
static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh); static void JS_RunGCInternal(JSRuntime *rt);
static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh);
static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects);
static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen, static JSValue js_array_from_iterator(JSContext *ctx, uint32_t *plen,
JSValueConst obj, JSValueConst method); JSValueConst obj, JSValueConst method);
static int js_string_find_invalid_codepoint(JSString *p); static int js_string_find_invalid_codepoint(JSString *p);
@@ -1524,8 +1501,6 @@ static JSClassShortDef const js_std_class_def[] = {
{ JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */ { JS_ATOM_BigInt, js_object_data_finalizer, js_object_data_mark }, /* JS_CLASS_BIG_INT */
{ JS_ATOM_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */ { 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_Set, js_map_finalizer, js_map_mark }, /* JS_CLASS_SET */
{ JS_ATOM_WeakMap, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKMAP */
{ JS_ATOM_WeakSet, js_map_finalizer, js_map_mark }, /* JS_CLASS_WEAKSET */
{ JS_ATOM_Map_Iterator, js_map_iterator_finalizer, js_map_iterator_mark }, /* JS_CLASS_MAP_ITERATOR */ { 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_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_Array_Iterator, js_array_iterator_finalizer, js_array_iterator_mark }, /* JS_CLASS_ARRAY_ITERATOR */
@@ -1602,7 +1577,6 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
init_list_head(&rt->gc_obj_list); init_list_head(&rt->gc_obj_list);
init_list_head(&rt->gc_zero_ref_count_list); init_list_head(&rt->gc_zero_ref_count_list);
rt->gc_phase = JS_GC_PHASE_NONE; rt->gc_phase = JS_GC_PHASE_NONE;
init_list_head(&rt->weakref_list);
#ifdef DUMP_LEAKS #ifdef DUMP_LEAKS
init_list_head(&rt->string_list); init_list_head(&rt->string_list);
@@ -1915,7 +1889,7 @@ void JS_FreeRuntime(JSRuntime *rt)
/* don't remove the weak objects to avoid create new jobs with /* don't remove the weak objects to avoid create new jobs with
FinalizationRegistry */ FinalizationRegistry */
JS_RunGCInternal(rt, FALSE); JS_RunGCInternal(rt);
#ifdef DUMP_LEAKS #ifdef DUMP_LEAKS
/* leaking objects */ /* leaking objects */
@@ -1957,7 +1931,6 @@ void JS_FreeRuntime(JSRuntime *rt)
} }
#endif #endif
assert(list_empty(&rt->gc_obj_list)); assert(list_empty(&rt->gc_obj_list));
assert(list_empty(&rt->weakref_list));
/* free the classes */ /* free the classes */
for(i = 0; i < rt->class_count; i++) { for(i = 0; i < rt->class_count; i++) {
@@ -2132,8 +2105,6 @@ JSContext *JS_NewContext(JSRuntime *rt)
JS_AddIntrinsicProxy(ctx); JS_AddIntrinsicProxy(ctx);
JS_AddIntrinsicMapSet(ctx); JS_AddIntrinsicMapSet(ctx);
JS_AddIntrinsicTypedArrays(ctx); JS_AddIntrinsicTypedArrays(ctx);
JS_AddIntrinsicPromise(ctx);
JS_AddIntrinsicWeakRef(ctx);
return ctx; return ctx;
} }
@@ -5063,7 +5034,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
p->has_immutable_prototype = 0; p->has_immutable_prototype = 0;
p->tmp_mark = 0; p->tmp_mark = 0;
p->is_HTMLDDA = 0; p->is_HTMLDDA = 0;
p->weakref_count = 0;
p->u.opaque = NULL; p->u.opaque = NULL;
p->shape = sh; p->shape = sh;
p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size); p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
@@ -5758,7 +5728,7 @@ static void free_object(JSRuntime *rt, JSObject *p)
remove_gc_object(&p->header); remove_gc_object(&p->header);
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) { if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) {
if (p->header.ref_count == 0 && p->weakref_count == 0) { if (p->header.ref_count == 0) {
js_free_rt(rt, p); js_free_rt(rt, p);
} else { } else {
/* keep the object structure because there are may be /* keep the object structure because there are may be
@@ -5766,12 +5736,7 @@ static void free_object(JSRuntime *rt, JSObject *p)
list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list); list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
} }
} else { } else {
/* keep the object structure in case there are weak references to it */ js_free_rt(rt, p);
if (p->weakref_count == 0) {
js_free_rt(rt, p);
} else {
p->header.mark = 0; /* reset the mark so that the weakref can be freed */
}
} }
} }
@@ -5890,36 +5855,6 @@ void __JS_FreeValue(JSContext *ctx, JSValue v)
/* garbage collection */ /* garbage collection */
static void gc_remove_weak_objects(JSRuntime *rt)
{
struct list_head *el;
/* add the freed objects to rt->gc_zero_ref_count_list so that
rt->weakref_list is not modified while we traverse it */
rt->gc_phase = JS_GC_PHASE_DECREF;
list_for_each(el, &rt->weakref_list) {
JSWeakRefHeader *wh = list_entry(el, JSWeakRefHeader, link);
switch(wh->weakref_type) {
case JS_WEAKREF_TYPE_MAP:
map_delete_weakrefs(rt, wh);
break;
case JS_WEAKREF_TYPE_WEAKREF:
weakref_delete_weakref(rt, wh);
break;
case JS_WEAKREF_TYPE_FINREC:
finrec_delete_weakref(rt, wh);
break;
default:
abort();
}
}
rt->gc_phase = JS_GC_PHASE_NONE;
/* free the freed objects here. */
free_zero_refcount(rt);
}
static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h, static void add_gc_object(JSRuntime *rt, JSGCObjectHeader *h,
JSGCObjectTypeEnum type) JSGCObjectTypeEnum type)
{ {
@@ -6170,27 +6105,14 @@ static void gc_free_cycles(JSRuntime *rt)
assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT || assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE || p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE ||
p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION); p->gc_obj_type == JS_GC_OBJ_TYPE_ASYNC_FUNCTION);
if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT && js_free_rt(rt, p);
((JSObject *)p)->weakref_count != 0) {
/* keep the object because there are weak references to it */
p->mark = 0;
} else {
js_free_rt(rt, p);
}
} }
init_list_head(&rt->gc_zero_ref_count_list); init_list_head(&rt->gc_zero_ref_count_list);
} }
static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects) static void JS_RunGCInternal(JSRuntime *rt)
{ {
if (remove_weak_objects) {
/* free the weakly referenced object or symbol structures, delete
the associated Map/Set entries and queue the finalization
registry callbacks. */
gc_remove_weak_objects(rt);
}
/* decrement the reference of the children of each object. mark = /* decrement the reference of the children of each object. mark =
1 after this pass. */ 1 after this pass. */
gc_decref(rt); gc_decref(rt);
@@ -6204,7 +6126,7 @@ static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects)
void JS_RunGC(JSRuntime *rt) void JS_RunGC(JSRuntime *rt)
{ {
JS_RunGCInternal(rt, TRUE); JS_RunGCInternal(rt);
} }
/* Return false if not an object or if the object has already been /* Return false if not an object or if the object has already been
@@ -6512,8 +6434,6 @@ void JS_ComputeMemoryUsage(JSRuntime *rt, JSMemoryUsage *s)
case JS_CLASS_DATAVIEW: /* u.typed_array */ case JS_CLASS_DATAVIEW: /* u.typed_array */
case JS_CLASS_MAP: /* u.map_state */ case JS_CLASS_MAP: /* u.map_state */
case JS_CLASS_SET: /* u.map_state */ case JS_CLASS_SET: /* u.map_state */
case JS_CLASS_WEAKMAP: /* u.map_state */
case JS_CLASS_WEAKSET: /* u.map_state */
case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */ case JS_CLASS_MAP_ITERATOR: /* u.map_iterator_data */
case JS_CLASS_SET_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_ARRAY_ITERATOR: /* u.array_iterator_data */
@@ -48193,81 +48113,7 @@ static const JSCFunctionListEntry js_symbol_funcs[] = {
/* Set/Map/WeakSet/WeakMap */ /* Set/Map/WeakSet/WeakMap */
static BOOL js_weakref_is_target(JSValueConst val)
{
switch (JS_VALUE_GET_TAG(val)) {
case JS_TAG_OBJECT:
return TRUE;
case JS_TAG_SYMBOL:
{
JSAtomStruct *p = JS_VALUE_GET_PTR(val);
if (p->atom_type == JS_ATOM_TYPE_SYMBOL &&
p->hash != JS_ATOM_HASH_PRIVATE)
return TRUE;
}
break;
default:
break;
}
return FALSE;
}
/* JS_UNDEFINED is considered as a live weakref */
/* XXX: add a specific JSWeakRef value type ? */
static BOOL js_weakref_is_live(JSValueConst val)
{
int *pref_count;
if (JS_IsUndefined(val))
return TRUE;
pref_count = JS_VALUE_GET_PTR(val);
return (*pref_count != 0);
}
/* 'val' can be JS_UNDEFINED */
static void js_weakref_free(JSRuntime *rt, JSValue val)
{
if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(val);
assert(p->weakref_count >= 1);
p->weakref_count--;
/* 'mark' is tested to avoid freeing the object structure when
it is about to be freed in a cycle or in
free_zero_refcount() */
if (p->weakref_count == 0 && p->header.ref_count == 0 &&
p->header.mark == 0) {
js_free_rt(rt, p);
}
} else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) {
JSString *p = JS_VALUE_GET_STRING(val);
assert(p->hash >= 1);
p->hash--;
if (p->hash == 0 && p->header.ref_count == 0) {
/* can remove the dummy structure */
js_free_rt(rt, p);
}
}
}
/* val must be an object, a symbol or undefined (see
js_weakref_is_target). */
static JSValue js_weakref_new(JSContext *ctx, JSValueConst val)
{
if (JS_VALUE_GET_TAG(val) == JS_TAG_OBJECT) {
JSObject *p = JS_VALUE_GET_OBJ(val);
p->weakref_count++;
} else if (JS_VALUE_GET_TAG(val) == JS_TAG_SYMBOL) {
JSString *p = JS_VALUE_GET_STRING(val);
/* XXX: could return an exception if too many references */
assert(p->hash < JS_ATOM_HASH_MASK - 2);
p->hash++;
} else {
assert(JS_IsUndefined(val));
}
return (JSValue)val;
}
#define MAGIC_SET (1 << 0) #define MAGIC_SET (1 << 0)
#define MAGIC_WEAK (1 << 1)
static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target, static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv, int magic) int argc, JSValueConst *argv, int magic)
@@ -48275,10 +48121,9 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
JSMapState *s; JSMapState *s;
JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED; JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
JSValueConst arr; JSValueConst arr;
BOOL is_set, is_weak; BOOL is_set;
is_set = magic & MAGIC_SET; is_set = magic & MAGIC_SET;
is_weak = ((magic & MAGIC_WEAK) != 0);
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic); obj = js_create_from_ctor(ctx, new_target, JS_CLASS_MAP + magic);
if (JS_IsException(obj)) if (JS_IsException(obj))
return JS_EXCEPTION; return JS_EXCEPTION;
@@ -48286,11 +48131,6 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
if (!s) if (!s)
goto fail; goto fail;
init_list_head(&s->records); init_list_head(&s->records);
s->is_weak = is_weak;
if (is_weak) {
s->weakref_header.weakref_type = JS_WEAKREF_TYPE_MAP;
list_add_tail(&s->weakref_header.link, &ctx->rt->weakref_list);
}
JS_SetOpaque(obj, s); JS_SetOpaque(obj, s);
s->hash_bits = 1; s->hash_bits = 1;
s->hash_size = 1U << s->hash_bits; s->hash_size = 1U << s->hash_bits;
@@ -48481,11 +48321,9 @@ static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
uint32_t h; uint32_t h;
h = map_hash_key(key, s->hash_bits); h = map_hash_key(key, s->hash_bits);
for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) { for(mr = s->hash_table[h]; mr != NULL; mr = mr->hash_next) {
if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { if (!mr->empty) {
/* cannot match */ if (js_same_value_zero(ctx, mr->key, key))
} else { return mr; // TODO: Check this logic. Why js_same_value_zero required?
if (js_same_value_zero(ctx, mr->key, key))
return mr;
} }
} }
return NULL; return NULL;
@@ -48510,11 +48348,10 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s)
list_for_each(el, &s->records) { list_for_each(el, &s->records) {
mr = list_entry(el, JSMapRecord, link); mr = list_entry(el, JSMapRecord, link);
if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { if (!mr->empty) {
} else { h = map_hash_key(mr->key, new_hash_bits);
h = map_hash_key(mr->key, new_hash_bits); mr->hash_next = new_hash_table[h];
mr->hash_next = new_hash_table[h]; new_hash_table[h] = mr;
new_hash_table[h] = mr;
} }
} }
s->hash_table = new_hash_table; s->hash_table = new_hash_table;
@@ -48534,11 +48371,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
return NULL; return NULL;
mr->ref_count = 1; mr->ref_count = 1;
mr->empty = FALSE; mr->empty = FALSE;
if (s->is_weak) { mr->key = JS_DupValue(ctx, key);
mr->key = js_weakref_new(ctx, key);
} else {
mr->key = JS_DupValue(ctx, key);
}
h = map_hash_key(key, s->hash_bits); h = map_hash_key(key, s->hash_bits);
mr->hash_next = s->hash_table[h]; mr->hash_next = s->hash_table[h];
s->hash_table[h] = mr; s->hash_table[h] = mr;
@@ -48556,11 +48389,7 @@ static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
if (mr->empty) if (mr->empty)
return; return;
if (s->is_weak) { JS_FreeValueRT(rt, mr->key);
js_weakref_free(rt, mr->key);
} else {
JS_FreeValueRT(rt, mr->key);
}
JS_FreeValueRT(rt, mr->value); JS_FreeValueRT(rt, mr->value);
if (--mr->ref_count == 0) { if (--mr->ref_count == 0) {
list_del(&mr->link); list_del(&mr->link);
@@ -48584,38 +48413,6 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
} }
} }
static void map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh)
{
JSMapState *s = container_of(wh, JSMapState, weakref_header);
struct list_head *el, *el1;
JSMapRecord *mr1, **pmr;
uint32_t h;
list_for_each_safe(el, el1, &s->records) {
JSMapRecord *mr = list_entry(el, JSMapRecord, link);
if (!js_weakref_is_live(mr->key)) {
/* even if key is not live it can be hashed as a pointer */
h = map_hash_key(mr->key, s->hash_bits);
pmr = &s->hash_table[h];
for(;;) {
mr1 = *pmr;
/* the entry may already be removed from the hash
table if the map was resized */
if (mr1 == NULL)
goto done;
if (mr1 == mr)
break;
pmr = &mr1->hash_next;
}
/* remove from the hash table */
*pmr = mr1->hash_next;
done:
map_delete_record(rt, s, mr);
}
}
}
static JSValue js_map_set(JSContext *ctx, JSValueConst this_val, static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv, int magic) int argc, JSValueConst *argv, int magic)
{ {
@@ -48626,8 +48423,6 @@ static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
if (!s) if (!s)
return JS_EXCEPTION; return JS_EXCEPTION;
key = map_normalize_key(ctx, argv[0]); key = map_normalize_key(ctx, argv[0]);
if (s->is_weak && !js_weakref_is_target(key))
return JS_ThrowTypeError(ctx, "invalid value used as %s key", (magic & MAGIC_SET) ? "WeakSet" : "WeakMap");
if (magic & MAGIC_SET) if (magic & MAGIC_SET)
value = JS_UNDEFINED; value = JS_UNDEFINED;
else else
@@ -48693,11 +48488,9 @@ static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
mr = *pmr; mr = *pmr;
if (mr == NULL) if (mr == NULL)
return JS_FALSE; return JS_FALSE;
if (mr->empty || (s->is_weak && !js_weakref_is_live(mr->key))) { if (!mr->empty) {
/* not valid */
} else {
if (js_same_value_zero(ctx, mr->key, key)) if (js_same_value_zero(ctx, mr->key, key))
break; break; // TODO: why js_same_value_zero
} }
pmr = &mr->hash_next; pmr = &mr->hash_next;
} }
@@ -48917,18 +48710,12 @@ static void js_map_finalizer(JSRuntime *rt, JSValue val)
list_for_each_safe(el, el1, &s->records) { list_for_each_safe(el, el1, &s->records) {
mr = list_entry(el, JSMapRecord, link); mr = list_entry(el, JSMapRecord, link);
if (!mr->empty) { if (!mr->empty) {
if (s->is_weak) JS_FreeValueRT(rt, mr->key);
js_weakref_free(rt, mr->key);
else
JS_FreeValueRT(rt, mr->key);
JS_FreeValueRT(rt, mr->value); JS_FreeValueRT(rt, mr->value);
} }
js_free_rt(rt, mr); js_free_rt(rt, mr);
} }
js_free_rt(rt, s->hash_table); js_free_rt(rt, s->hash_table);
if (s->is_weak) {
list_del(&s->weakref_header.link);
}
js_free_rt(rt, s); js_free_rt(rt, s);
} }
} }
@@ -48944,8 +48731,7 @@ static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
if (s) { if (s) {
list_for_each(el, &s->records) { list_for_each(el, &s->records) {
mr = list_entry(el, JSMapRecord, link); mr = list_entry(el, JSMapRecord, link);
if (!s->is_weak) JS_MarkValue(rt, mr->key, mark_func);
JS_MarkValue(rt, mr->key, mark_func);
JS_MarkValue(rt, mr->value, mark_func); JS_MarkValue(rt, mr->value, mark_func);
} }
} }
@@ -49128,26 +48914,9 @@ static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "Set Iterator", JS_PROP_CONFIGURABLE ), 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] = { static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
js_map_proto_funcs, js_map_proto_funcs,
js_set_proto_funcs, js_set_proto_funcs,
js_weak_map_proto_funcs,
js_weak_set_proto_funcs,
js_map_iterator_proto_funcs, js_map_iterator_proto_funcs,
js_set_iterator_proto_funcs, js_set_iterator_proto_funcs,
}; };
@@ -49155,8 +48924,6 @@ static const JSCFunctionListEntry * const js_map_proto_funcs_ptr[6] = {
static const uint8_t js_map_proto_funcs_count[6] = { static const uint8_t js_map_proto_funcs_count[6] = {
countof(js_map_proto_funcs), countof(js_map_proto_funcs),
countof(js_set_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_map_iterator_proto_funcs),
countof(js_set_iterator_proto_funcs), countof(js_set_iterator_proto_funcs),
}; };
@@ -55587,283 +55354,6 @@ void JS_AddIntrinsicTypedArrays(JSContext *ctx)
#endif #endif
} }
/* WeakRef */
typedef struct JSWeakRefData {
JSWeakRefHeader weakref_header;
JSValue target;
} JSWeakRefData;
static void js_weakref_finalizer(JSRuntime *rt, JSValue val)
{
JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF);
if (!wrd)
return;
js_weakref_free(rt, wrd->target);
list_del(&wrd->weakref_header.link);
js_free_rt(rt, wrd);
}
static void weakref_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
{
JSWeakRefData *wrd = container_of(wh, JSWeakRefData, weakref_header);
if (!js_weakref_is_live(wrd->target)) {
js_weakref_free(rt, wrd->target);
wrd->target = JS_UNDEFINED;
}
}
static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSValueConst arg;
JSValue obj;
if (JS_IsUndefined(new_target))
return JS_ThrowTypeError(ctx, "constructor requires 'new'");
arg = argv[0];
if (!js_weakref_is_target(arg))
return JS_ThrowTypeError(ctx, "invalid target");
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF);
if (JS_IsException(obj))
return JS_EXCEPTION;
JSWeakRefData *wrd = js_mallocz(ctx, sizeof(*wrd));
if (!wrd) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
wrd->target = js_weakref_new(ctx, arg);
wrd->weakref_header.weakref_type = JS_WEAKREF_TYPE_WEAKREF;
list_add_tail(&wrd->weakref_header.link, &ctx->rt->weakref_list);
JS_SetOpaque(obj, wrd);
return obj;
}
static JSValue js_weakref_deref(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF);
if (!wrd)
return JS_EXCEPTION;
if (js_weakref_is_live(wrd->target))
return JS_DupValue(ctx, wrd->target);
else
return JS_UNDEFINED;
}
static const JSCFunctionListEntry js_weakref_proto_funcs[] = {
JS_CFUNC_DEF("deref", 0, js_weakref_deref ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakRef", JS_PROP_CONFIGURABLE ),
};
static const JSClassShortDef js_weakref_class_def[] = {
{ JS_ATOM_WeakRef, js_weakref_finalizer, NULL }, /* JS_CLASS_WEAK_REF */
};
typedef struct JSFinRecEntry {
struct list_head link;
JSValue target;
JSValue held_val;
JSValue token;
} JSFinRecEntry;
typedef struct JSFinalizationRegistryData {
JSWeakRefHeader weakref_header;
struct list_head entries; /* list of JSFinRecEntry.link */
JSContext *ctx;
JSValue cb;
} JSFinalizationRegistryData;
static void js_finrec_finalizer(JSRuntime *rt, JSValue val)
{
JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY);
if (frd) {
struct list_head *el, *el1;
list_for_each_safe(el, el1, &frd->entries) {
JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
js_weakref_free(rt, fre->target);
js_weakref_free(rt, fre->token);
JS_FreeValueRT(rt, fre->held_val);
js_free_rt(rt, fre);
}
JS_FreeValueRT(rt, frd->cb);
list_del(&frd->weakref_header.link);
js_free_rt(rt, frd);
}
}
static void js_finrec_mark(JSRuntime *rt, JSValueConst val,
JS_MarkFunc *mark_func)
{
JSFinalizationRegistryData *frd = JS_GetOpaque(val, JS_CLASS_FINALIZATION_REGISTRY);
struct list_head *el;
if (frd) {
list_for_each(el, &frd->entries) {
JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
JS_MarkValue(rt, fre->held_val, mark_func);
}
JS_MarkValue(rt, frd->cb, mark_func);
}
}
static JSValue js_finrec_job(JSContext *ctx, int argc, JSValueConst *argv)
{
return JS_Call(ctx, argv[0], JS_UNDEFINED, 1, &argv[1]);
}
static void finrec_delete_weakref(JSRuntime *rt, JSWeakRefHeader *wh)
{
JSFinalizationRegistryData *frd = container_of(wh, JSFinalizationRegistryData, weakref_header);
struct list_head *el, *el1;
list_for_each_safe(el, el1, &frd->entries) {
JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
if (!js_weakref_is_live(fre->token)) {
js_weakref_free(rt, fre->token);
fre->token = JS_UNDEFINED;
}
if (!js_weakref_is_live(fre->target)) {
JSValueConst args[2];
args[0] = frd->cb;
args[1] = fre->held_val;
JS_EnqueueJob(frd->ctx, js_finrec_job, 2, args);
js_weakref_free(rt, fre->target);
js_weakref_free(rt, fre->token);
JS_FreeValueRT(rt, fre->held_val);
list_del(&fre->link);
js_free_rt(rt, fre);
}
}
}
static JSValue js_finrec_constructor(JSContext *ctx, JSValueConst new_target,
int argc, JSValueConst *argv)
{
JSValueConst cb;
JSValue obj;
JSFinalizationRegistryData *frd;
if (JS_IsUndefined(new_target))
return JS_ThrowTypeError(ctx, "constructor requires 'new'");
cb = argv[0];
if (!JS_IsFunction(ctx, cb))
return JS_ThrowTypeError(ctx, "argument must be a function");
obj = js_create_from_ctor(ctx, new_target, JS_CLASS_FINALIZATION_REGISTRY);
if (JS_IsException(obj))
return JS_EXCEPTION;
frd = js_mallocz(ctx, sizeof(*frd));
if (!frd) {
JS_FreeValue(ctx, obj);
return JS_EXCEPTION;
}
frd->weakref_header.weakref_type = JS_WEAKREF_TYPE_FINREC;
list_add_tail(&frd->weakref_header.link, &ctx->rt->weakref_list);
init_list_head(&frd->entries);
frd->ctx = ctx; /* XXX: JS_DupContext() ? */
frd->cb = JS_DupValue(ctx, cb);
JS_SetOpaque(obj, frd);
return obj;
}
static JSValue js_finrec_register(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv)
{
JSValueConst target, held_val, token;
JSFinalizationRegistryData *frd;
JSFinRecEntry *fre;
frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY);
if (!frd)
return JS_EXCEPTION;
target = argv[0];
held_val = argv[1];
token = argc > 2 ? argv[2] : JS_UNDEFINED;
if (!js_weakref_is_target(target))
return JS_ThrowTypeError(ctx, "invalid target");
if (js_same_value(ctx, target, held_val))
return JS_ThrowTypeError(ctx, "held value cannot be the target");
if (!JS_IsUndefined(token) && !js_weakref_is_target(token))
return JS_ThrowTypeError(ctx, "invalid unregister token");
fre = js_malloc(ctx, sizeof(*fre));
if (!fre)
return JS_EXCEPTION;
fre->target = js_weakref_new(ctx, target);
fre->held_val = JS_DupValue(ctx, held_val);
fre->token = js_weakref_new(ctx, token);
list_add_tail(&fre->link, &frd->entries);
return JS_UNDEFINED;
}
static JSValue js_finrec_unregister(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
JSFinalizationRegistryData *frd = JS_GetOpaque2(ctx, this_val, JS_CLASS_FINALIZATION_REGISTRY);
JSValueConst token;
BOOL removed;
struct list_head *el, *el1;
if (!frd)
return JS_EXCEPTION;
token = argv[0];
if (!js_weakref_is_target(token))
return JS_ThrowTypeError(ctx, "invalid unregister token");
removed = FALSE;
list_for_each_safe(el, el1, &frd->entries) {
JSFinRecEntry *fre = list_entry(el, JSFinRecEntry, link);
if (js_weakref_is_live(fre->token) && js_same_value(ctx, fre->token, token)) {
js_weakref_free(ctx->rt, fre->target);
js_weakref_free(ctx->rt, fre->token);
JS_FreeValue(ctx, fre->held_val);
list_del(&fre->link);
js_free(ctx, fre);
removed = TRUE;
}
}
return JS_NewBool(ctx, removed);
}
static const JSCFunctionListEntry js_finrec_proto_funcs[] = {
JS_CFUNC_DEF("register", 2, js_finrec_register ),
JS_CFUNC_DEF("unregister", 1, js_finrec_unregister ),
JS_PROP_STRING_DEF("[Symbol.toStringTag]", "FinalizationRegistry", JS_PROP_CONFIGURABLE ),
};
static const JSClassShortDef js_finrec_class_def[] = {
{ JS_ATOM_FinalizationRegistry, js_finrec_finalizer, js_finrec_mark }, /* JS_CLASS_FINALIZATION_REGISTRY */
};
void JS_AddIntrinsicWeakRef(JSContext *ctx)
{
JSRuntime *rt = ctx->rt;
/* WeakRef */
if (!JS_IsRegisteredClass(rt, JS_CLASS_WEAK_REF)) {
init_class_range(rt, js_weakref_class_def, JS_CLASS_WEAK_REF,
countof(js_weakref_class_def));
}
ctx->class_proto[JS_CLASS_WEAK_REF] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_WEAK_REF],
js_weakref_proto_funcs,
countof(js_weakref_proto_funcs));
JS_NewGlobalCConstructor(ctx, "WeakRef", js_weakref_constructor, 1, ctx->class_proto[JS_CLASS_WEAK_REF]);
/* FinalizationRegistry */
if (!JS_IsRegisteredClass(rt, JS_CLASS_FINALIZATION_REGISTRY)) {
init_class_range(rt, js_finrec_class_def, JS_CLASS_FINALIZATION_REGISTRY,
countof(js_finrec_class_def));
}
ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY] = JS_NewObject(ctx);
JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY],
js_finrec_proto_funcs,
countof(js_finrec_proto_funcs));
JS_NewGlobalCConstructor(ctx, "FinalizationRegistry", js_finrec_constructor, 1, ctx->class_proto[JS_CLASS_FINALIZATION_REGISTRY]);
}
#define STRLEN(s) (sizeof(s)/sizeof(s[0])) #define STRLEN(s) (sizeof(s)/sizeof(s[0]))
#define CSTR "<native C>" #define CSTR "<native C>"

View File

@@ -405,8 +405,6 @@ void JS_AddIntrinsicJSON(JSContext *ctx);
void JS_AddIntrinsicProxy(JSContext *ctx); void JS_AddIntrinsicProxy(JSContext *ctx);
void JS_AddIntrinsicMapSet(JSContext *ctx); void JS_AddIntrinsicMapSet(JSContext *ctx);
void JS_AddIntrinsicTypedArrays(JSContext *ctx); void JS_AddIntrinsicTypedArrays(JSContext *ctx);
void JS_AddIntrinsicPromise(JSContext *ctx);
void JS_AddIntrinsicWeakRef(JSContext *ctx);
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val, JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
int argc, JSValueConst *argv); int argc, JSValueConst *argv);