Skip to content

Commit 1dae9f7

Browse files
committed
fix(android): clear quickjs weak refs before gc
Ports the iOS QuickJS WeakRef keepalive clearing path to both Android QuickJS backends and clears those keepalives before explicit GC and JS exit.
1 parent a0e0967 commit 1dae9f7

5 files changed

Lines changed: 84 additions & 2 deletions

File tree

NativeScript/napi/android/quickjs/quickjs-api.c

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -308,6 +308,7 @@ static inline void js_enter(napi_env env) {
308308

309309
static inline void js_exit(napi_env env) {
310310
if (--env->js_enter_state <= 0) {
311+
JS_ClearWeakRefKeepAlives(JS_GetRuntime(env->context));
311312
qjs_execute_pending_jobs(env);
312313
}
313314
}
@@ -4112,7 +4113,9 @@ static JSValue JSRunGCCallback(JSContext *ctx, JSValue
41124113
this_val,
41134114
int argc, JSValue
41144115
*argv) {
4115-
JS_RunGC(JS_GetRuntime(ctx));
4116+
JSRuntime *rt = JS_GetRuntime(ctx);
4117+
JS_ClearWeakRefKeepAlives(rt);
4118+
JS_RunGC(rt);
41164119
return JS_TRUE;
41174120
}
41184121

NativeScript/napi/android/quickjs/source/quickjs.c

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,7 @@ struct JSRuntime {
261261
JSGCPhaseEnum gc_phase : 8;
262262
size_t malloc_gc_threshold;
263263
struct list_head weakref_list; /* list of JSWeakRefHeader.link */
264+
struct list_head weakref_keepalive_list; /* strong refs kept until the current host job completes */
264265
#ifdef DUMP_LEAKS
265266
struct list_head string_list; /* list of JSString.link */
266267
#endif
@@ -339,6 +340,11 @@ typedef struct JSStackFrame {
339340
JSValue *cur_sp;
340341
} JSStackFrame;
341342

343+
typedef struct JSWeakRefKeepAliveEntry {
344+
struct list_head link;
345+
JSValue value;
346+
} JSWeakRefKeepAliveEntry;
347+
342348
typedef enum {
343349
JS_GC_OBJ_TYPE_JS_OBJECT,
344350
JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
@@ -1669,6 +1675,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
16691675
init_list_head(&rt->gc_zero_ref_count_list);
16701676
rt->gc_phase = JS_GC_PHASE_NONE;
16711677
init_list_head(&rt->weakref_list);
1678+
init_list_head(&rt->weakref_keepalive_list);
16721679

16731680
#ifdef DUMP_LEAKS
16741681
init_list_head(&rt->string_list);
@@ -1990,6 +1997,7 @@ void JS_FreeRuntime(JSRuntime *rt)
19901997
js_free_rt(rt, e);
19911998
}
19921999
init_list_head(&rt->job_list);
2000+
JS_ClearWeakRefKeepAlives(rt);
19932001

19942002
/* don't remove the weak objects to avoid create new jobs with
19952003
FinalizationRegistry */
@@ -59255,6 +59263,7 @@ static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target,
5925559263
wrd->target = js_weakref_new(ctx, arg);
5925659264
wrd->weakref_header.weakref_type = JS_WEAKREF_TYPE_WEAKREF;
5925759265
list_add_tail(&wrd->weakref_header.link, &ctx->rt->weakref_list);
59266+
JS_KeepWeakRefTargetAlive(ctx, arg);
5925859267
JS_SetOpaque(obj, wrd);
5925959268
return obj;
5926059269
}
@@ -59524,6 +59533,35 @@ JSValue JS_WeakRef_Deref(JSContext *ctx, JSValueConst this_val) {
5952459533
return js_weakref_deref(ctx, this_val, 0, NULL);
5952559534
}
5952659535

59536+
void JS_KeepWeakRefTargetAlive(JSContext *ctx, JSValueConst value)
59537+
{
59538+
JSRuntime *rt;
59539+
JSWeakRefKeepAliveEntry *entry;
59540+
59541+
if (!js_weakref_is_target(value))
59542+
return;
59543+
59544+
rt = JS_GetRuntime(ctx);
59545+
entry = js_malloc(ctx, sizeof(*entry));
59546+
if (!entry)
59547+
return;
59548+
59549+
entry->value = JS_DupValue(ctx, value);
59550+
list_add_tail(&entry->link, &rt->weakref_keepalive_list);
59551+
}
59552+
59553+
void JS_ClearWeakRefKeepAlives(JSRuntime *rt)
59554+
{
59555+
struct list_head *el, *el1;
59556+
59557+
list_for_each_safe(el, el1, &rt->weakref_keepalive_list) {
59558+
JSWeakRefKeepAliveEntry *entry = list_entry(el, JSWeakRefKeepAliveEntry, link);
59559+
list_del(&entry->link);
59560+
JS_FreeValueRT(rt, entry->value);
59561+
js_free_rt(rt, entry);
59562+
}
59563+
}
59564+
5952759565
int JS_GetLength(JSContext *ctx, JSValueConst obj, int64_t *pres) {
5952859566
return js_get_length64(ctx, pres, obj);
5952959567
}
@@ -59577,4 +59615,3 @@ int JS_FreezeObject(JSContext *ctx, JSValueConst obj)
5957759615
size_t JS_GetGCThreshold(JSRuntime *rt) {
5957859616
return rt->malloc_gc_threshold;
5957959617
}
59580-

NativeScript/napi/android/quickjs/source/quickjs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -406,6 +406,8 @@ int JS_AddIntrinsicMapSet(JSContext *ctx);
406406
int JS_AddIntrinsicTypedArrays(JSContext *ctx);
407407
int JS_AddIntrinsicPromise(JSContext *ctx);
408408
int JS_AddIntrinsicWeakRef(JSContext *ctx);
409+
void JS_KeepWeakRefTargetAlive(JSContext *ctx, JSValueConst value);
410+
void JS_ClearWeakRefKeepAlives(JSRuntime *rt);
409411

410412
JSValue js_string_codePointRange(JSContext *ctx, JSValueConst this_val,
411413
int argc, JSValueConst *argv);

NativeScript/napi/android/quickjs/source_ng/quickjs.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@ struct JSRuntime {
304304
void *host_promise_rejection_tracker_opaque;
305305

306306
struct list_head job_list; /* list of JSJobEntry.link */
307+
struct list_head weakref_keepalive_list; /* strong refs kept until the current host job completes */
307308

308309
JSModuleNormalizeFunc *module_normalize_func;
309310
JSModuleLoaderFunc *module_loader_func;
@@ -352,6 +353,11 @@ typedef struct JSStackFrame {
352353
JSValue *cur_sp;
353354
} JSStackFrame;
354355

356+
typedef struct JSWeakRefKeepAliveEntry {
357+
struct list_head link;
358+
JSValue value;
359+
} JSWeakRefKeepAliveEntry;
360+
355361
typedef enum {
356362
JS_GC_OBJ_TYPE_JS_OBJECT,
357363
JS_GC_OBJ_TYPE_FUNCTION_BYTECODE,
@@ -1856,6 +1862,7 @@ JSRuntime *JS_NewRuntime2(const JSMallocFunctions *mf, void *opaque)
18561862
init_list_head(&rt->string_list);
18571863
#endif
18581864
init_list_head(&rt->job_list);
1865+
init_list_head(&rt->weakref_keepalive_list);
18591866

18601867
if (JS_InitAtoms(rt))
18611868
goto fail;
@@ -2151,6 +2158,7 @@ void JS_FreeRuntime(JSRuntime *rt)
21512158
js_free_rt(rt, e);
21522159
}
21532160
init_list_head(&rt->job_list);
2161+
JS_ClearWeakRefKeepAlives(rt);
21542162

21552163
JS_RunGC(rt);
21562164

@@ -57510,6 +57518,7 @@ static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target,
5751057518
wr->kind = JS_WEAK_REF_KIND_WEAK_REF;
5751157519
wr->u.weak_ref_data = wrd;
5751257520
insert_weakref_record(arg, wr);
57521+
JS_KeepWeakRefTargetAlive(ctx, arg);
5751357522

5751457523
JS_SetOpaqueInternal(obj, wrd);
5751557524
return obj;
@@ -57840,6 +57849,35 @@ static void insert_weakref_record(JSValueConst target,
5784057849
*pwr = wr;
5784157850
}
5784257851

57852+
void JS_KeepWeakRefTargetAlive(JSContext *ctx, JSValueConst value)
57853+
{
57854+
JSRuntime *rt;
57855+
JSWeakRefKeepAliveEntry *entry;
57856+
57857+
if (!JS_IsObject(value) && !JS_IsSymbol(value))
57858+
return;
57859+
57860+
rt = JS_GetRuntime(ctx);
57861+
entry = js_malloc(ctx, sizeof(*entry));
57862+
if (!entry)
57863+
return;
57864+
57865+
entry->value = JS_DupValue(ctx, value);
57866+
list_add_tail(&entry->link, &rt->weakref_keepalive_list);
57867+
}
57868+
57869+
void JS_ClearWeakRefKeepAlives(JSRuntime *rt)
57870+
{
57871+
struct list_head *el, *el1;
57872+
57873+
list_for_each_safe(el, el1, &rt->weakref_keepalive_list) {
57874+
JSWeakRefKeepAliveEntry *entry = list_entry(el, JSWeakRefKeepAliveEntry, link);
57875+
list_del(&entry->link);
57876+
JS_FreeValueRT(rt, entry->value);
57877+
js_free_rt(rt, entry);
57878+
}
57879+
}
57880+
5784357881
/* CallSite */
5784457882

5784557883
static void js_callsite_finalizer(JSRuntime *rt, JSValueConst val)

NativeScript/napi/android/quickjs/source_ng/quickjs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,8 @@ JS_EXTERN void JS_AddIntrinsicTypedArrays(JSContext *ctx);
484484
JS_EXTERN void JS_AddIntrinsicPromise(JSContext *ctx);
485485
JS_EXTERN void JS_AddIntrinsicBigInt(JSContext *ctx);
486486
JS_EXTERN void JS_AddIntrinsicWeakRef(JSContext *ctx);
487+
JS_EXTERN void JS_KeepWeakRefTargetAlive(JSContext *ctx, JSValueConst value);
488+
JS_EXTERN void JS_ClearWeakRefKeepAlives(JSRuntime *rt);
487489
JS_EXTERN void JS_AddPerformance(JSContext *ctx);
488490
JS_EXTERN void JS_AddIntrinsicDOMException(JSContext *ctx);
489491

0 commit comments

Comments
 (0)