Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WeakRef taming follows taming pattern #1931

Merged
merged 3 commits into from
Oct 27, 2020
Merged
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
127 changes: 105 additions & 22 deletions packages/SwingSet/src/weakref.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
/* global globalThis */

import { assert, details as d } from '@agoric/assert';

const { defineProperties } = Object;

/*
* We retain a measure of compatibility with Node.js v12, which does not
* expose WeakRef or FinalizationRegistry (there is a --flag for it, but it's
Expand All @@ -14,25 +18,104 @@
*
*/

function FakeWeakRef(obj) {
const wr = Object.create({
deref: () => obj,
});
delete wr.constructor;
return wr;
}

function FakeFinalizationRegistry(_callback) {
const fr = Object.create({
register: (_obj, _handle) => undefined,
unregister: _handle => undefined,
});
delete fr.constructor;
return fr;
}

const WR = globalThis.WeakRef || FakeWeakRef;
const FR = globalThis.FinalizationRegistry || FakeFinalizationRegistry;

export const WeakRef = WR;
export const FinalizationRegistry = FR;
// TODO We need to migrate this into a ses-level tame-weakref.js, to happen
// as part of `lockdown`. In anticipation, this uses some of the patterns
// followed by the other tamings there.

// Emulate the internal [[WeakRefTarget]] slot. Despite the term "Weak" in the
// "WeakMap" used in the emulation, this map holds the target strongly. The only
// weakness here is that the weakref,target pair can go away together if the
// weakref is not reachable.
const weakRefTarget = new WeakMap();

const FakeWeakRef = function WeakRef(target) {
assert(
new.target !== undefined,
d`WeakRef Constructor requires 'new'`,
TypeError,
);
assert.equal(
Object(target),
target,
d`WeakRef target must be an object`,
TypeError,
);
weakRefTarget.set(this, target);
};

const InertWeakRef = function WeakRef(_target) {
throw new TypeError('Not available');
};

const FakeWeakRefPrototype = {
deref() {
return weakRefTarget.get(this);
},
[Symbol.toStringTag]: 'WeakRef',
};

defineProperties(FakeWeakRef, {
prototype: { value: FakeWeakRefPrototype },
});

const WeakRef = globalThis.WeakRef || FakeWeakRef;

// If there is a real WeakRef constructor, we still make it safe before
// exporting it. Unlike https://github.com/tc39/ecma262/issues/2214
// rather than deleting the `constructor` property, we follow the other
// taming patterns and point it at a throw-only inert one.
defineProperties(WeakRef.prototype, {
constructor: { value: InertWeakRef },
});

harden(WeakRef);

export { WeakRef };

// /////////////////////////////////////////////////////////////////////////////

const FakeFinalizationRegistry = function FinalizationRegistry(
cleanupCallback,
) {
assert(
new.target !== undefined,
d`FinalizationRegistry Constructor requires 'new'`,
TypeError,
);
assert.typeof(
cleanupCallback,
'function',
d`cleanupCallback must be a function`,
);
// fall off the end with an empty instance
};

const InertFinalizationRegistry = function FinalizationRegistry(
_cleanupCallback,
) {
throw new TypeError('Not available');
};

const FakeFinalizationRegistryPrototype = {
register() {},
unregister() {},
[Symbol.toStringTag]: 'FinalizationRegistry',
};

defineProperties(FakeFinalizationRegistry, {
prototype: { value: FakeFinalizationRegistryPrototype },
});

const FinalizationRegistry =
globalThis.FinalizationRegistry || FakeFinalizationRegistry;

// If there is a real FinalizationRegistry constructor, we still make it safe
// before exporting it. Rather than deleting the `constructor` property, we
// follow the other taming patterns and point it at a throw-only inert one.
defineProperties(FinalizationRegistry.prototype, {
constructor: { value: InertFinalizationRegistry },
});

harden(FinalizationRegistry);

export { FinalizationRegistry };