diff --git a/api-test.c b/api-test.c
index 4e5da750d..24708e1a4 100644
--- a/api-test.c
+++ b/api-test.c
@@ -269,6 +269,48 @@ static void two_byte_string(void)
JS_FreeRuntime(rt);
}
+static void weak_map_gc_check(void)
+{
+ static const char init_code[] =
+"const map = new WeakMap(); \
+function addItem() { \
+ const k = { \
+ text: 'a', \
+ }; \
+ map.set(k, {k}); \
+}";
+ static const char test_code[] = "addItem()";
+
+ JSRuntime *rt = JS_NewRuntime();
+ JSContext *ctx = JS_NewContext(rt);
+
+ JSValue ret = JS_Eval(ctx, init_code, strlen(init_code), "", JS_EVAL_TYPE_GLOBAL);
+ assert(!JS_IsException(ret));
+
+ JSValue ret_test = JS_Eval(ctx, test_code, strlen(test_code), "", JS_EVAL_TYPE_GLOBAL);
+ assert(!JS_IsException(ret_test));
+ JS_RunGC(rt);
+ JSMemoryUsage memory_usage;
+ JS_ComputeMemoryUsage(rt, &memory_usage);
+
+ for (int i = 0; i < 3; i++) {
+ JSValue ret_test2 = JS_Eval(ctx, test_code, strlen(test_code), "", JS_EVAL_TYPE_GLOBAL);
+ assert(!JS_IsException(ret_test2));
+ JS_RunGC(rt);
+ JSMemoryUsage memory_usage2;
+ JS_ComputeMemoryUsage(rt, &memory_usage2);
+
+ assert(memory_usage.memory_used_count == memory_usage2.memory_used_count);
+ assert(memory_usage.memory_used_size == memory_usage2.memory_used_size);
+ JS_FreeValue(ctx, ret_test2);
+ }
+
+ JS_FreeValue(ctx, ret);
+ JS_FreeValue(ctx, ret_test);
+ JS_FreeContext(ctx);
+ JS_FreeRuntime(rt);
+}
+
int main(void)
{
sync_call();
@@ -278,5 +320,6 @@ int main(void)
is_array();
module_serde();
two_byte_string();
+ weak_map_gc_check();
return 0;
}
diff --git a/quickjs.c b/quickjs.c
index 38ecefc54..bb38fbdf8 100644
--- a/quickjs.c
+++ b/quickjs.c
@@ -493,6 +493,26 @@ typedef struct JSWeakRefRecord {
} u;
} JSWeakRefRecord;
+typedef struct JSMapRecord {
+ int ref_count; /* used during enumeration to avoid freeing the record */
+ bool empty; /* true if the record is deleted */
+ struct JSMapState *map;
+ struct list_head link;
+ struct list_head hash_link;
+ JSValue key;
+ JSValue value;
+} JSMapRecord;
+
+typedef struct JSMapState {
+ bool is_weak; /* true if WeakSet/WeakMap */
+ struct list_head records; /* list of JSMapRecord.link */
+ uint32_t record_count;
+ struct list_head *hash_table;
+ uint32_t hash_size; /* must be a power of two */
+ uint32_t record_count_threshold; /* count at which a hash table
+ resize is needed */
+} JSMapState;
+
enum {
JS_ATOM_TYPE_STRING = 1,
JS_ATOM_TYPE_GLOBAL_SYMBOL,
@@ -1694,8 +1714,8 @@ 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_WeakMap, js_map_finalizer, NULL }, /* JS_CLASS_WEAKMAP */
+ { JS_ATOM_WeakSet, js_map_finalizer, NULL }, /* JS_CLASS_WEAKSET */
{ JS_ATOM_Iterator, NULL, NULL }, /* JS_CLASS_ITERATOR */
{ JS_ATOM_IteratorHelper, js_iterator_helper_finalizer, js_iterator_helper_mark }, /* JS_CLASS_ITERATOR_HELPER */
{ JS_ATOM_IteratorWrap, js_iterator_wrap_finalizer, js_iterator_wrap_mark }, /* JS_CLASS_ITERATOR_WRAP */
@@ -5783,6 +5803,22 @@ void JS_MarkValue(JSRuntime *rt, JSValueConst val, JS_MarkFunc *mark_func)
}
}
+static void mark_weak_map_value(JSRuntime *rt, JSWeakRefRecord *first_weak_ref, JS_MarkFunc *mark_func) {
+ JSWeakRefRecord *wr;
+ JSMapRecord *mr;
+ JSMapState *s;
+
+ for (wr = first_weak_ref; wr != NULL; wr = wr->next_weak_ref) {
+ if (wr->kind == JS_WEAK_REF_KIND_MAP) {
+ mr = wr->u.map_record;
+ s = mr->map;
+ assert(s->is_weak);
+ assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
+ JS_MarkValue(rt, mr->value, mark_func);
+ }
+ }
+}
+
static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
JS_MarkFunc *mark_func)
{
@@ -5822,6 +5858,10 @@ static void mark_children(JSRuntime *rt, JSGCObjectHeader *gp,
prs++;
}
+ if (unlikely(p->first_weak_ref)) {
+ mark_weak_map_value(rt, p->first_weak_ref, mark_func);
+ }
+
if (p->class_id != JS_CLASS_OBJECT) {
JSClassGCMark *gc_mark;
gc_mark = rt->class_array[p->class_id].gc_mark;
@@ -48433,26 +48473,6 @@ static const JSCFunctionListEntry js_symbol_funcs[] = {
/* Set/Map/WeakSet/WeakMap */
-typedef struct JSMapRecord {
- int ref_count; /* used during enumeration to avoid freeing the record */
- bool empty; /* true if the record is deleted */
- struct JSMapState *map;
- struct list_head link;
- struct list_head hash_link;
- JSValue key;
- JSValue value;
-} JSMapRecord;
-
-typedef struct JSMapState {
- bool is_weak; /* true if WeakSet/WeakMap */
- struct list_head records; /* list of JSMapRecord.link */
- uint32_t record_count;
- struct list_head *hash_table;
- uint32_t hash_size; /* must be a power of two */
- uint32_t record_count_threshold; /* count at which a hash table
- resize is needed */
-} JSMapState;
-
#define MAGIC_SET (1 << 0)
#define MAGIC_WEAK (1 << 1)
@@ -49056,10 +49076,10 @@ static void js_map_mark(JSRuntime *rt, JSValueConst val,
s = p->u.map_state;
if (s) {
+ assert(!s->is_weak);
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);
}
}