Skip to content

Commit

Permalink
fix: stateShape causes accessor sharing
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Mar 30, 2023
1 parent 316deca commit d7f9d82
Showing 1 changed file with 57 additions and 38 deletions.
95 changes: 57 additions & 38 deletions packages/swingset-liveslots/src/virtualObjectManager.js
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -11,7 +11,6 @@ import { enumerateKeysWithPrefix } from './vatstore-iterators.js';
/** @template T @typedef {import('@agoric/vat-data').DefineKindOptions<T>} DefineKindOptions */

const { ownKeys } = Reflect;
const { details: X, quote: q } = assert;

// import { kdebug } from './kdebug.js';

Expand Down Expand Up @@ -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);
Expand All @@ -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': {
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit d7f9d82

Please sign in to comment.