diff --git a/packages/swingset-liveslots/src/virtualObjectManager.js b/packages/swingset-liveslots/src/virtualObjectManager.js index 4f28d8de8966..30ac7538b930 100644 --- a/packages/swingset-liveslots/src/virtualObjectManager.js +++ b/packages/swingset-liveslots/src/virtualObjectManager.js @@ -1,6 +1,6 @@ /* eslint-disable no-use-before-define, jsdoc/require-returns-type */ -import { assert, Fail } from '@agoric/assert'; +import { assert, Fail, q } from '@agoric/assert'; import { objectMap } from '@agoric/internal'; import { assertPattern, mustMatch } from '@agoric/store'; import { defendPrototype, defendPrototypeKit } from '@agoric/store/tools.js'; @@ -11,7 +11,6 @@ import { enumerateKeysWithPrefix } from './vatstore-iterators.js'; /** @template T @typedef {import('@agoric/vat-data').DefineKindOptions} DefineKindOptions */ const { ownKeys } = Reflect; -const { details: X, quote: q } = assert; // import { kdebug } from './kdebug.js'; @@ -598,20 +597,21 @@ export function makeVirtualObjectManager( let contextMapTemplate; let prototypeTemplate; + const statePrototype = {}; // Not frozen yet + const stateToInnerSelfMap = new WeakMap(); // from state to innerSelf + harden(stateShape); stateShape === undefined || passStyleOf(stateShape) === 'copyRecord' || - assert.fail(X`A stateShape must be a copyRecord: ${q(stateShape)}`); + Fail`A stateShape must be a copyRecord: ${q(stateShape)}`; assertPattern(stateShape); const serializeSlot = (slotState, prop) => { if (stateShape !== undefined) { hasOwnPropertyOf(stateShape, prop) || - assert.fail( - X`State must only have fields described by stateShape: ${q( - ownKeys(stateShape), - )}`, - ); + Fail`State must only have fields described by stateShape: ${q( + ownKeys(stateShape), + )}`; mustMatch(slotState, stateShape[prop], prop); } return serialize(slotState); @@ -621,16 +621,57 @@ export function makeVirtualObjectManager( const slotValue = unserialize(slotData); if (stateShape !== undefined) { hasOwnPropertyOf(stateShape, prop) || - assert.fail( - X`State only has fields described by stateShape: ${q( - ownKeys(stateShape), - )}`, - ); + Fail`State only has fields described by stateShape: ${q( + ownKeys(stateShape), + )}`; mustMatch(slotValue, stateShape[prop]); } return slotValue; }; + const getInnerSelf = state => { + stateToInnerSelfMap.has(state) || + Fail`state accessors can only be applied to state`; + let innerSelf = stateToInnerSelfMap.get(state); + if (innerSelf.rawState) { + cache.refresh(innerSelf); + } else { + innerSelf = cache.lookup(innerSelf.baseRef, true); + stateToInnerSelfMap.set(state, innerSelf); + } + return innerSelf; + }; + + const makeFieldDescriptor = prop => { + return harden({ + get() { + const innerSelf = getInnerSelf(this); + return unserializeSlot(innerSelf.rawState[prop], prop); + }, + set(value) { + const innerSelf = getInnerSelf(this); + const before = innerSelf.rawState[prop]; + const after = serializeSlot(value, prop); + assertAcceptableSyscallCapdataSize([after]); + if (isDurable) { + after.slots.forEach((vref, index) => { + vrm.isDurable(vref) || + Fail`value for ${q(prop)} is not durable at slot ${q( + index, + )} of ${after}`; + }); + } + vrm.updateReferenceCounts(before.slots, after.slots); + innerSelf.rawState[prop] = after; + cache.markDirty(innerSelf); + }, + enumerable: true, + configurable: false, + }); + }; + + harden(statePrototype); + const facetiousness = assessFacetiousness(behavior); switch (facetiousness) { case 'one': { @@ -717,37 +758,15 @@ export function makeVirtualObjectManager( } } - const state = {}; + const state = { __proto__: statePrototype }; if (!initializing) { ensureState(); } for (const prop of Object.getOwnPropertyNames(innerSelf.rawState)) { - Object.defineProperty(state, prop, { - get: () => { - ensureState(); - return unserializeSlot(innerSelf.rawState[prop], prop); - }, - set: value => { - ensureState(); - const before = innerSelf.rawState[prop]; - const after = serializeSlot(value, prop); - assertAcceptableSyscallCapdataSize([after]); - if (isDurable) { - after.slots.forEach((vref, index) => { - vrm.isDurable(vref) || - Fail`value for ${q(prop)} is not durable at slot ${q( - index, - )} of ${after}`; - }); - } - vrm.updateReferenceCounts(before.slots, after.slots); - innerSelf.rawState[prop] = after; - cache.markDirty(innerSelf); - }, - enumerable: true, - }); + Object.defineProperty(state, prop, makeFieldDescriptor(prop)); } harden(state); + stateToInnerSelfMap.set(state, innerSelf); if (initializing) { cache.remember(innerSelf);