Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 12 additions & 6 deletions src/generics/lib.generics.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<T extends object> {
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 = <Opaque>target;
GC_general_register_disappearing_link(Ref(this._target), <Opaque>target);
this._hidden = ~(<index><Opaque>target);
GC_general_register_disappearing_link(<Reference<Opaque>>Ref(this._hidden), <Opaque>target);
}

deref(): T | undefined {
if (this._target != null) {
return <T>this._target;
const hidden = this._hidden;
if (hidden == 0) {
return undefined;
}
return undefined;
return <T><Opaque>(~hidden);
}
}
24 changes: 19 additions & 5 deletions tests/weakref_basic.ts
Original file line number Diff line number Diff line change
@@ -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) {
}
Expand All @@ -9,17 +11,29 @@ function makeAndDrop(): WeakRef<Foo> {
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<Foo>): 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<Foo>(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");
}