|
|
|
|
@@ -134,15 +134,12 @@ enum {
|
|
|
|
|
JS_CLASS_BIG_INT, /* u.object_data */
|
|
|
|
|
JS_CLASS_MAP, /* 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_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 */
|
|
|
|
|
JS_CLASS_PROXY, /* u.proxy_data */
|
|
|
|
|
JS_CLASS_WEAK_REF,
|
|
|
|
|
JS_CLASS_FINALIZATION_REGISTRY,
|
|
|
|
|
|
|
|
|
|
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
|
|
|
|
|
@@ -219,7 +216,6 @@ struct JSRuntime {
|
|
|
|
|
struct list_head tmp_obj_list; /* used during GC */
|
|
|
|
|
JSGCPhaseEnum gc_phase : 8;
|
|
|
|
|
size_t malloc_gc_threshold;
|
|
|
|
|
struct list_head weakref_list; /* list of JSWeakRefHeader.link */
|
|
|
|
|
#ifdef DUMP_LEAKS
|
|
|
|
|
struct list_head string_list; /* list of JSString.link */
|
|
|
|
|
#endif
|
|
|
|
|
@@ -295,17 +291,6 @@ struct JSGCObjectHeader {
|
|
|
|
|
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 {
|
|
|
|
|
union {
|
|
|
|
|
JSGCObjectHeader header; /* must come first */
|
|
|
|
|
@@ -724,10 +709,6 @@ struct JSObject {
|
|
|
|
|
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 */
|
|
|
|
|
JSProperty *prop; /* array of properties */
|
|
|
|
|
union {
|
|
|
|
|
@@ -776,7 +757,6 @@ typedef struct JSMapRecord {
|
|
|
|
|
} JSMapRecord;
|
|
|
|
|
|
|
|
|
|
typedef struct JSMapState {
|
|
|
|
|
BOOL is_weak; /* TRUE if WeakSet/WeakMap */
|
|
|
|
|
struct list_head records; /* list of JSMapRecord.link */
|
|
|
|
|
uint32_t record_count;
|
|
|
|
|
JSMapRecord **hash_table;
|
|
|
|
|
@@ -784,7 +764,6 @@ typedef struct JSMapState {
|
|
|
|
|
uint32_t hash_size; /* = 2 ^ hash_bits */
|
|
|
|
|
uint32_t record_count_threshold; /* count at which a hash table
|
|
|
|
|
resize is needed */
|
|
|
|
|
JSWeakRefHeader weakref_header; /* only used if is_weak = TRUE */
|
|
|
|
|
} JSMapState;
|
|
|
|
|
|
|
|
|
|
enum {
|
|
|
|
|
@@ -994,10 +973,7 @@ 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 map_delete_weakrefs(JSRuntime *rt, JSWeakRefHeader *wh);
|
|
|
|
|
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 void JS_RunGCInternal(JSRuntime *rt);
|
|
|
|
|
static int js_string_find_invalid_codepoint(JSString *p);
|
|
|
|
|
static JSValue js_regexp_toString(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
int argc, JSValueConst *argv);
|
|
|
|
|
@@ -1208,8 +1184,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_Map, js_map_finalizer, js_map_mark }, /* JS_CLASS_MAP */
|
|
|
|
|
{ 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_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 */
|
|
|
|
|
@@ -1285,7 +1259,6 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
|
|
|
|
|
init_list_head(&rt->gc_obj_list);
|
|
|
|
|
init_list_head(&rt->gc_zero_ref_count_list);
|
|
|
|
|
rt->gc_phase = JS_GC_PHASE_NONE;
|
|
|
|
|
init_list_head(&rt->weakref_list);
|
|
|
|
|
|
|
|
|
|
#ifdef DUMP_LEAKS
|
|
|
|
|
init_list_head(&rt->string_list);
|
|
|
|
|
@@ -1517,9 +1490,7 @@ void JS_FreeRuntime(JSRuntime *rt)
|
|
|
|
|
|
|
|
|
|
JS_FreeValueRT(rt, rt->current_exception);
|
|
|
|
|
|
|
|
|
|
/* don't remove the weak objects to avoid create new jobs with
|
|
|
|
|
FinalizationRegistry */
|
|
|
|
|
JS_RunGCInternal(rt, FALSE);
|
|
|
|
|
JS_RunGCInternal(rt);
|
|
|
|
|
|
|
|
|
|
#ifdef DUMP_LEAKS
|
|
|
|
|
/* leaking objects */
|
|
|
|
|
@@ -1561,7 +1532,6 @@ void JS_FreeRuntime(JSRuntime *rt)
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
assert(list_empty(&rt->gc_obj_list));
|
|
|
|
|
assert(list_empty(&rt->weakref_list));
|
|
|
|
|
|
|
|
|
|
/* free the classes */
|
|
|
|
|
for(i = 0; i < rt->class_count; i++) {
|
|
|
|
|
@@ -4578,7 +4548,6 @@ static JSValue JS_NewObjectFromShape(JSContext *ctx, JSShape *sh, JSClassID clas
|
|
|
|
|
p->is_constructor = 0;
|
|
|
|
|
p->has_immutable_prototype = 0;
|
|
|
|
|
p->tmp_mark = 0;
|
|
|
|
|
p->weakref_count = 0;
|
|
|
|
|
p->u.opaque = NULL;
|
|
|
|
|
p->shape = sh;
|
|
|
|
|
p->prop = js_malloc(ctx, sizeof(JSProperty) * sh->prop_size);
|
|
|
|
|
@@ -5212,22 +5181,11 @@ static void free_object(JSRuntime *rt, JSObject *p)
|
|
|
|
|
p->u.func.var_refs = NULL;
|
|
|
|
|
|
|
|
|
|
remove_gc_object(&p->header);
|
|
|
|
|
if (rt->gc_phase == JS_GC_PHASE_REMOVE_CYCLES) {
|
|
|
|
|
if (p->header.ref_count == 0 && p->weakref_count == 0) {
|
|
|
|
|
js_free_rt(rt, p);
|
|
|
|
|
} else {
|
|
|
|
|
/* keep the object structure because there are may be
|
|
|
|
|
references to it */
|
|
|
|
|
list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
/* keep the object structure in case there are weak references to it */
|
|
|
|
|
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 */
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
/* no more weakrefs: free if no strong refs, else queue for zero-ref processing */
|
|
|
|
|
if (p->header.ref_count == 0)
|
|
|
|
|
js_free_rt(rt, p);
|
|
|
|
|
else
|
|
|
|
|
list_add_tail(&p->header.link, &rt->gc_zero_ref_count_list);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void free_gc_object(JSRuntime *rt, JSGCObjectHeader *gp)
|
|
|
|
|
@@ -5339,36 +5297,6 @@ void __JS_FreeValue(JSContext *ctx, JSValue v)
|
|
|
|
|
|
|
|
|
|
/* 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,
|
|
|
|
|
JSGCObjectTypeEnum type)
|
|
|
|
|
{
|
|
|
|
|
@@ -5592,27 +5520,14 @@ static void gc_free_cycles(JSRuntime *rt)
|
|
|
|
|
p = list_entry(el, JSGCObjectHeader, link);
|
|
|
|
|
assert(p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT ||
|
|
|
|
|
p->gc_obj_type == JS_GC_OBJ_TYPE_FUNCTION_BYTECODE);
|
|
|
|
|
if (p->gc_obj_type == JS_GC_OBJ_TYPE_JS_OBJECT &&
|
|
|
|
|
((JSObject *)p)->weakref_count != 0) {
|
|
|
|
|
/* keep the object because there are weak references to it */
|
|
|
|
|
p->mark = 0;
|
|
|
|
|
} else {
|
|
|
|
|
js_free_rt(rt, p);
|
|
|
|
|
}
|
|
|
|
|
js_free_rt(rt, p);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 =
|
|
|
|
|
1 after this pass. */
|
|
|
|
|
gc_decref(rt);
|
|
|
|
|
@@ -5626,7 +5541,7 @@ static void JS_RunGCInternal(JSRuntime *rt, BOOL remove_weak_objects)
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
@@ -5874,8 +5789,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_WEAKMAP: /* u.map_state */
|
|
|
|
|
case JS_CLASS_WEAKSET: /* 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 */
|
|
|
|
|
@@ -13838,15 +13751,6 @@ static BOOL js_strict_eq2(JSContext *ctx, JSValue op1, JSValue op2,
|
|
|
|
|
tag1 = JS_VALUE_GET_NORM_TAG(op1);
|
|
|
|
|
tag2 = JS_VALUE_GET_NORM_TAG(op2);
|
|
|
|
|
|
|
|
|
|
/* throw on any mismatched non-numeric tags */
|
|
|
|
|
if (tag1 != tag2
|
|
|
|
|
&& !((tag1 == JS_TAG_INT || tag1 == JS_TAG_FLOAT64)
|
|
|
|
|
&& (tag2 == JS_TAG_INT || tag2 == JS_TAG_FLOAT64))) {
|
|
|
|
|
JS_ThrowTypeError(ctx,
|
|
|
|
|
"Strict equality: cannot compare two differing types");
|
|
|
|
|
goto done_no_free;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch(tag1) {
|
|
|
|
|
case JS_TAG_BOOL:
|
|
|
|
|
if (tag1 != tag2) {
|
|
|
|
|
@@ -40593,81 +40497,7 @@ static const JSCFunctionListEntry js_symbol_funcs[] = {
|
|
|
|
|
|
|
|
|
|
/* 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_WEAK (1 << 1)
|
|
|
|
|
|
|
|
|
|
static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
|
|
|
|
|
int argc, JSValueConst *argv, int magic)
|
|
|
|
|
@@ -40675,10 +40505,9 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
|
|
|
|
|
JSMapState *s;
|
|
|
|
|
JSValue obj, adder = JS_UNDEFINED, iter = JS_UNDEFINED, next_method = JS_UNDEFINED;
|
|
|
|
|
JSValueConst arr;
|
|
|
|
|
BOOL is_set, is_weak;
|
|
|
|
|
BOOL is_set;
|
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
@@ -40686,11 +40515,6 @@ static JSValue js_map_constructor(JSContext *ctx, JSValueConst new_target,
|
|
|
|
|
if (!s)
|
|
|
|
|
goto fail;
|
|
|
|
|
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);
|
|
|
|
|
s->hash_bits = 1;
|
|
|
|
|
s->hash_size = 1U << s->hash_bits;
|
|
|
|
|
@@ -40881,7 +40705,7 @@ static JSMapRecord *map_find_record(JSContext *ctx, JSMapState *s,
|
|
|
|
|
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 || (s->is_weak && !js_weakref_is_live(mr->key))) {
|
|
|
|
|
if (mr->empty) {
|
|
|
|
|
/* cannot match */
|
|
|
|
|
} else {
|
|
|
|
|
if (js_same_value_zero(ctx, mr->key, key))
|
|
|
|
|
@@ -40910,7 +40734,7 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s)
|
|
|
|
|
|
|
|
|
|
list_for_each(el, &s->records) {
|
|
|
|
|
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);
|
|
|
|
|
mr->hash_next = new_hash_table[h];
|
|
|
|
|
@@ -40934,11 +40758,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
|
|
|
|
|
return NULL;
|
|
|
|
|
mr->ref_count = 1;
|
|
|
|
|
mr->empty = FALSE;
|
|
|
|
|
if (s->is_weak) {
|
|
|
|
|
mr->key = js_weakref_new(ctx, key);
|
|
|
|
|
} else {
|
|
|
|
|
mr->key = JS_DupValue(ctx, key);
|
|
|
|
|
}
|
|
|
|
|
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;
|
|
|
|
|
@@ -40956,11 +40776,7 @@ static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
|
|
|
|
|
if (mr->empty)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (s->is_weak) {
|
|
|
|
|
js_weakref_free(rt, mr->key);
|
|
|
|
|
} else {
|
|
|
|
|
JS_FreeValueRT(rt, mr->key);
|
|
|
|
|
}
|
|
|
|
|
JS_FreeValueRT(rt, mr->key);
|
|
|
|
|
JS_FreeValueRT(rt, mr->value);
|
|
|
|
|
if (--mr->ref_count == 0) {
|
|
|
|
|
list_del(&mr->link);
|
|
|
|
|
@@ -40984,38 +40800,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,
|
|
|
|
|
int argc, JSValueConst *argv, int magic)
|
|
|
|
|
{
|
|
|
|
|
@@ -41026,8 +40810,6 @@ static JSValue js_map_set(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
if (!s)
|
|
|
|
|
return JS_EXCEPTION;
|
|
|
|
|
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)
|
|
|
|
|
value = JS_UNDEFINED;
|
|
|
|
|
else
|
|
|
|
|
@@ -41093,7 +40875,7 @@ static JSValue js_map_delete(JSContext *ctx, JSValueConst this_val,
|
|
|
|
|
mr = *pmr;
|
|
|
|
|
if (mr == NULL)
|
|
|
|
|
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))
|
|
|
|
|
@@ -41317,18 +41099,12 @@ static void js_map_finalizer(JSRuntime *rt, JSValue val)
|
|
|
|
|
list_for_each_safe(el, el1, &s->records) {
|
|
|
|
|
mr = list_entry(el, JSMapRecord, link);
|
|
|
|
|
if (!mr->empty) {
|
|
|
|
|
if (s->is_weak)
|
|
|
|
|
js_weakref_free(rt, mr->key);
|
|
|
|
|
else
|
|
|
|
|
JS_FreeValueRT(rt, mr->key);
|
|
|
|
|
JS_FreeValueRT(rt, mr->key);
|
|
|
|
|
JS_FreeValueRT(rt, mr->value);
|
|
|
|
|
}
|
|
|
|
|
js_free_rt(rt, mr);
|
|
|
|
|
}
|
|
|
|
|
js_free_rt(rt, s->hash_table);
|
|
|
|
|
if (s->is_weak) {
|
|
|
|
|
list_del(&s->weakref_header.link);
|
|
|
|
|
}
|
|
|
|
|
js_free_rt(rt, s);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -41344,8 +41120,7 @@ static void js_map_mark(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
|
|
|
|
|
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->key, mark_func);
|
|
|
|
|
JS_MarkValue(rt, mr->value, mark_func);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -41528,68 +41303,53 @@ static const JSCFunctionListEntry js_set_iterator_proto_funcs[] = {
|
|
|
|
|
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[4] = {
|
|
|
|
|
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] = {
|
|
|
|
|
static const uint8_t js_map_proto_funcs_count[4] = {
|
|
|
|
|
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];
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
/* --- 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]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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]);
|
|
|
|
|
}
|
|
|
|
|
/* --- 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]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* URI handling */
|
|
|
|
|
@@ -42346,277 +42106,6 @@ void JS_AddIntrinsicBaseObjects(JSContext *ctx)
|
|
|
|
|
JS_AddIntrinsicBigInt(ctx);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* 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 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_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 CSTR "<native C>"
|
|
|
|
|
|
|
|
|
|
|