diff --git a/src/generics/lib.generics.ts b/src/generics/lib.generics.ts index f59534c..b3b9b59 100644 --- a/src/generics/lib.generics.ts +++ b/src/generics/lib.generics.ts @@ -1504,18 +1504,24 @@ namespace __Iterator } } +// gc only: the collector clears the link. Under any other model the compiler rejects the +// GC_* call below. class WeakRef { - private _target: Opaque | null; + // The target's address complemented, as gc.h's GC_HIDE_POINTER does: a plain pointer here is + // one the collector finds when it scans this object, and it then keeps the target alive, so + // the link was never cleared. The collector sets this to 0 when the target goes. + private _hidden: index; constructor(target: T) { - this._target = target; - GC_general_register_disappearing_link(Ref(this._target), target); + this._hidden = ~(target); + GC_general_register_disappearing_link(>Ref(this._hidden), target); } deref(): T | undefined { - if (this._target != null) { - return this._target; + const hidden = this._hidden; + if (hidden == 0) { + return undefined; } - return undefined; + return (~hidden); } } \ No newline at end of file diff --git a/tests/weakref_basic.ts b/tests/weakref_basic.ts index b550640..f2d5dd8 100644 --- a/tests/weakref_basic.ts +++ b/tests/weakref_basic.ts @@ -1,3 +1,5 @@ +// gc only: WeakRef is built on the collector's weak links, and this calls GC_gcollect() itself. +// Under any other model the compiler rejects both. class Foo { constructor(public value: int) { } @@ -9,17 +11,29 @@ function makeAndDrop(): WeakRef { return ref; } +// in a function of its own, so no pointer to the object stays in main's frame for the +// collector's conservative stack scan to find +function aliveValue(ref: WeakRef): int { + const target = ref.deref(); + return target != undefined ? target.value : -1; +} + function main() { const ref = makeAndDrop(); - // object should still be alive here since no GC ran yet in a way that could collect it - const before = ref.deref(); - console.log(before != undefined); + // nothing has collected yet + assert(aliveValue(ref) == 42, "deref before a collection gives the object"); + + // one that is still held + const kept = new Foo(7); + const keptRef = new WeakRef(kept); GC_gcollect(); GC_gcollect(); - const after = ref.deref(); - console.log(after == undefined); + assert(ref.deref() == undefined, "deref after the last reference went and a collection ran"); + assert(aliveValue(keptRef) == 7, "a held object survives the collection"); + assert(kept.value == 7); + console.log("ALL DONE"); }