Skip to content

Commit 5c136ed

Browse files
committed
Implement WeakRef
1 parent 1df9615 commit 5c136ed

File tree

4 files changed

+129
-5
lines changed

4 files changed

+129
-5
lines changed

quickjs-atom.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -210,6 +210,7 @@ DEF(Float32Array, "Float32Array")
210210
DEF(Float64Array, "Float64Array")
211211
DEF(DataView, "DataView")
212212
DEF(BigInt, "BigInt")
213+
DEF(WeakRef, "WeakRef")
213214
DEF(Map, "Map")
214215
DEF(Set, "Set") /* Map + 1 */
215216
DEF(WeakMap, "WeakMap") /* Map + 2 */

quickjs.c

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,7 @@ enum {
175175
JS_CLASS_ASYNC_FROM_SYNC_ITERATOR, /* u.async_from_sync_iterator_data */
176176
JS_CLASS_ASYNC_GENERATOR_FUNCTION, /* u.func */
177177
JS_CLASS_ASYNC_GENERATOR, /* u.async_generator_data */
178+
JS_CLASS_WEAK_REF,
178179

179180
JS_CLASS_INIT_COUNT, /* last entry for predefined classes */
180181
};
@@ -419,15 +420,22 @@ typedef union JSFloat64Union {
419420
uint32_t u32[2];
420421
} JSFloat64Union;
421422

423+
typedef struct JSWeakRefData {
424+
JSValue target;
425+
JSValue obj;
426+
} JSWeakRefData;
427+
422428
typedef enum {
423429
JS_WEAK_REF_KIND_MAP,
430+
JS_WEAK_REF_KIND_WEAK_REF,
424431
} JSWeakRefKindEnum;
425432

426433
typedef struct JSWeakRefRecord {
427434
JSWeakRefKindEnum kind;
428435
struct JSWeakRefRecord *next_weak_ref;
429436
union {
430437
struct JSMapRecord *map_record;
438+
JSWeakRefData *weak_ref_data;
431439
} u;
432440
} JSWeakRefRecord;
433441

@@ -1009,6 +1017,8 @@ static void js_promise_mark(JSRuntime *rt, JSValueConst val,
10091017
static void js_promise_resolve_function_finalizer(JSRuntime *rt, JSValue val);
10101018
static void js_promise_resolve_function_mark(JSRuntime *rt, JSValueConst val,
10111019
JS_MarkFunc *mark_func);
1020+
static void js_weakref_finalizer(JSRuntime *rt, JSValue val);
1021+
10121022
static JSValue JS_ToStringFree(JSContext *ctx, JSValue val);
10131023
static int JS_ToBoolFree(JSContext *ctx, JSValue val);
10141024
static int JS_ToInt32Free(JSContext *ctx, int32_t *pres, JSValue val);
@@ -2004,6 +2014,7 @@ JSContext *JS_NewContext(JSRuntime *rt)
20042014
JS_AddIntrinsicTypedArrays(ctx);
20052015
JS_AddIntrinsicPromise(ctx);
20062016
JS_AddIntrinsicBigInt(ctx);
2017+
JS_AddIntrinsicWeakRef(ctx);
20072018

20082019
JS_AddPerformance(ctx);
20092020

@@ -43880,22 +43891,26 @@ static void map_decref_record(JSRuntime *rt, JSMapRecord *mr)
4388043891
static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref)
4388143892
{
4388243893
JSWeakRefRecord *wr, *wr_next;
43894+
JSWeakRefData *wrd;
4388343895
JSMapRecord *mr;
4388443896
JSMapState *s;
4388543897

4388643898
/* first pass to remove the records from the WeakMap/WeakSet
4388743899
lists */
4388843900
for(wr = *first_weak_ref; wr != NULL; wr = wr->next_weak_ref) {
4388943901
switch(wr->kind) {
43890-
case JS_WEAK_REF_KIND_MAP: {
43902+
case JS_WEAK_REF_KIND_MAP:
4389143903
mr = wr->u.map_record;
4389243904
s = mr->map;
4389343905
assert(s->is_weak);
4389443906
assert(!mr->empty); /* no iterator on WeakMap/WeakSet */
4389543907
list_del(&mr->hash_link);
4389643908
list_del(&mr->link);
4389743909
break;
43898-
}
43910+
case JS_WEAK_REF_KIND_WEAK_REF:
43911+
wrd = wr->u.weak_ref_data;
43912+
wrd->target = JS_UNDEFINED;
43913+
break;
4389943914
default:
4390043915
abort();
4390143916
}
@@ -43906,12 +43921,16 @@ static void reset_weak_ref(JSRuntime *rt, JSWeakRefRecord **first_weak_ref)
4390643921
for(wr = *first_weak_ref; wr != NULL; wr = wr_next) {
4390743922
wr_next = wr->next_weak_ref;
4390843923
switch(wr->kind) {
43909-
case JS_WEAK_REF_KIND_MAP: {
43924+
case JS_WEAK_REF_KIND_MAP:
4391043925
mr = wr->u.map_record;
4391143926
JS_FreeValueRT(rt, mr->value);
4391243927
js_free_rt(rt, mr);
4391343928
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;
4391543934
default:
4391643935
abort();
4391743936
}
@@ -50542,3 +50561,106 @@ void JS_AddPerformance(JSContext *ctx)
5054250561
JS_PROP_ENUMERABLE);
5054350562
JS_FreeValue(ctx, performance);
5054450563
}
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+
}

quickjs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -364,6 +364,7 @@ JS_EXTERN void JS_AddIntrinsicMapSet(JSContext *ctx);
364364
JS_EXTERN void JS_AddIntrinsicTypedArrays(JSContext *ctx);
365365
JS_EXTERN void JS_AddIntrinsicPromise(JSContext *ctx);
366366
JS_EXTERN void JS_AddIntrinsicBigInt(JSContext *ctx);
367+
JS_EXTERN void JS_AddIntrinsicWeakRef(JSContext *ctx);
367368
JS_EXTERN void JS_AddPerformance(JSContext *ctx);
368369

369370
/* Only used for running 262 tests. TODO(saghul) add build time flag. */

test262.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ Uint32Array
204204
Uint8Array
205205
Uint8ClampedArray
206206
WeakMap
207-
WeakRef=skip
207+
WeakRef
208208
WeakSet
209209
well-formed-json-stringify
210210

0 commit comments

Comments
 (0)