From ea219954e5937ac8bae49a811280f6822d84ecb6 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Mon, 17 Apr 2023 21:35:55 -0700 Subject: [PATCH] refactor: stateShape causes accessor sharing --- .../src/virtualObjectManager.js | 80 ++++++++++--------- 1 file changed, 43 insertions(+), 37 deletions(-) diff --git a/packages/swingset-liveslots/src/virtualObjectManager.js b/packages/swingset-liveslots/src/virtualObjectManager.js index 67141c7b5e7c..6aedfe96dd49 100644 --- a/packages/swingset-liveslots/src/virtualObjectManager.js +++ b/packages/swingset-liveslots/src/virtualObjectManager.js @@ -4,7 +4,7 @@ import { assert, Fail } from '@agoric/assert'; import { assertPattern, mustMatch } from '@agoric/store'; import { defendPrototype, defendPrototypeKit } from '@endo/exo/tools.js'; -import { Far, hasOwnPropertyOf, passStyleOf } from '@endo/marshal'; +import { Far, passStyleOf } from '@endo/marshal'; import { Nat } from '@endo/nat'; import { parseVatSlot, makeBaseRef } from './parseVatSlots.js'; import { enumerateKeysWithPrefix } from './vatstore-iterators.js'; @@ -13,7 +13,7 @@ import { assessFacetiousness } from './facetiousness.js'; /** @template T @typedef {import('@agoric/vat-data').DefineKindOptions} DefineKindOptions */ -const { defineProperty, getOwnPropertyNames } = Object; +const { hasOwn, defineProperty, getOwnPropertyNames } = Object; const { ownKeys } = Reflect; const { quote: q } = assert; @@ -742,6 +742,7 @@ export const makeVirtualObjectManager = ( } = options; let facetNames; // undefined or a list of strings + const statePrototype = {}; // Not frozen yet // 'multifaceted' tells us which API was used: define[Durable]Kind // vs define[Durable]KindMulti. This function checks whether @@ -817,15 +818,16 @@ export const makeVirtualObjectManager = ( saveDurableKindDescriptor(durableKindDescriptor); } - let checkStateProperty = _prop => undefined; + let checkStateProperty = _prop => {}; /** @type {(value: any, prop: string) => void} */ - let checkStatePropertyValue = (_value, _prop) => undefined; + let checkStatePropertyValue = (_value, _prop) => {}; if (stateShape) { - checkStateProperty = prop => - hasOwnPropertyOf(stateShape, prop) || - Fail`State must only have fields described by stateShape: ${q( - ownKeys(stateShape), - )}`; + checkStateProperty = prop => { + hasOwn(stateShape, prop) || + Fail`State must only have fields described by stateShape: ${q( + ownKeys(stateShape), + )}`; + }; checkStatePropertyValue = (value, prop) => { checkStateProperty(prop); mustMatch(value, stateShape[prop]); @@ -854,37 +856,41 @@ export const makeVirtualObjectManager = ( // * when state.prop is written, invoking the setter // This will cause a syscall.vatstoreGet only once per crank. + const makeFieldDescriptor = (prop, baseRef) => { + return harden({ + get: () => { + const { valueMap, capdatas } = dataCache.get(baseRef); + if (!valueMap.has(prop)) { + const value = harden(unserialize(capdatas[prop])); + checkStatePropertyValue(value, prop); + valueMap.set(prop, value); + } + return valueMap.get(prop); + }, + set: value => { + checkStatePropertyValue(value, prop); + const capdata = serialize(value); + assertAcceptableSyscallCapdataSize([capdata]); + if (isDurable) { + insistDurableCapdata(vrm, prop, capdata, true); + } + const record = dataCache.get(baseRef); // mutable + const oldSlots = record.capdatas[prop].slots; + const newSlots = capdata.slots; + vrm.updateReferenceCounts(oldSlots, newSlots); + record.capdatas[prop] = capdata; // modify in place .. + record.valueMap.set(prop, value); + dataCache.set(baseRef, record); // .. but mark as dirty + }, + enumerable: true, + }); + }; + const makeState = baseRef => { - const state = {}; + const state = { __proto__: statePrototype }; for (const prop of getOwnPropertyNames(dataCache.get(baseRef).capdatas)) { checkStateProperty(prop); - defineProperty(state, prop, { - get: () => { - const { valueMap, capdatas } = dataCache.get(baseRef); - if (!valueMap.has(prop)) { - const value = harden(unserialize(capdatas[prop])); - checkStatePropertyValue(value, prop); - valueMap.set(prop, value); - } - return valueMap.get(prop); - }, - set: value => { - checkStatePropertyValue(value, prop); - const capdata = serialize(value); - assertAcceptableSyscallCapdataSize([capdata]); - if (isDurable) { - insistDurableCapdata(vrm, prop, capdata, true); - } - const record = dataCache.get(baseRef); // mutable - const oldSlots = record.capdatas[prop].slots; - const newSlots = capdata.slots; - vrm.updateReferenceCounts(oldSlots, newSlots); - record.capdatas[prop] = capdata; // modify in place .. - record.valueMap.set(prop, value); - dataCache.set(baseRef, record); // .. but mark as dirty - }, - enumerable: true, - }); + defineProperty(state, prop, makeFieldDescriptor(prop, baseRef)); } return harden(state); };