From ef286d101dc3e7405da6c8b5b72a6281e908a516 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Wed, 31 Aug 2022 15:56:05 -0700 Subject: [PATCH] fix: review suggestions --- packages/ERTP/src/payment.js | 3 +- packages/ERTP/src/typeGuards.js | 20 ++-- packages/ERTP/test/unitTests/test-mintObj.js | 4 +- .../src/liveslots/virtualObjectManager.js | 5 +- .../SwingSet/src/liveslots/watchedPromises.js | 4 +- .../test/promise-watcher/vat-upton.js | 3 +- packages/SwingSet/test/upgrade/vat-ulrik-1.js | 11 ++- packages/SwingSet/test/upgrade/vat-ulrik-2.js | 5 +- .../virtualObjects/test-reachable-vrefs.js | 5 +- .../virtualObjects/test-retain-remotable.js | 5 +- .../test/virtualObjects/vat-orphan-bob.js | 3 +- packages/assert/src/assert.js | 15 +++ packages/inter-protocol/src/psm/psm.js | 18 ++-- .../src/vpool-xyk-amm/multipoolMarketMaker.js | 3 +- packages/store/src/index.js | 2 +- packages/store/src/keys/checkKey.js | 14 +-- .../store/src/patterns/interface-tools.js | 17 ++++ .../store/src/patterns/patternMatchers.js | 97 ++++++++++--------- packages/store/test/test-patterns.js | 6 +- packages/vat-data/src/far-class-utils.js | 6 +- packages/vat-data/src/index.js | 2 + packages/vat-data/src/kind-utils.js | 4 - packages/vat-data/src/types.d.ts | 44 +++++++++ packages/zoe/src/contractFacet/zcfZygote.js | 4 +- packages/zoe/src/makeHandle.js | 3 +- packages/zoe/src/zoeService/feeMint.js | 3 +- .../zoe/src/zoeService/installationStorage.js | 3 +- 27 files changed, 202 insertions(+), 107 deletions(-) diff --git a/packages/ERTP/src/payment.js b/packages/ERTP/src/payment.js index db3b9c6fbc7..ec6fc369330 100644 --- a/packages/ERTP/src/payment.js +++ b/packages/ERTP/src/payment.js @@ -1,5 +1,6 @@ // @ts-check +import { initEmpty } from '@agoric/store'; import { vivifyFarClass } from '@agoric/vat-data'; /** @typedef {import('@agoric/vat-data').Baggage} Baggage */ @@ -17,7 +18,7 @@ export const vivifyPaymentKind = (issuerBaggage, name, brand, PaymentI) => { issuerBaggage, `${name} payment`, PaymentI, - () => ({}), + initEmpty, { getAllegedBrand() { return brand; diff --git a/packages/ERTP/src/typeGuards.js b/packages/ERTP/src/typeGuards.js index 6ee3f39498c..19613bd0161 100644 --- a/packages/ERTP/src/typeGuards.js +++ b/packages/ERTP/src/typeGuards.js @@ -114,7 +114,7 @@ harden(isCopyBagValue); // One GOOGOLth should be enough decimal places for anybody. export const MAX_ABSOLUTE_DECIMAL_PLACES = 100; -export const AssetValueShape = M.or('nat', 'set', 'copySet', 'copyBag'); +export const AssetKindShape = M.or('nat', 'set', 'copySet', 'copyBag'); export const DisplayInfoShape = M.partial( harden({ @@ -122,7 +122,7 @@ export const DisplayInfoShape = M.partial( M.gte(-MAX_ABSOLUTE_DECIMAL_PLACES), M.lte(MAX_ABSOLUTE_DECIMAL_PLACES), ), - assetKind: AssetValueShape, + assetKind: AssetKindShape, }), harden({ // Including this empty `rest` ensures that there are no other @@ -148,19 +148,19 @@ export const BrandI = M.interface('Brand', { }); /** - * @param {Pattern} [brand] - * @param {Pattern} [assetKind] + * @param {Pattern} [brandShape] + * @param {Pattern} [assetKindShape] * @param {Pattern} [amountShape] */ export const makeIssuerInterfaces = ( - brand = BrandShape, - assetKind = AssetValueShape, + brandShape = BrandShape, + assetKindShape = AssetKindShape, amountShape = AmountShape, ) => { const IssuerI = M.interface('Issuer', { - getBrand: M.call().returns(brand), + getBrand: M.call().returns(brandShape), getAllegedName: M.call().returns(M.string()), - getAssetKind: M.call().returns(assetKind), + getAssetKind: M.call().returns(assetKindShape), getDisplayInfo: M.call().returns(DisplayInfoShape), makeEmptyPurse: M.call().returns(PurseShape), @@ -190,11 +190,11 @@ export const makeIssuerInterfaces = ( }); const PaymentI = M.interface('Payment', { - getAllegedBrand: M.call().returns(brand), + getAllegedBrand: M.call().returns(brandShape), }); const PurseI = M.interface('Purse', { - getAllegedBrand: M.call().returns(brand), + getAllegedBrand: M.call().returns(brandShape), getCurrentAmount: M.call().returns(amountShape), getCurrentAmountNotifier: M.call().returns(NotifierShape), // PurseI does *not* delay `deposit` until `srcPayment` is fulfulled. diff --git a/packages/ERTP/test/unitTests/test-mintObj.js b/packages/ERTP/test/unitTests/test-mintObj.js index 12009da19dc..c3f1e45ec3b 100644 --- a/packages/ERTP/test/unitTests/test-mintObj.js +++ b/packages/ERTP/test/unitTests/test-mintObj.js @@ -1,7 +1,7 @@ // @ts-check import { test } from '@agoric/swingset-vat/tools/prepare-test-env-ava.js'; -import { M } from '@agoric/store'; +import { initEmpty, M } from '@agoric/store'; // eslint-disable-next-line import/no-extraneous-dependencies import { assert } from '@agoric/assert'; @@ -52,7 +52,7 @@ test('mint.mintPayment set w strings AssetKind', async t => { const makeDurableHandle = name => { const kindHandle = makeKindHandle(name); - const maker = defineDurableKind(kindHandle, () => ({}), {}); + const maker = defineDurableKind(kindHandle, initEmpty, {}); return maker(); }; diff --git a/packages/SwingSet/src/liveslots/virtualObjectManager.js b/packages/SwingSet/src/liveslots/virtualObjectManager.js index c4895a79f7a..524ecfb4307 100644 --- a/packages/SwingSet/src/liveslots/virtualObjectManager.js +++ b/packages/SwingSet/src/liveslots/virtualObjectManager.js @@ -500,9 +500,8 @@ export function makeVirtualObjectManager( * * @param {DefineKindOptions<*>} 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. + * being defined. See the documentation of DefineKindOptions + * for the meaning of each option. * * @param {boolean} isDurable A flag indicating whether or not the newly defined * kind should be a durable kind. diff --git a/packages/SwingSet/src/liveslots/watchedPromises.js b/packages/SwingSet/src/liveslots/watchedPromises.js index b559847fcc0..6618ef141c9 100644 --- a/packages/SwingSet/src/liveslots/watchedPromises.js +++ b/packages/SwingSet/src/liveslots/watchedPromises.js @@ -4,7 +4,7 @@ /* eslint-disable no-lonely-if */ import { assert } from '@agoric/assert'; -import { M } from '@agoric/store'; +import { initEmpty, M } from '@agoric/store'; import { parseVatSlot } from '../lib/parseVatSlots.js'; /** @@ -148,7 +148,7 @@ export function makeWatchedPromiseManager( assert.typeof(fulfillHandler, 'function'); assert.typeof(rejectHandler, 'function'); - const makeWatcher = defineDurableKind(kindHandle, () => ({}), { + const makeWatcher = defineDurableKind(kindHandle, initEmpty, { // @ts-expect-error TS is confused by the spread operator onFulfilled: (_context, res, ...args) => fulfillHandler(res, ...args), // @ts-expect-error diff --git a/packages/SwingSet/test/promise-watcher/vat-upton.js b/packages/SwingSet/test/promise-watcher/vat-upton.js index 8f786cc5c89..16b9bef78f3 100644 --- a/packages/SwingSet/test/promise-watcher/vat-upton.js +++ b/packages/SwingSet/test/promise-watcher/vat-upton.js @@ -1,5 +1,6 @@ import { E } from '@endo/eventual-send'; import { Far } from '@endo/marshal'; +import { initEmpty } from '@agoric/store'; import { makePromiseKit } from '@endo/promise-kit'; import { provideKindHandle, @@ -27,7 +28,7 @@ export function buildRootObject(vatPowers, vatParameters, baggage) { ); // prettier-ignore - const makeDK = defineDurableKindMulti(dkHandle, () => ({}), { + const makeDK = defineDurableKindMulti(dkHandle, initEmpty, { full: { onFulfilled: (_context, res, tag) => log(`${tag} resolved ${res} version ${vatParameters.version} via VDO`), diff --git a/packages/SwingSet/test/upgrade/vat-ulrik-1.js b/packages/SwingSet/test/upgrade/vat-ulrik-1.js index 73af2e610dc..6e49c9ccd4f 100644 --- a/packages/SwingSet/test/upgrade/vat-ulrik-1.js +++ b/packages/SwingSet/test/upgrade/vat-ulrik-1.js @@ -1,6 +1,7 @@ import { Far } from '@endo/marshal'; import { E } from '@endo/eventual-send'; import { makePromiseKit } from '@endo/promise-kit'; +import { initEmpty } from '@agoric/store'; import { makeKindHandle, defineDurableKind, @@ -183,14 +184,14 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { switch (mode) { case 's2mFacetiousnessMismatch': { // upgrade should fail - defineDurableKind(mkh, () => ({}), { + defineDurableKind(mkh, initEmpty, { fooMethod: () => 1, }); break; } case 'facetCountMismatch': { // upgrade should fail - defineDurableKindMulti(mkh, () => ({}), { + defineDurableKindMulti(mkh, initEmpty, { foo: { fooMethod: () => 1, }, @@ -202,7 +203,7 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { } case 'facetNameMismatch': { // upgrade should fail - defineDurableKindMulti(mkh, () => ({}), { + defineDurableKindMulti(mkh, initEmpty, { foo: { fooMethod: () => 1, }, @@ -217,7 +218,7 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { } case 'facetOrderMismatch': { // upgrade should succeed since facet names get sorted - defineDurableKindMulti(mkh, () => ({}), { + defineDurableKindMulti(mkh, initEmpty, { baz: { bazMethod: () => 1, }, @@ -232,7 +233,7 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { } default: { // upgrade should succeed - defineDurableKindMulti(mkh, () => ({}), { + defineDurableKindMulti(mkh, initEmpty, { foo: { fooMethod: () => 1, }, diff --git a/packages/SwingSet/test/upgrade/vat-ulrik-2.js b/packages/SwingSet/test/upgrade/vat-ulrik-2.js index 3e3e061aa76..1c7f48c3ff6 100644 --- a/packages/SwingSet/test/upgrade/vat-ulrik-2.js +++ b/packages/SwingSet/test/upgrade/vat-ulrik-2.js @@ -1,6 +1,7 @@ import { Far } from '@endo/marshal'; import { E } from '@endo/eventual-send'; import { assert } from '@agoric/assert'; +import { initEmpty } from '@agoric/store'; import { defineDurableKind, defineDurableKindMulti } from '@agoric/vat-data'; const initialize = (name, imp, value) => { @@ -34,11 +35,11 @@ export const buildRootObject = (_vatPowers, vatParameters, baggage) => { if (baggage.has('mkh')) { const mkh = baggage.get('mkh'); if (vatParameters.mode === 'm2sFacetiousnessMismatch') { - defineDurableKind(mkh, () => ({}), { + defineDurableKind(mkh, initEmpty, { fooMethod: () => 2, }); } else { - defineDurableKindMulti(mkh, () => ({}), { + defineDurableKindMulti(mkh, initEmpty, { bar: { barMethod: () => 2, }, diff --git a/packages/SwingSet/test/virtualObjects/test-reachable-vrefs.js b/packages/SwingSet/test/virtualObjects/test-reachable-vrefs.js index a7621589efa..2da82ad799d 100644 --- a/packages/SwingSet/test/virtualObjects/test-reachable-vrefs.js +++ b/packages/SwingSet/test/virtualObjects/test-reachable-vrefs.js @@ -1,7 +1,8 @@ +// eslint-disable-next-line import/order import { test } from '../../tools/prepare-test-env-ava.js'; -// eslint-disable-next-line import/order import { Remotable } from '@endo/marshal'; +import { initEmpty } from '@agoric/store'; import { makeVatSlot } from '../../src/lib/parseVatSlots.js'; import { makeFakeVirtualStuff } from '../../tools/fakeVirtualSupport.js'; @@ -14,7 +15,7 @@ test('VOM tracks reachable vrefs', async t => { const weakStore = makeScalarBigWeakMapStore('test'); // empty object, used as weap map store key - const makeKey = defineKind('key', () => ({}), {}); + const makeKey = defineKind('key', initEmpty, {}); const makeHolder = defineKind('holder', held => ({ held }), { setHeld: ({ state }, held) => { state.held = held; diff --git a/packages/SwingSet/test/virtualObjects/test-retain-remotable.js b/packages/SwingSet/test/virtualObjects/test-retain-remotable.js index d3699db70be..9217f11d8ad 100644 --- a/packages/SwingSet/test/virtualObjects/test-retain-remotable.js +++ b/packages/SwingSet/test/virtualObjects/test-retain-remotable.js @@ -1,8 +1,9 @@ /* global WeakRef */ +// eslint-disable-next-line import/order import { test } from '../../tools/prepare-test-env-ava.js'; -// eslint-disable-next-line import/order import { Far } from '@endo/marshal'; +import { initEmpty } from '@agoric/store'; import engineGC from '../../src/lib-nodejs/engine-gc.js'; import { makeGcAndFinalize } from '../../src/lib-nodejs/gc-and-finalize.js'; @@ -60,7 +61,7 @@ test('remotables retained by virtualized data', async t => { const { makeScalarBigWeakMapStore } = cm; const weakStore = makeScalarBigWeakMapStore('ws'); // empty object, used as weak map store key - const makeKey = defineKind('key', () => ({}), {}); + const makeKey = defineKind('key', initEmpty, {}); const makeHolder = defineKind('holder', held => ({ held }), { setHeld: ({ state }, held) => { state.held = held; diff --git a/packages/SwingSet/test/virtualObjects/vat-orphan-bob.js b/packages/SwingSet/test/virtualObjects/vat-orphan-bob.js index 94216bb7dea..7d2661415a0 100644 --- a/packages/SwingSet/test/virtualObjects/vat-orphan-bob.js +++ b/packages/SwingSet/test/virtualObjects/vat-orphan-bob.js @@ -1,4 +1,5 @@ import { Far } from '@endo/marshal'; +import { initEmpty } from '@agoric/store'; import { defineKindMulti } from '@agoric/vat-data'; export function buildRootObject(vatPowers) { @@ -6,7 +7,7 @@ export function buildRootObject(vatPowers) { let extracted; - const makeThing = defineKindMulti('thing', () => ({}), { + const makeThing = defineKindMulti('thing', initEmpty, { regularFacet: { statelessMethod: () => 0, extractState: ({ state }) => { diff --git a/packages/assert/src/assert.js b/packages/assert/src/assert.js index 15a90dccb42..3a239ce85f5 100644 --- a/packages/assert/src/assert.js +++ b/packages/assert/src/assert.js @@ -20,6 +20,8 @@ // but we need to import it here as well. import './types.js'; +/** @typedef {import('@endo/marshal').Checker} Checker */ + const { freeze } = Object; /** @type {Assert} */ @@ -89,3 +91,16 @@ function an(str) { } freeze(an); export { an }; + +/** + * In the `assertFoo`/`isFoo`/`checkFoo` pattern, `checkFoo` has a `check` + * parameter of type `Checker`. `assertFoo` calls `checkFoo` passes + * `assertChecker` as the `check` argument. `isFoo` passes `identChecker` + * as the `check` argument. `identChecker` acts precisely like an + * identity function, but is typed as a `Checker` to indicate its + * intended use. + * + * @type {Checker} + */ +export const identChecker = (cond, _details) => cond; +harden(identChecker); diff --git a/packages/inter-protocol/src/psm/psm.js b/packages/inter-protocol/src/psm/psm.js index f96e1910c8d..3843fbcc889 100644 --- a/packages/inter-protocol/src/psm/psm.js +++ b/packages/inter-protocol/src/psm/psm.js @@ -10,6 +10,7 @@ import { floorMultiplyBy, } from '@agoric/zoe/src/contractSupport/index.js'; import { Far } from '@endo/marshal'; +import { initEmpty } from '@agoric/store'; import { handleParamGovernance, ParamTypes } from '@agoric/governance'; import { provide, vivifyKindMulti, M } from '@agoric/vat-data'; import { AmountMath } from '@agoric/ertp'; @@ -30,13 +31,18 @@ const { details: X } = assert; /** * @typedef {object} MetricsNotification - * Metrics naming scheme is that nouns are present values and past-participles are accumulative. + * Metrics naming scheme is that nouns are present values and past-participles + * are accumulative. * - * @property {Amount<'nat'>} anchorPoolBalance amount of Anchor token available to be swapped - * @property {Amount<'nat'>} feePoolBalance amount of Stable token fees available to be collected + * @property {Amount<'nat'>} anchorPoolBalance amount of Anchor token + * available to be swapped + * @property {Amount<'nat'>} feePoolBalance amount of Stable token + * fees available to be collected * - * @property {Amount<'nat'>} totalAnchorProvided running sum of Anchor ever given by this contract - * @property {Amount<'nat'>} totalStableProvided running sum of Stable ever given by this contract + * @property {Amount<'nat'>} totalAnchorProvided running sum of Anchor + * ever given by this contract + * @property {Amount<'nat'>} totalStableProvided running sum of Stable + * ever given by this contract */ /** @@ -274,7 +280,7 @@ export const start = async (zcf, privateArgs, baggage) => { const { limitedCreatorFacet, governorFacet } = // @ts-expect-error over-determined decl of creatorFacet makeVirtualGovernorFacet(creatorFacet); - const makePSM = vivifyKindMulti(baggage, 'PSM', () => ({}), { + const makePSM = vivifyKindMulti(baggage, 'PSM', initEmpty, { creatorFacet: governorFacet, limitedCreatorFacet, publicFacet: augmentVirtualPublicFacet(publicFacet), diff --git a/packages/inter-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js b/packages/inter-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js index d14a5b0bdb7..8b3856eb686 100644 --- a/packages/inter-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js +++ b/packages/inter-protocol/src/vpool-xyk-amm/multipoolMarketMaker.js @@ -11,6 +11,7 @@ import { } from '@agoric/zoe/src/contractSupport/index.js'; import { E } from '@endo/far'; import { Far } from '@endo/marshal'; +import { initEmpty } from '@agoric/store'; import { provideDurableMapStore, provideDurableWeakMapStore, @@ -483,7 +484,7 @@ const start = async (zcf, privateArgs, baggage) => { }), ); - const makeAMM = vivifyKindMulti(baggage, 'AMM', () => ({}), { + const makeAMM = vivifyKindMulti(baggage, 'AMM', initEmpty, { publicFacet, creatorFacet: governorFacet, limitedCreatorFacet, diff --git a/packages/store/src/index.js b/packages/store/src/index.js index 294ca4e168a..c8a330437c2 100755 --- a/packages/store/src/index.js +++ b/packages/store/src/index.js @@ -55,7 +55,7 @@ export { matches, fit, } from './patterns/patternMatchers.js'; -export { defendPrototype } from './patterns/interface-tools.js'; +export { defendPrototype, initEmpty } from './patterns/interface-tools.js'; export { compareRank, isRankSorted, sortByRank } from './patterns/rankOrder.js'; export { makeDecodePassable, diff --git a/packages/store/src/keys/checkKey.js b/packages/store/src/keys/checkKey.js index 64704465466..f095c3188a1 100644 --- a/packages/store/src/keys/checkKey.js +++ b/packages/store/src/keys/checkKey.js @@ -11,6 +11,8 @@ import { makeTagged, passStyleOf, } from '@endo/marshal'; +import { identChecker } from '@agoric/assert'; + import { checkElements, makeSetOfElements } from './copySet.js'; import { checkBagEntries, makeBagOfEntries } from './copyBag.js'; import { @@ -43,7 +45,7 @@ const checkPrimitiveKey = (val, check) => { * @param {Passable} val * @returns {boolean} */ -export const isPrimitiveKey = val => checkPrimitiveKey(val, x => x); +export const isPrimitiveKey = val => checkPrimitiveKey(val, identChecker); harden(isPrimitiveKey); /** @@ -75,7 +77,7 @@ export const checkScalarKey = (val, check) => { * @param {Passable} val * @returns {boolean} */ -export const isScalarKey = val => checkScalarKey(val, x => x); +export const isScalarKey = val => checkScalarKey(val, identChecker); harden(isScalarKey); /** @@ -127,7 +129,7 @@ harden(checkKey); * @param {Passable} val * @returns {boolean} */ -export const isKey = val => checkKey(val, x => x); +export const isKey = val => checkKey(val, identChecker); harden(isKey); /** @@ -176,7 +178,7 @@ harden(checkCopySet); */ /** @type {IsCopySet} */ -export const isCopySet = s => checkCopySet(s, x => x); +export const isCopySet = s => checkCopySet(s, identChecker); harden(isCopySet); /** @@ -262,7 +264,7 @@ harden(checkCopyBag); */ /** @type {IsCopyBag} */ -export const isCopyBag = b => checkCopyBag(b, x => x); +export const isCopyBag = b => checkCopyBag(b, identChecker); harden(isCopyBag); /** @@ -389,7 +391,7 @@ harden(checkCopyMap); */ /** @type {IsCopyMap} */ -export const isCopyMap = m => checkCopyMap(m, x => x); +export const isCopyMap = m => checkCopyMap(m, identChecker); harden(isCopyMap); /** diff --git a/packages/store/src/patterns/interface-tools.js b/packages/store/src/patterns/interface-tools.js index 68cbbbcfbd7..f994a908f74 100644 --- a/packages/store/src/patterns/interface-tools.js +++ b/packages/store/src/patterns/interface-tools.js @@ -63,6 +63,10 @@ const isAwaitArgGuard = argGuard => const desync = methodGuard => { const { argGuards, optionalArgGuards = [], restArgGuard } = methodGuard; + assert( + !isAwaitArgGuard(restArgGuard), + X`Rest args may not be awaited: ${restArgGuard}`, + ); const rawArgGuards = [...argGuards, ...optionalArgGuards]; const awaitIndexes = []; @@ -234,3 +238,16 @@ export const defendPrototype = ( return Far(tag, prototype); }; harden(defendPrototype); + +const emptyRecord = harden({}); + +/** + * When calling `defineDurableKind` and + * its siblings, used as the `init` function argument to indicate that the + * state record of the (virtual/durable) instances of the kind/farClass + * should be empty, and that the returned maker function should have zero + * parameters. + * + * @returns {{}} + */ +export const initEmpty = () => emptyRecord; diff --git a/packages/store/src/patterns/patternMatchers.js b/packages/store/src/patterns/patternMatchers.js index 149f744c280..44634d4bfec 100644 --- a/packages/store/src/patterns/patternMatchers.js +++ b/packages/store/src/patterns/patternMatchers.js @@ -9,7 +9,9 @@ import { passStyleOf, hasOwnPropertyOf, } from '@endo/marshal'; +import { identChecker } from '@agoric/assert'; import { applyLabelingError, listDifference } from '@agoric/internal'; + import { compareRank, getPassStyleCover, @@ -112,7 +114,7 @@ const makePatternKit = () => { ); return check( false, - X`A ${q(tag)} must be a Key but was not: ${patt}`, + X`A ${q(tag)} - Must be a Key but was not: ${patt}`, ); } case 'copyMap': { @@ -153,7 +155,7 @@ const makePatternKit = () => { * @param {Passable} patt * @returns {boolean} */ - const isPattern = patt => checkPattern(patt, x => x); + const isPattern = patt => checkPattern(patt, identChecker); /** * @param {Pattern} patt @@ -227,7 +229,7 @@ const makePatternKit = () => { * @param {Passable} patt * @returns {boolean} */ - const isKeyPattern = patt => checkKeyPattern(patt, x => x); + const isKeyPattern = patt => checkKeyPattern(patt, identChecker); /** * @param {Pattern} patt @@ -262,11 +264,11 @@ const makePatternKit = () => { return check(keyEQ(specimen, patt), X`${specimen} - Must be: ${patt}`); } assertPattern(patt); - const specStyle = passStyleOf(specimen); + const specimenStyle = passStyleOf(specimen); const pattStyle = passStyleOf(patt); switch (pattStyle) { case 'copyArray': { - if (specStyle !== 'copyArray') { + if (specimenStyle !== 'copyArray') { return check( false, X`${specimen} - Must be a copyArray to match a copyArray pattern: ${patt}`, @@ -282,34 +284,32 @@ const makePatternKit = () => { return patt.every((p, i) => checkMatches(specimen[i], p, check, i)); } case 'copyRecord': { - if (specStyle !== 'copyRecord') { + if (specimenStyle !== 'copyRecord') { return check( false, X`${specimen} - Must be a copyRecord to match a copyRecord pattern: ${patt}`, ); } - const [specNames, specValues] = recordParts(specimen); + const [specimenNames, specimenValues] = recordParts(specimen); const [pattNames, pattValues] = recordParts(patt); - { - const missing = listDifference(pattNames, specNames); - if (missing.length >= 1) { - return check( - false, - X`${specimen} - Must have missing properties ${q(missing)}`, - ); - } - const unexpected = listDifference(specNames, pattNames); - if (unexpected.length >= 1) { - return check( - false, - X`${specimen} - Must not have unexpected properties: ${q( - unexpected, - )}`, - ); - } + const missing = listDifference(pattNames, specimenNames); + if (missing.length >= 1) { + return check( + false, + X`${specimen} - Must have missing properties ${q(missing)}`, + ); + } + const unexpected = listDifference(specimenNames, pattNames); + if (unexpected.length >= 1) { + return check( + false, + X`${specimen} - Must not have unexpected properties: ${q( + unexpected, + )}`, + ); } return pattNames.every((label, i) => - checkMatches(specValues[i], pattValues[i], check, label), + checkMatches(specimenValues[i], pattValues[i], check, label), ); } case 'tagged': { @@ -318,16 +318,16 @@ const makePatternKit = () => { if (matchHelper) { return matchHelper.checkMatches(specimen, patt.payload, check); } - if (specStyle !== 'tagged' || getTag(specimen) !== pattTag) { + const specimenTag = + specimenStyle === 'tagged' ? getTag(specimen) : undefined; + if (specimenStyle !== 'tagged' || specimenTag !== pattTag) { return check( false, - X`${specimen} - Only a ${q(pattTag)} matches a ${q( - pattTag, - )} pattern: ${patt}`, + X`${specimen} - Must be tagged as a ${pattTag}: ${specimenTag}`, ); } const { payload: pattPayload } = patt; - const { payload: specPayload } = specimen; + const { payload: specimenPayload } = specimen; switch (pattTag) { case 'copySet': case 'copyBag': { @@ -344,15 +344,15 @@ const makePatternKit = () => { return false; } const pattKeySet = copyMapKeySet(pattPayload); - const specKeySet = copyMapKeySet(specPayload); + const specimenKeySet = copyMapKeySet(specimenPayload); // Compare keys as copySets - if (checkMatches(specKeySet, pattKeySet, check)) { + if (checkMatches(specimenKeySet, pattKeySet, check)) { return false; } const pattValues = pattPayload.values; - const specValues = specPayload.values; + const specimenValues = specimenPayload.values; // compare values as copyArrays - return checkMatches(specValues, pattValues, check); + return checkMatches(specimenValues, pattValues, check); } default: { assert.fail(X`Unexpected tag ${q(pattTag)}`); @@ -370,7 +370,8 @@ const makePatternKit = () => { * @param {Pattern} patt * @returns {boolean} */ - const matches = (specimen, patt) => checkMatches(specimen, patt, x => x); + const matches = (specimen, patt) => + checkMatches(specimen, patt, identChecker); /** * Returning normally indicates success. Match failure is indicated by @@ -563,13 +564,13 @@ const makePatternKit = () => { // /////////////////////// Match Helpers ///////////////////////////////////// /** @type {MatchHelper} */ - const matchAnyHelper = Far('match.any helper', { + const matchAnyHelper = Far('match:any helper', { checkMatches: (_specimen, _matcherPayload, _check) => true, checkIsMatcherPayload: (matcherPayload, check) => check( matcherPayload === undefined, - X`Payload must be undefined: ${matcherPayload}`, + X`match:any payload: ${matcherPayload} - Must be undefined`, ), getRankCover: (_matchPayload, _encodePassable) => ['', '{'], @@ -639,7 +640,7 @@ const makePatternKit = () => { if (matches(specimen, patt)) { return check( false, - X`${specimen} - must fail negated pattern: ${patt}`, + X`${specimen} - Must fail negated pattern: ${patt}`, ); } else { return true; @@ -654,7 +655,7 @@ const makePatternKit = () => { }); /** @type {MatchHelper} */ - const matchScalarHelper = Far('M.scalar helper', { + const matchScalarHelper = Far('match:scalar helper', { checkMatches: (specimen, _matcherPayload, check) => checkScalarKey(specimen, check), @@ -666,7 +667,7 @@ const makePatternKit = () => { }); /** @type {MatchHelper} */ - const matchKeyHelper = Far('M.key helper', { + const matchKeyHelper = Far('match:key helper', { checkMatches: (specimen, _matcherPayload, check) => checkKey(specimen, check), @@ -678,7 +679,7 @@ const makePatternKit = () => { }); /** @type {MatchHelper} */ - const matchPatternHelper = Far('M.pattern helper', { + const matchPatternHelper = Far('match:pattern helper', { checkMatches: (specimen, _matcherPayload, check) => checkPattern(specimen, check), @@ -690,13 +691,13 @@ const makePatternKit = () => { }); /** @type {MatchHelper} */ - const matchKindHelper = Far('M.kind helper', { + const matchKindHelper = Far('match:kind helper', { checkMatches: checkKind, checkIsMatcherPayload: (allegedKeyKind, check) => check( typeof allegedKeyKind === 'string', - X`A kind name must be a string: ${allegedKeyKind}`, + X`match:kind: payload: ${allegedKeyKind} - A kind name must be a string`, ), getRankCover: (kind, _encodePassable) => { @@ -735,7 +736,7 @@ const makePatternKit = () => { const matchRemotableHelper = Far('match:remotable helper', { checkMatches: (specimen, remotableDesc, check) => { // Unfortunate duplication of checkKind logic, but no better choices. - if (checkKind(specimen, 'remotable', x => x)) { + if (checkKind(specimen, 'remotable', identChecker)) { return true; } let specimenKind = passStyleOf(specimen); @@ -763,7 +764,7 @@ const makePatternKit = () => { getRankCover: (_remotableDesc, _encodePassable) => getPassStyleCover('remotable'), - checkKeyPattern: (_remotableDesc, _check = x => x) => true, + checkKeyPattern: (_remotableDesc, _check) => true, }); /** @type {MatchHelper} */ @@ -1025,10 +1026,10 @@ const makePatternKit = () => { let specR; let newBase; if (baseStyle === 'copyArray') { - const { length: specLen } = specimen; + const { length: specimenLen } = specimen; const { length: baseLen } = base; - if (specLen < baseLen) { - newBase = harden(base.slice(0, specLen)); + if (specimenLen < baseLen) { + newBase = harden(base.slice(0, specimenLen)); specB = specimen; // eslint-disable-next-line no-use-before-define specR = []; diff --git a/packages/store/test/test-patterns.js b/packages/store/test/test-patterns.js index 527faf60c83..8dab91690d5 100644 --- a/packages/store/test/test-patterns.js +++ b/packages/store/test/test-patterns.js @@ -36,8 +36,8 @@ const matchTests = harden([ ], noPatterns: [ [4, '3 - Must be: 4'], - [M.not(3), '3 - must fail negated pattern: 3'], - [M.not(M.any()), '3 - must fail negated pattern: "[match:any]"'], + [M.not(3), '3 - Must fail negated pattern: 3'], + [M.not(M.any()), '3 - Must fail negated pattern: "[match:any]"'], [M.string(), 'number 3 - Must be a string'], [[3, 4], '3 - Must be: [3,4]'], [M.gte(7), '3 - Must be >= 7'], @@ -284,7 +284,7 @@ const matchTests = harden([ { specimen: makeTagged('match:any', 88), yesPatterns: [M.any(), M.not(M.pattern())], - noPatterns: [[M.pattern(), 'Payload must be undefined: 88']], + noPatterns: [[M.pattern(), 'match:any payload: 88 - Must be undefined']], }, { specimen: makeTagged('match:remotable', 88), diff --git a/packages/vat-data/src/far-class-utils.js b/packages/vat-data/src/far-class-utils.js index c74d1f1b89d..0c627c67558 100644 --- a/packages/vat-data/src/far-class-utils.js +++ b/packages/vat-data/src/far-class-utils.js @@ -1,3 +1,5 @@ +import { initEmpty } from '@agoric/store'; + import { provideKindHandle } from './kind-utils.js'; import { defineKind, @@ -64,7 +66,7 @@ export const defineVirtualFarClassKit = ( thisfulMethods: true, interfaceGuard: interfaceGuardKit, }); -harden(defineVirtualFarClass); +harden(defineVirtualFarClassKit); /** * @template A,S,T @@ -190,7 +192,7 @@ export const vivifyFarInstance = ( baggage, kindName, interfaceGuard, - () => ({}), + initEmpty, methods, options, ); diff --git a/packages/vat-data/src/index.js b/packages/vat-data/src/index.js index 91f915dc264..4d5c7bc0432 100644 --- a/packages/vat-data/src/index.js +++ b/packages/vat-data/src/index.js @@ -11,3 +11,5 @@ export { vivifyFarInstance, vivifySingleton, } from './far-class-utils.js'; + +/** @template T @typedef {import('./types.js').DefineKindOptions} DefineKindOptions */ diff --git a/packages/vat-data/src/kind-utils.js b/packages/vat-data/src/kind-utils.js index ca76aa6813a..9062e8a0ec2 100644 --- a/packages/vat-data/src/kind-utils.js +++ b/packages/vat-data/src/kind-utils.js @@ -5,11 +5,7 @@ import { makeKindHandle, } from './vat-data-bindings.js'; -/** @template L,R @typedef {import('@endo/eventual-send').RemotableBrand} RemotableBrand */ /** @typedef {import('./types.js').Baggage} Baggage */ -/** @template T @typedef {import('./types.js').DefineKindOptions} DefineKindOptions */ -/** @template T @typedef {import('./types.js').KindFacet} KindFacet */ -/** @template T @typedef {import('./types.js').KindFacets} KindFacets */ /** @typedef {import('./types.js').DurableKindHandle} DurableKindHandle */ /** diff --git a/packages/vat-data/src/types.d.ts b/packages/vat-data/src/types.d.ts index 7c08e576746..8de2fc794fa 100644 --- a/packages/vat-data/src/types.d.ts +++ b/packages/vat-data/src/types.d.ts @@ -42,10 +42,54 @@ declare class DurableKindHandleClass { } export type DurableKindHandle = DurableKindHandleClass; +/** + * Grab bag of options that can be provided to `defineDurableKind` and its + * siblings. Not all options are meaningful in all contexts. See the + * doc-comments on each option. + */ type DefineKindOptions = { + /** + * If provided, the `finish` function will be called after the instance is + * made and internally registered, but before it is returned. The finish + * function is to do any post-intantiation initialization that should be + * done before exposing the object to its clients. + */ finish?: (context: C) => void; + + /** + * Meaningful to `makeScalarBigMapStore` and its siblings. These maker + * fuctions will make either virtual or durable stores, depending on + * this flag. Defaults to off, making virtual but not durable collections. + * + * Generally, durable collections are provided with `provideDurableMapStore` + * and its sibling, which use this flag internally. If you do not make + * durable collections by other means, you can consider this as + * intended for internal use only. + */ durable?: boolean; + + /** + * Intended for internal use only. + * Should the raw methods receive their `context` argument as their first + * argument or as their `this` binding? For `defineDurableKind` and its + * siblings (including `vivifySingleton`), this defaults to off, meaning that + * their behavior methods receive `context` as their first argument. + * `vivifyFarClass` and its siblings (including `vivifyFarInstance`) use + * this flag internally to indicate that their methods receive `context` + * as their `this` binding. + */ thisfulMethods?: boolean; + + /** + * Intended for internal use only. + * If an `interfaceGuard` is provided, then the raw methods passed alongside + * it wrapped by a function that first checks that this method's guard + * pattern is satisfied before calling the raw method. + * + * In `defineDurableKind` and its siblings, this defaults to off. + * `vivifyFarClass` use this internally to protect their raw class methods + * using the provided interface. + */ interfaceGuard?: object; // TODO type }; diff --git a/packages/zoe/src/contractFacet/zcfZygote.js b/packages/zoe/src/contractFacet/zcfZygote.js index 4ae0496a442..42d3917a5e5 100644 --- a/packages/zoe/src/contractFacet/zcfZygote.js +++ b/packages/zoe/src/contractFacet/zcfZygote.js @@ -4,7 +4,7 @@ import { E } from '@endo/eventual-send'; import { Far, Remotable, passStyleOf } from '@endo/marshal'; import { AssetKind } from '@agoric/ertp'; import { makePromiseKit } from '@endo/promise-kit'; -import { assertPattern } from '@agoric/store'; +import { assertPattern, initEmpty } from '@agoric/store'; import { makeScalarBigMapStore, provideDurableMapStore, @@ -241,7 +241,7 @@ export const makeZCFZygote = async ( const makeHandleOfferObj = vivifyKind( zcfBaggage, 'handleOfferObj', - () => ({}), + initEmpty, { handleOffer: (_context, invitationHandle, seatData) => { const zcfSeat = makeZCFSeat(seatData); diff --git a/packages/zoe/src/makeHandle.js b/packages/zoe/src/makeHandle.js index ccf209c73e9..1aad9c06a94 100644 --- a/packages/zoe/src/makeHandle.js +++ b/packages/zoe/src/makeHandle.js @@ -1,6 +1,7 @@ // @ts-check import { assert } from '@agoric/assert'; +import { initEmpty } from '@agoric/store'; import { provide, defineDurableKind, makeKindHandle } from '@agoric/vat-data'; import { Far } from '@endo/marshal'; @@ -19,7 +20,7 @@ export const defineDurableHandle = (baggage, handleType) => { `${handleType}KindHandle`, () => makeKindHandle(`${handleType}Handle`), ); - const makeHandle = defineDurableKind(durableHandleKindHandle, () => ({}), {}); + const makeHandle = defineDurableKind(durableHandleKindHandle, initEmpty, {}); return /** @type {() => Handle} */ (makeHandle); }; harden(defineDurableHandle); diff --git a/packages/zoe/src/zoeService/feeMint.js b/packages/zoe/src/zoeService/feeMint.js index a972ddd7c88..52a34bf6e98 100644 --- a/packages/zoe/src/zoeService/feeMint.js +++ b/packages/zoe/src/zoeService/feeMint.js @@ -1,6 +1,7 @@ // @ts-check import { makeDurableIssuerKit, AssetKind } from '@agoric/ertp'; +import { initEmpty } from '@agoric/store'; import { vivifyKindMulti, provideDurableMapStore } from '@agoric/vat-data'; const FEE_MINT_KIT = 'FeeMintKit'; @@ -46,7 +47,7 @@ const vivifyFeeMint = (zoeBaggage, feeIssuerConfig, shutdownZoeVat) => { return mintBaggage.get(FEE_MINT_KIT); }; - const makeFeeMintKit = vivifyKindMulti(mintBaggage, 'FeeMint', () => ({}), { + const makeFeeMintKit = vivifyKindMulti(mintBaggage, 'FeeMint', initEmpty, { feeMint: { getFeeIssuerKit, getFeeMintAccess: ({ facets }) => facets.feeMintAccess, diff --git a/packages/zoe/src/zoeService/installationStorage.js b/packages/zoe/src/zoeService/installationStorage.js index 71edac7595b..fc3c1ab5e87 100644 --- a/packages/zoe/src/zoeService/installationStorage.js +++ b/packages/zoe/src/zoeService/installationStorage.js @@ -7,6 +7,7 @@ import { provideDurableWeakMapStore, vivifyKind, } from '@agoric/vat-data'; +import { initEmpty } from '@agoric/store'; /** @typedef { import('@agoric/swingset-vat').BundleID} BundleID */ /** @typedef {import('@agoric/vat-data').Baggage} Baggage */ @@ -33,7 +34,7 @@ export const makeInstallationStorage = ( const makeBundleIDInstallation = vivifyKind( zoeBaggage, 'BundleIDInstallation', - () => ({}), + initEmpty, { getBundle: _context => assert.fail('bundleID-based Installation') }, );