Skip to content

Commit

Permalink
feat: yet another overhaul of the defineKind API
Browse files Browse the repository at this point in the history
Closes #4905
  • Loading branch information
FUDCo committed Apr 5, 2022
1 parent 6fcb546 commit d4ca736
Show file tree
Hide file tree
Showing 22 changed files with 282 additions and 340 deletions.
4 changes: 2 additions & 2 deletions packages/ERTP/src/payment.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ export const makePaymentMaker = (allegedName, brand) => {
const makePayment = defineKind(
`${allegedName} payment`,
() => ({}),
() => ({
{
getAllegedBrand: () => brand,
}),
},
);
return makePayment;
};
Expand Down
20 changes: 8 additions & 12 deletions packages/SwingSet/src/liveslots/collectionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -788,24 +788,20 @@ export function makeCollectionManager(
);
}

function reanimateScalarMapStore(vobjID, proForma) {
return proForma ? null : collectionToMapStore(reanimateCollection(vobjID));
function reanimateScalarMapStore(vobjID) {
return collectionToMapStore(reanimateCollection(vobjID));
}

function reanimateScalarWeakMapStore(vobjID, proForma) {
return proForma
? null
: collectionToWeakMapStore(reanimateCollection(vobjID));
function reanimateScalarWeakMapStore(vobjID) {
return collectionToWeakMapStore(reanimateCollection(vobjID));
}

function reanimateScalarSetStore(vobjID, proForma) {
return proForma ? null : collectionToSetStore(reanimateCollection(vobjID));
function reanimateScalarSetStore(vobjID) {
return collectionToSetStore(reanimateCollection(vobjID));
}

function reanimateScalarWeakSetStore(vobjID, proForma) {
return proForma
? null
: collectionToWeakSetStore(reanimateCollection(vobjID));
function reanimateScalarWeakSetStore(vobjID) {
return collectionToWeakSetStore(reanimateCollection(vobjID));
}

const testHooks = { obtainStoreKindID, storeSizeInternal, makeCollection };
Expand Down
12 changes: 1 addition & 11 deletions packages/SwingSet/src/liveslots/liveslots.js
Original file line number Diff line number Diff line change
Expand Up @@ -690,16 +690,6 @@ function build(
let val = getValForSlot(baseRef);
if (val) {
if (virtual) {
// If it's a virtual object for which we already have a representative,
// we are going to use that existing representative to preserve ===
// equality and WeakMap key usability, BUT we are going to ask the user
// code to make a new representative anyway (which we'll discard) so
// that as far as the user code is concerned we are making a new
// representative with each act of deserialization. This way they can't
// detect reanimation by playing games inside their kind definition to
// try to observe when new representatives are created (e.g., by
// counting calls or squirreling things away in hidden WeakMaps).
vrm.reanimate(baseRef, true); // N.b.: throwing away the result
if (facet !== undefined) {
return val[facet];
}
Expand All @@ -709,7 +699,7 @@ function build(
let result;
if (virtual) {
assert.equal(type, 'object');
val = vrm.reanimate(baseRef, false);
val = vrm.reanimate(baseRef);
if (facet !== undefined) {
result = val[facet];
}
Expand Down
165 changes: 99 additions & 66 deletions packages/SwingSet/src/liveslots/virtualObjectManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,24 @@ export function makeVirtualObjectManager(
}
}

function copyMethods(behaviorTemplate) {
const obj = {};
for (const [name, func] of Object.entries(behaviorTemplate)) {
assert.typeof(func, 'function');
obj[name] = func;
}
return obj;
}

function bindMethods(context, behaviorTemplate) {
const obj = {};
for (const [name, func] of Object.entries(behaviorTemplate)) {
assert.typeof(func, 'function');
obj[name] = (...args) => Reflect.apply(func, null, [context, ...args]);
}
return obj;
}

/**
* Define a new kind of virtual object.
*
Expand All @@ -421,13 +439,14 @@ export function makeVirtualObjectManager(
* @param {*} init An initialization function that will return the initial
* state of a new instance of the kind of virtual object being defined.
*
* @param {*} actualize An actualization function that will provide the
* in-memory representative object that wraps behavior around the
* virtualized state of an instance of the object kind being defined.
* @param {*} behavior A bag of functions (in the case of a single-faceted
* object) or a bag of bags of functions (in the case of a multi-faceted
* object) that will become the methods of the object or its facets.
*
* @param {*} finish An optional finisher function that can perform
* post-creation initialization operations, such as inserting the new
* object in a cyclical object graph.
* @param {*} options Additional options to configure the virtual object kind
* being defined. Currently the only supported option is `finish`, an
* optional finisher function that can perform post-creation initialization
* operations, such as inserting the new object in a cyclical object graph.
*
* @param {boolean} durable A flag indicating whether or not the newly defined
* kind should be a durable kind.
Expand Down Expand Up @@ -496,17 +515,60 @@ export function makeVirtualObjectManager(
* reference to the state is nulled out and the object holding the state
* becomes garbage collectable.
*/
function defineKindInternal(kindID, tag, init, actualize, finish, durable) {
function defineKindInternal(kindID, tag, init, behavior, options, durable) {
const finish = options ? options.finish : undefined;
let nextInstanceID = 1;

function makeRepresentative(innerSelf, initializing, proForma) {
if (!proForma) {
let facetNames;
let behaviorTemplate;

const facetiousness = assessFacetiousness(behavior);
switch (facetiousness) {
case 'one': {
facetNames = null;
behaviorTemplate = copyMethods(behavior);
break;
}
case 'many': {
facetNames = Object.getOwnPropertyNames(behavior).sort();
assert(
innerSelf.repCount === 0,
X`${innerSelf.baseRef} already has a representative`,
facetNames.length > 1,
'a multi-facet object must have multiple facets',
);
innerSelf.repCount += 1;
behaviorTemplate = {};
for (const name of facetNames) {
behaviorTemplate[name] = copyMethods(behavior[name]);
}
break;
}
case 'not':
assert.fail(X`invalid behavior specifier for ${q(tag)}`);
default:
assert.fail(X`unexepected facetiousness: ${q(facetiousness)}`);
}
vrm.registerKind(kindID, reanimate, deleteStoredVO, durable);
vrm.rememberFacetNames(kindID, facetNames);
harden(behaviorTemplate);

function actualize(state) {
if (facetNames === null) {
const context = { state };
context.self = bindMethods(context, behaviorTemplate);
return context.self;
} else {
const context = { state, facets: {} };
for (const name of facetNames) {
context.facets[name] = bindMethods(context, behaviorTemplate[name]);
}
return context.facets;
}
}

function makeRepresentative(innerSelf, initializing) {
assert(
innerSelf.repCount === 0,
X`${innerSelf.baseRef} already has a representative`,
);
innerSelf.repCount += 1;

function ensureState() {
if (innerSelf.rawState) {
Expand Down Expand Up @@ -552,53 +614,30 @@ export function makeVirtualObjectManager(
const self = actualize(wrappedState);
let toHold;
let toExpose;
const facetiousness = assessFacetiousness(self);
switch (facetiousness) {
case 'one': {
toHold = Far(tag, self);
vrm.checkOrAcquireFacetNames(kindID, null);
toExpose = toHold;
break;
}
case 'many': {
toExpose = {};
toHold = [];
const facetNames = Object.getOwnPropertyNames(self).sort();
assert(
facetNames.length > 1,
'a multi-facet object must have multiple facets',
);
vrm.checkOrAcquireFacetNames(kindID, facetNames);
for (const facetName of facetNames) {
const facet = Far(`${tag} ${facetName}`, self[facetName]);
toExpose[facetName] = facet;
toHold.push(facet);
facetToCohort.set(facet, toHold);
}
harden(toExpose);
break;
if (facetNames === null) {
toHold = Far(tag, self);
toExpose = toHold;
} else {
toExpose = {};
toHold = [];
for (const facetName of facetNames) {
const facet = Far(`${tag} ${facetName}`, self[facetName]);
toExpose[facetName] = facet;
toHold.push(facet);
facetToCohort.set(facet, toHold);
}
case 'not':
assert.fail(X`invalid self actualization for ${q(tag)}`);
default:
assert.fail(X`unexepected facetiousness: ${q(facetiousness)}`);
}
if (!proForma) {
innerSelf.representative = toHold;
stateToRepresentative.set(wrappedState, toHold);
harden(toExpose);
}
innerSelf.representative = toHold;
stateToRepresentative.set(wrappedState, toHold);
return [toHold, toExpose, wrappedState];
}

function reanimate(baseRef, proForma) {
function reanimate(baseRef) {
// kdebug(`vo reanimate ${baseRef}`);
const innerSelf = cache.lookup(baseRef, false);
const [toHold] = makeRepresentative(innerSelf, false, proForma);
if (proForma) {
return null;
} else {
return toHold;
}
const [toHold] = makeRepresentative(innerSelf, false);
return toHold;
}

function deleteStoredVO(baseRef) {
Expand All @@ -615,8 +654,6 @@ export function makeVirtualObjectManager(
return doMoreGC;
}

vrm.registerKind(kindID, reanimate, deleteStoredVO, durable);

function makeNewInstance(...args) {
const baseRef = `o+${kindID}/${nextInstanceID}`;
nextInstanceID += 1;
Expand All @@ -635,11 +672,7 @@ export function makeVirtualObjectManager(
rawState[prop] = data;
}
const innerSelf = { baseRef, rawState, repCount: 0 };
const [toHold, toExpose, state] = makeRepresentative(
innerSelf,
true,
false,
);
const [toHold, toExpose, state] = makeRepresentative(innerSelf, true);
registerValue(baseRef, toHold, Array.isArray(toHold));
if (finish) {
finish(state, toExpose);
Expand All @@ -651,9 +684,9 @@ export function makeVirtualObjectManager(
return makeNewInstance;
}

function defineKind(tag, init, actualize, finish) {
function defineKind(tag, init, behavior, options) {
const kindID = `${allocateExportID()}`;
return defineKindInternal(kindID, tag, init, actualize, finish, false);
return defineKindInternal(kindID, tag, init, behavior, options, false);
}

let kindIDID;
Expand All @@ -669,7 +702,7 @@ export function makeVirtualObjectManager(
vrm.registerKind(kindIDID, reanimateDurableKindID, () => null, true);
}

function reanimateDurableKindID(vobjID, _proforma) {
function reanimateDurableKindID(vobjID) {
const { subid: kindID } = parseVatSlot(vobjID);
const raw = syscall.vatstoreGet(`vom.kind.${kindID}`);
assert(raw, X`unknown kind ID ${kindID}`);
Expand All @@ -694,16 +727,16 @@ export function makeVirtualObjectManager(
return kindHandle;
};

function defineDurableKind(kindHandle, init, actualize, finish) {
function defineDurableKind(kindHandle, init, behavior, options) {
const durableKindDescriptor = kindDescriptors.get(kindHandle);
assert(durableKindDescriptor);
const { kindID, tag } = durableKindDescriptor;
const maker = defineKindInternal(
kindID,
tag,
init,
actualize,
finish,
behavior,
options,
true,
);
definedDurableKinds.add(kindID);
Expand Down
Loading

0 comments on commit d4ca736

Please sign in to comment.