@@ -175,6 +175,7 @@ enum {
175
175
JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
176
176
JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
177
177
JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
178
+ JS_CLASS_WEAK_REF,
178
179
179
180
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
180
181
};
@@ -419,15 +420,22 @@ typedef union JSFloat64Union {
419
420
uint32_t u32[2];
420
421
} JSFloat64Union;
421
422
423
+ typedef struct JSWeakRefData {
424
+ JSValue target;
425
+ JSValue obj;
426
+ } JSWeakRefData;
427
+
422
428
typedef enum {
423
429
JS_WEAK_REF_KIND_MAP,
430
+ JS_WEAK_REF_KIND_WEAK_REF,
424
431
} JSWeakRefKindEnum;
425
432
426
433
typedef struct JSWeakRefRecord {
427
434
JSWeakRefKindEnum kind;
428
435
struct JSWeakRefRecord *next_weak_ref;
429
436
union {
430
437
struct JSMapRecord *map_record;
438
+ JSWeakRefData *weak_ref_data;
431
439
} u;
432
440
} JSWeakRefRecord;
433
441
@@ -1009,6 +1017,8 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val,
1009
1017
static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
1010
1018
static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
1011
1019
JS_MarkFunc *mark_func);
1020
+ static void js_weakref_finalizer(JSRuntime *rt, JSValue val);
1021
+
1012
1022
static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
1013
1023
static int JS_ToBoolFree(JSContext *ctx, JSValue val);
1014
1024
static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
@@ -2004,6 +2014,7 @@ JSContext *JS_NewContext(JSRuntime *rt)
2004
2014
JS_AddIntrinsicTypedArrays(ctx);
2005
2015
JS_AddIntrinsicPromise(ctx);
2006
2016
JS_AddIntrinsicBigInt(ctx);
2017
+ JS_AddIntrinsicWeakRef(ctx);
2007
2018
2008
2019
JS_AddPerformance(ctx);
2009
2020
@@ -43880,22 +43891,26 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
43880
43891
static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref)
43881
43892
{
43882
43893
JSWeakRefRecord *wr, *wr_next;
43894
+ JSWeakRefData *wrd;
43883
43895
JSMapRecord *mr;
43884
43896
JSMapState *s;
43885
43897
43886
43898
/* first pass to remove the records from the WeakMap/WeakSet
43887
43899
lists */
43888
43900
for(wr = *first_weak_ref; wr != NULL; wr = wr->next_weak_ref) {
43889
43901
switch(wr->kind) {
43890
- case JS_WEAK_REF_KIND_MAP: {
43902
+ case JS_WEAK_REF_KIND_MAP:
43891
43903
mr = wr->u.map_record;
43892
43904
s = mr->map;
43893
43905
assert(s->is_weak);
43894
43906
assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
43895
43907
list_del(&mr->hash_link);
43896
43908
list_del(&mr->link);
43897
43909
break;
43898
- }
43910
+ case JS_WEAK_REF_KIND_WEAK_REF:
43911
+ wrd = wr->u.weak_ref_data;
43912
+ wrd->target = JS_UNDEFINED;
43913
+ break;
43899
43914
default:
43900
43915
abort();
43901
43916
}
@@ -43906,12 +43921,16 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref)
43906
43921
for(wr = *first_weak_ref; wr != NULL; wr = wr_next) {
43907
43922
wr_next = wr->next_weak_ref;
43908
43923
switch(wr->kind) {
43909
- case JS_WEAK_REF_KIND_MAP: {
43924
+ case JS_WEAK_REF_KIND_MAP:
43910
43925
mr = wr->u.map_record;
43911
43926
JS_FreeValueRT(rt, mr->value);
43912
43927
js_free_rt(rt, mr);
43913
43928
break;
43914
- }
43929
+ case JS_WEAK_REF_KIND_WEAK_REF:
43930
+ wrd = wr->u.weak_ref_data;
43931
+ JS_SetOpaque(wrd->obj, NULL);
43932
+ js_free_rt(rt, wrd);
43933
+ break;
43915
43934
default:
43916
43935
abort();
43917
43936
}
@@ -50542,3 +50561,106 @@ void JS_AddPerformance(JSContext *ctx)
50542
50561
JS_PROP_ENUMERABLE);
50543
50562
JS_FreeValue(ctx, performance);
50544
50563
}
50564
+
50565
+ /* WeakRef */
50566
+
50567
+ static void js_weakref_finalizer(JSRuntime *rt, JSValue val)
50568
+ {
50569
+ JSWeakRefData *wrd = JS_GetOpaque(val, JS_CLASS_WEAK_REF);
50570
+ if (!wrd)
50571
+ return;
50572
+
50573
+ /* Delete weak ref */
50574
+ JSWeakRefRecord **pwr, *wr;
50575
+
50576
+ pwr = get_first_weak_ref(wrd->target);
50577
+ for(;;) {
50578
+ wr = *pwr;
50579
+ assert(wr != NULL);
50580
+ if (wr->kind == JS_WEAK_REF_KIND_WEAK_REF && wr->u.weak_ref_data == wrd)
50581
+ break;
50582
+ pwr = &wr->next_weak_ref;
50583
+ }
50584
+ *pwr = wr->next_weak_ref;
50585
+ js_free_rt(rt, wrd);
50586
+ js_free_rt(rt, wr);
50587
+ }
50588
+
50589
+ static JSValue js_weakref_constructor(JSContext *ctx, JSValueConst new_target, int argc, JSValueConst *argv)
50590
+ {
50591
+ if (JS_IsUndefined(new_target))
50592
+ return JS_ThrowTypeError(ctx, "constructor requires 'new'");
50593
+ JSValue arg = argv[0];
50594
+ switch (JS_VALUE_GET_TAG(arg)) {
50595
+ case JS_TAG_OBJECT:
50596
+ break;
50597
+ case JS_TAG_SYMBOL: {
50598
+ // Per spec: prohibit symbols registered with Symbol.for()
50599
+ JSAtomStruct *p = JS_VALUE_GET_PTR(arg);
50600
+ if (p->atom_type != JS_ATOM_TYPE_GLOBAL_SYMBOL)
50601
+ break;
50602
+ // fallthru
50603
+ }
50604
+ default:
50605
+ return JS_ThrowTypeError(ctx, "invalid target");
50606
+ }
50607
+ // TODO(saghul): short-circuit if the refcount is 1?
50608
+ JSValue obj = js_create_from_ctor(ctx, new_target, JS_CLASS_WEAK_REF);
50609
+ if (JS_IsException(obj))
50610
+ return JS_EXCEPTION;
50611
+ JSWeakRefData *wrd = js_malloc(ctx, sizeof(*wrd));
50612
+ if (!wrd) {
50613
+ JS_FreeValue(ctx, obj);
50614
+ return JS_EXCEPTION;
50615
+ }
50616
+ JSWeakRefRecord *wr, **pwr;
50617
+ wr = js_malloc(ctx, sizeof(*wr));
50618
+ if (!wr) {
50619
+ JS_FreeValue(ctx, obj);
50620
+ js_free(ctx, wrd);
50621
+ return JS_EXCEPTION;
50622
+ }
50623
+ wrd->target = arg;
50624
+ wrd->obj = obj;
50625
+ wr->kind = JS_WEAK_REF_KIND_WEAK_REF;
50626
+ wr->u.weak_ref_data = wrd;
50627
+ pwr = get_first_weak_ref(arg);
50628
+ /* Add the weak reference */
50629
+ wr->next_weak_ref = *pwr;
50630
+ *pwr = wr;
50631
+ JS_SetOpaque(obj, wrd);
50632
+ return obj;
50633
+ }
50634
+
50635
+ static JSValue js_weakref_deref(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
50636
+ {
50637
+ JSWeakRefData *wrd = JS_GetOpaque2(ctx, this_val, JS_CLASS_WEAK_REF);
50638
+ if (!wrd)
50639
+ return JS_EXCEPTION;
50640
+ return JS_DupValue(ctx, wrd->target);
50641
+ }
50642
+
50643
+ static const JSCFunctionListEntry js_weakref_proto_funcs[] = {
50644
+ JS_CFUNC_DEF("deref", 0, js_weakref_deref ),
50645
+ JS_PROP_STRING_DEF("[Symbol.toStringTag]", "WeakRef", JS_PROP_CONFIGURABLE ),
50646
+ };
50647
+
50648
+ static const JSClassShortDef js_weakref_class_def[] = {
50649
+ { JS_ATOM_WeakRef, js_weakref_finalizer, NULL }, /* JS_CLASS_WEAK_REF */
50650
+ };
50651
+
50652
+ void JS_AddIntrinsicWeakRef(JSContext *ctx)
50653
+ {
50654
+ JSRuntime *rt = ctx->rt;
50655
+
50656
+ if (!JS_IsRegisteredClass(rt, JS_CLASS_WEAK_REF)) {
50657
+ init_class_range(rt, js_weakref_class_def, JS_CLASS_WEAK_REF,
50658
+ countof(js_weakref_class_def));
50659
+ }
50660
+
50661
+ ctx->class_proto[JS_CLASS_WEAK_REF] = JS_NewObject(ctx);
50662
+ JS_SetPropertyFunctionList(ctx, ctx->class_proto[JS_CLASS_WEAK_REF],
50663
+ js_weakref_proto_funcs,
50664
+ countof(js_weakref_proto_funcs));
50665
+ JS_NewGlobalCConstructor(ctx, "WeakRef", js_weakref_constructor, 1, ctx->class_proto[JS_CLASS_WEAK_REF]);
50666
+ }
0 commit comments