Generalize weakref handling logic

This commit is contained in:
Saúl Ibarra Corretgé 2023-11-27 22:13:44 +01:00
parent 331356625e
commit 1df9615638

100
quickjs.c
View file

@ -419,6 +419,18 @@ typedef union JSFloat64Union {
uint32_t u32[2]; uint32_t u32[2];
} JSFloat64Union; } JSFloat64Union;
typedef enum {
JS_WEAK_REF_KIND_MAP,
} JSWeakRefKindEnum;
typedef struct JSWeakRefRecord {
JSWeakRefKindEnum kind;
struct JSWeakRefRecord *next_weak_ref;
union {
struct JSMapRecord *map_record;
} u;
} JSWeakRefRecord;
enum { enum {
JS_ATOM_TYPE_STRING = 1, JS_ATOM_TYPE_STRING = 1,
JS_ATOM_TYPE_GLOBAL_SYMBOL, JS_ATOM_TYPE_GLOBAL_SYMBOL,
@ -449,7 +461,7 @@ struct JSString {
uint32_t hash : 30; uint32_t hash : 30;
uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */ uint8_t atom_type : 2; /* != 0 if atom, JS_ATOM_TYPE_x */
uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */ uint32_t hash_next; /* atom_index for JS_ATOM_TYPE_SYMBOL */
struct JSMapRecord *first_weak_ref; JSWeakRefRecord *first_weak_ref;
#ifdef DUMP_LEAKS #ifdef DUMP_LEAKS
struct list_head link; /* string list */ struct list_head link; /* string list */
#endif #endif
@ -802,7 +814,7 @@ struct JSObject {
JSShape *shape; /* prototype and property names + flag */ JSShape *shape; /* prototype and property names + flag */
JSProperty *prop; /* array of properties */ JSProperty *prop; /* array of properties */
/* byte offsets: 24/40 */ /* byte offsets: 24/40 */
struct JSMapRecord *first_weak_ref; /* XXX: use a bit and an external hash table? */ JSWeakRefRecord *first_weak_ref;
/* byte offsets: 28/48 */ /* byte offsets: 28/48 */
union { union {
void *opaque; void *opaque;
@ -1056,7 +1068,7 @@ static int JS_CreateProperty(JSContext *ctx, JSObject *p,
JSValueConst getter, JSValueConst setter, JSValueConst getter, JSValueConst setter,
int flags); int flags);
static int js_string_memcmp(const JSString *p1, const JSString *p2, int len); static int js_string_memcmp(const JSString *p1, const JSString *p2, int len);
static void reset_weak_ref(JSRuntime *rt, struct JSMapRecord **first_weak_ref); static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref);
static JSValue js_array_buffer_constructor3(JSContext *ctx, static JSValue js_array_buffer_constructor3(JSContext *ctx,
JSValueConst new_target, JSValueConst new_target,
uint64_t len, JSClassID class_id, uint64_t len, JSClassID class_id,
@ -43521,7 +43533,6 @@ typedef struct JSMapRecord {
int ref_count; /* used during enumeration to avoid freeing the record */ int ref_count; /* used during enumeration to avoid freeing the record */
BOOL empty; /* TRUE if the record is deleted */ BOOL empty; /* TRUE if the record is deleted */
struct JSMapState *map; struct JSMapState *map;
struct JSMapRecord *next_weak_ref;
struct list_head link; struct list_head link;
struct list_head hash_link; struct list_head hash_link;
JSValue key; JSValue key;
@ -43754,7 +43765,7 @@ static void map_hash_resize(JSContext *ctx, JSMapState *s)
s->record_count_threshold = new_hash_size * 2; s->record_count_threshold = new_hash_size * 2;
} }
static JSMapRecord **get_first_weak_ref(JSValueConst key) static JSWeakRefRecord **get_first_weak_ref(JSValueConst key)
{ {
switch (JS_VALUE_GET_TAG(key)) { switch (JS_VALUE_GET_TAG(key)) {
case JS_TAG_OBJECT: case JS_TAG_OBJECT:
@ -43778,7 +43789,7 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
JSValueConst key) JSValueConst key)
{ {
uint32_t h; uint32_t h;
JSMapRecord *mr, **pmr; JSMapRecord *mr;
mr = js_malloc(ctx, sizeof(*mr)); mr = js_malloc(ctx, sizeof(*mr));
if (!mr) if (!mr)
@ -43787,10 +43798,18 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
mr->map = s; mr->map = s;
mr->empty = FALSE; mr->empty = FALSE;
if (s->is_weak) { if (s->is_weak) {
pmr = get_first_weak_ref(key); JSWeakRefRecord *wr, **pwr;
wr = js_malloc(ctx, sizeof(*wr));
if (!wr) {
js_free(ctx, mr);
return NULL;
}
wr->kind = JS_WEAK_REF_KIND_MAP;
wr->u.map_record = mr;
pwr = get_first_weak_ref(key);
/* Add the weak reference */ /* Add the weak reference */
mr->next_weak_ref = *pmr; wr->next_weak_ref = *pwr;
*pmr = mr; *pwr = wr;
} else { } else {
JS_DupValue(ctx, key); JS_DupValue(ctx, key);
} }
@ -43809,19 +43828,20 @@ static JSMapRecord *map_add_record(JSContext *ctx, JSMapState *s,
reference list. we don't use a doubly linked list to reference list. we don't use a doubly linked list to
save space, assuming a given object has few weak save space, assuming a given object has few weak
references to it */ references to it */
static void delete_weak_ref(JSRuntime *rt, JSMapRecord *mr) static void delete_map_weak_ref(JSRuntime *rt, JSMapRecord *mr)
{ {
JSMapRecord **pmr, *mr1; JSWeakRefRecord **pwr, *wr;
pmr = get_first_weak_ref(mr->key); pwr = get_first_weak_ref(mr->key);
for(;;) { for(;;) {
mr1 = *pmr; wr = *pwr;
assert(mr1 != NULL); assert(wr != NULL);
if (mr1 == mr) if (wr->kind == JS_WEAK_REF_KIND_MAP && wr->u.map_record == mr)
break; break;
pmr = &mr1->next_weak_ref; pwr = &wr->next_weak_ref;
} }
*pmr = mr1->next_weak_ref; *pwr = wr->next_weak_ref;
js_free_rt(rt, wr);
} }
static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr) static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
@ -43830,7 +43850,7 @@ static void map_delete_record(JSRuntime *rt, JSMapState *s, JSMapRecord *mr)
return; return;
list_del(&mr->hash_link); list_del(&mr->hash_link);
if (s->is_weak) { if (s->is_weak) {
delete_weak_ref(rt, mr); delete_map_weak_ref(rt, mr);
} else { } else {
JS_FreeValueRT(rt, mr->key); JS_FreeValueRT(rt, mr->key);
} }
@ -43857,27 +43877,45 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
} }
} }
static void reset_weak_ref(JSRuntime *rt, struct JSMapRecord **first_weak_ref) static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref)
{ {
JSMapRecord *mr, *mr_next; JSWeakRefRecord *wr, *wr_next;
JSMapRecord *mr;
JSMapState *s; JSMapState *s;
/* first pass to remove the records from the WeakMap/WeakSet /* first pass to remove the records from the WeakMap/WeakSet
lists */ lists */
for(mr = *first_weak_ref; mr != NULL; mr = mr->next_weak_ref) { for(wr = *first_weak_ref; wr != NULL; wr = wr->next_weak_ref) {
s = mr->map; switch(wr->kind) {
assert(s->is_weak); case JS_WEAK_REF_KIND_MAP: {
assert(!mr->empty); /* no iterator on WeakMap/WeakSet */ mr = wr->u.map_record;
list_del(&mr->hash_link); s = mr->map;
list_del(&mr->link); assert(s->is_weak);
assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
list_del(&mr->hash_link);
list_del(&mr->link);
break;
}
default:
abort();
}
} }
/* second pass to free the values to avoid modifying the weak /* second pass to free the values to avoid modifying the weak
reference list while traversing it. */ reference list while traversing it. */
for(mr = *first_weak_ref; mr != NULL; mr = mr_next) { for(wr = *first_weak_ref; wr != NULL; wr = wr_next) {
mr_next = mr->next_weak_ref; wr_next = wr->next_weak_ref;
JS_FreeValueRT(rt, mr->value); switch(wr->kind) {
js_free_rt(rt, mr); case JS_WEAK_REF_KIND_MAP: {
mr = wr->u.map_record;
JS_FreeValueRT(rt, mr->value);
js_free_rt(rt, mr);
break;
}
default:
abort();
}
js_free_rt(rt, wr);
} }
*first_weak_ref = NULL; /* fail safe */ *first_weak_ref = NULL; /* fail safe */
@ -44149,7 +44187,7 @@ static void js_map_finalizer(JSRuntime *rt, JSValue val)
mr = list_entry(el, JSMapRecord, link); mr = list_entry(el, JSMapRecord, link);
if (!mr->empty) { if (!mr->empty) {
if (s->is_weak) if (s->is_weak)
delete_weak_ref(rt, mr); delete_map_weak_ref(rt, mr);
else else
JS_FreeValueRT(rt, mr->key); JS_FreeValueRT(rt, mr->key);
JS_FreeValueRT(rt, mr->value); JS_FreeValueRT(rt, mr->value);