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

refactor: stateShape causes accessor sharing #7444

Merged
merged 2 commits into from
Apr 18, 2023
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
95 changes: 62 additions & 33 deletions packages/swingset-liveslots/src/virtualObjectManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -742,6 +742,15 @@ export const makeVirtualObjectManager = (
} = options;

let facetNames; // undefined or a list of strings
const statePrototype = {}; // Not frozen yet
const stateToBaseRefMap = new WeakMap();

const getBaseRef = state => {
const baseRef = stateToBaseRefMap.get(state);
baseRef !== undefined ||
Fail`state accessors can only be applied to state`;
return baseRef;
};

// 'multifaceted' tells us which API was used: define[Durable]Kind
// vs define[Durable]KindMulti. This function checks whether
Expand Down Expand Up @@ -818,9 +827,9 @@ export const makeVirtualObjectManager = (
}

/** @type {(prop: string) => void} */
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 => {
hasOwn(stateShape, prop) ||
Expand Down Expand Up @@ -856,39 +865,59 @@ export const makeVirtualObjectManager = (
// * when state.prop is written, invoking the setter
// This will cause a syscall.vatstoreGet only once per crank.

const makeState = baseRef => {
const state = {};
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 => {
const makeFieldDescriptor = prop => {
return harden({
get() {
const baseRef = getBaseRef(this);
const { valueMap, capdatas } = dataCache.get(baseRef);
if (!valueMap.has(prop)) {
const value = harden(unserialize(capdatas[prop]));
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,
});
valueMap.set(prop, value);
}
return valueMap.get(prop);
},
set(value) {
const baseRef = getBaseRef(this);
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,
configurable: false,
});
};

if (stateShape !== undefined) {
for (const prop of ownKeys(stateShape)) {
defineProperty(statePrototype, prop, makeFieldDescriptor(prop));
}
}

harden(statePrototype);

const makeState = baseRef => {
const state = { __proto__: statePrototype };
if (stateShape === undefined) {
for (const prop of ownKeys(dataCache.get(baseRef).capdatas)) {
assert(typeof prop === 'string');
checkStateProperty(prop);
defineProperty(state, prop, makeFieldDescriptor(prop));
}
}
return harden(state);
harden(state);
stateToBaseRefMap.set(state, baseRef);
return state;
};

// More specifically, behavior functions receive a "context"
Expand Down