From a35b833140f50bb21ab2f4fb9685eb07a49ec0f9 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Thu, 17 Aug 2023 21:20:06 -0700 Subject: [PATCH] refactor: Prepare for better guard typing from endo --- packages/ERTP/src/issuerKit.js | 8 +- packages/ERTP/src/payment.js | 5 + packages/ERTP/src/types-ambient.js | 5 + packages/store/src/stores/store-utils.js | 2 + packages/store/src/types.js | 110 +++--------------- .../swingset-liveslots/src/vatDataTypes.d.ts | 34 +++++- .../src/virtualObjectManager.js | 11 +- packages/time/src/timeMath.js | 3 +- packages/time/src/types.d.ts | 2 +- packages/vat-data/src/exo-utils.js | 31 +++-- packages/zoe/src/contractFacet/types.js | 1 + 11 files changed, 92 insertions(+), 120 deletions(-) diff --git a/packages/ERTP/src/issuerKit.js b/packages/ERTP/src/issuerKit.js index 86b1107787d..7b13135a14e 100644 --- a/packages/ERTP/src/issuerKit.js +++ b/packages/ERTP/src/issuerKit.js @@ -123,8 +123,8 @@ export const hasIssuer = baggage => baggage.has(INSTANCE_KEY); * `displayInfo` gives information to the UI on how to display the amount. * @param {Baggage} issuerBaggage * @param {string} name - * @param {K} [assetKind=AssetKind.NAT] - * @param {AdditionalDisplayInfo} [displayInfo={}] + * @param {K} [assetKind] + * @param {AdditionalDisplayInfo} [displayInfo] * @param {ShutdownWithFailure} [optShutdownWithFailure] If this issuer fails in * the middle of an atomic action (which btw should never happen), it * potentially leaves its ledger in a corrupted state. If this function was @@ -164,8 +164,8 @@ harden(makeDurableIssuerKit); * * `displayInfo` gives information to the UI on how to display the amount. * @param {string} name - * @param {K} [assetKind='nat'] - * @param {AdditionalDisplayInfo} [displayInfo={}] + * @param {K} [assetKind] + * @param {AdditionalDisplayInfo} [displayInfo] * @param {ShutdownWithFailure} [optShutdownWithFailure] If this issuer fails in * the middle of an atomic action (which btw should never happen), it * potentially leaves its ledger in a corrupted state. If this function was diff --git a/packages/ERTP/src/payment.js b/packages/ERTP/src/payment.js index 9c3a0e64428..809be8cc965 100644 --- a/packages/ERTP/src/payment.js +++ b/packages/ERTP/src/payment.js @@ -3,6 +3,11 @@ import { initEmpty } from '@agoric/store'; import { prepareExoClass } from '@agoric/vat-data'; +/** @typedef {import('@endo/patterns').MethodGuard} MethodGuard */ +/** + * @template {Record} [T=Record] + * @typedef {import('@endo/patterns').InterfaceGuard} InterfaceGuard + */ /** @typedef {import('@agoric/vat-data').Baggage} Baggage */ /** diff --git a/packages/ERTP/src/types-ambient.js b/packages/ERTP/src/types-ambient.js index 4522ebfee6d..32951d1c67e 100644 --- a/packages/ERTP/src/types-ambient.js +++ b/packages/ERTP/src/types-ambient.js @@ -2,6 +2,11 @@ /// +/** + * @template {Key} [K=Key] + * @typedef {import('@endo/patterns').CopyBag} CopyBag + */ + /** * @template {AssetKind} [K=AssetKind] * @typedef {object} Amount Amounts are descriptions of digital assets, diff --git a/packages/store/src/stores/store-utils.js b/packages/store/src/stores/store-utils.js index 2b1bd95f241..b6a359bbce8 100644 --- a/packages/store/src/stores/store-utils.js +++ b/packages/store/src/stores/store-utils.js @@ -1,6 +1,8 @@ import { Far } from '@endo/marshal'; import { M, matches } from '@endo/patterns'; +/** @typedef {import('@endo/marshal').RankCompare} RankCompare */ + const { Fail, quote: q } = assert; // TODO: Undate `@endo/patterns` to export the original, and delete the diff --git a/packages/store/src/types.js b/packages/store/src/types.js index 2d48326843c..d54e837a148 100644 --- a/packages/store/src/types.js +++ b/packages/store/src/types.js @@ -1,118 +1,44 @@ /// -/** @typedef {import('@endo/marshal').Passable} Passable */ -/** @typedef {import('@endo/marshal').PassStyle} PassStyle */ -/** @typedef {import('@endo/marshal').CopyTagged} CopyTagged */ -/** @template T @typedef {import('@endo/marshal').CopyRecord} CopyRecord */ -/** @template T @typedef {import('@endo/marshal').CopyArray} CopyArray */ -/** @typedef {import('@endo/marshal').Checker} Checker */ -/** @typedef {import('@endo/marshal/src/rankOrder').RankCompare} RankCompare */ -/** @typedef {import('@endo/marshal/src/rankOrder').RankComparison} RankComparison */ - -// ///////////////////////////////////////////////////////////////////////////// -// Placeholder redundant types, to be imported from `@endo/patterns` instead. - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** @typedef {Passable} Key */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** @typedef {Passable} Pattern */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead /** - * @template {Key} [K=Key] - * @typedef {CopyTagged & { - * [Symbol.toStringTag]: 'copySet'; - * payload: K[]; - * }} CopySet + * Note TODO https://github.com/endojs/endo/issues/1488 + * + * @typedef {import('@endo/pass-style').Passable} Passable */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead +/** @typedef {import('@endo/pass-style').CopyTagged} CopyTagged */ +/** @typedef {import('@endo/patterns').Pattern} Pattern */ +/** @typedef {import('@endo/patterns').Key} Key */ /** * @template {Key} [K=Key] - * @typedef {CopyTagged & { - * [Symbol.toStringTag]: 'copyBag'; - * payload: [K, bigint][]; - * }} CopyBag + * @typedef {import('@endo/patterns').CopySet} CopySet */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead /** * @template {Key} [K=Key] * @template {Passable} [V=Passable] - * @typedef {CopyTagged & { - * [Symbol.toStringTag]: 'copyMap'; - * payload: { keys: K[]; values: V[] }; - * }} CopyMap - */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** - * @typedef {object} GuardMakers - * @property {>( - * interfaceName: string, - * methodGuards: M, - * options?: { sloppy?: boolean }, - * ) => InterfaceGuard} interface - * @property {(...argGuards: ArgGuard[]) => MethodGuardMaker} call Guard a - * synchronous call - * @property {(...argGuards: ArgGuard[]) => MethodGuardMaker} callWhen Guard an - * async call - * @property {(argGuard: ArgGuard) => ArgGuard} await Guard an await + * @typedef {import('@endo/patterns').CopyMap} CopyMap */ -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** @typedef {(...args: any[]) => any} Method */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** - * @typedef {{ - * klass: 'Interface'; - * interfaceName: string; - * methodGuards: Record; - * sloppy?: boolean; - * }} InterfaceGuard - */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** @typedef {any} MethodGuardMaker */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** - * @typedef {{ - * klass: 'methodGuard'; - * callKind: 'sync' | 'async'; - * returnGuard: unknown; - * }} MethodGuard - */ - -// TODO placeholder. Figure out how to import from `@endo/patterns` instead -/** @typedef {any} ArgGuard */ - -// ///////////////////////////////////////////////////////////////////////////// - /** * @typedef {object} StoreOptions Of the dimensions on which KeyedStores can * differ, we only represent a few of them as standard options. A given store * maker should document which options it supports, as well as its positions * on dimensions for which it does not support options. - * @property {boolean} [longLived=true] Which way to optimize a weak store. True + * @property {boolean} [longLived] Which way to optimize a weak store. True * means that we expect this weak store to outlive most of its keys, in which * case we internally may use a JavaScript `WeakMap`. Otherwise we internally * may use a JavaScript `Map`. Defaults to true, so please mark short lived * stores explicitly. - * @property {boolean} [durable=false] The contents of this store survive - * termination of its containing process, allowing for restart or upgrade but - * at the cost of forbidding storage of references to ephemeral data. Defaults - * to false. - * @property {boolean} [fakeDurable=false] This store pretends to be a durable - * store but does not enforce that the things stored in it actually be - * themselves durable (whereas an actual durable store would forbid storage of - * such items). This is in service of allowing incremental transition to use - * of durable stores, to enable normal operation and testing when some stuff + * @property {boolean} [durable] The contents of this store survive termination + * of its containing process, allowing for restart or upgrade but at the cost + * of forbidding storage of references to ephemeral data. Defaults to false. + * @property {boolean} [fakeDurable] This store pretends to be a durable store + * but does not enforce that the things stored in it actually be themselves + * durable (whereas an actual durable store would forbid storage of such + * items). This is in service of allowing incremental transition to use of + * durable stores, to enable normal operation and testing when some stuff * intended to eventually be durable has not yet been made durable. A store * marked as fakeDurable will appear to operate normally but any attempt to - * upgrade its containing vat will fail with an error. + * upgrade its containing vat will fail with an error. Defaults to false. * @property {Pattern} [keyShape] * @property {Pattern} [valueShape] */ diff --git a/packages/swingset-liveslots/src/vatDataTypes.d.ts b/packages/swingset-liveslots/src/vatDataTypes.d.ts index ff9770975f7..84a8e4ef96c 100644 --- a/packages/swingset-liveslots/src/vatDataTypes.d.ts +++ b/packages/swingset-liveslots/src/vatDataTypes.d.ts @@ -5,16 +5,19 @@ * Behavior is a description when defining a kind of what facets it will have. * For the non-multi defineKind, there is just one facet so it doesn't have a key. */ +import type { InterfaceGuard, Pattern } from '@endo/patterns'; import type { - InterfaceGuard, MapStore, - Pattern, SetStore, StoreOptions, WeakMapStore, WeakSetStore, } from '@agoric/store'; +// TODO should be moved into @endo/patterns and eventually imported here +// instead of this local definition. +export type InterfaceGuardKit = Record; + export type { MapStore, Pattern }; // This needs `any` values. If they were `unknown`, code that uses Baggage @@ -97,15 +100,36 @@ export type DefineKindOptions = { /** * Intended for internal use only. + * Only applicable if this is a class kind. A class kit kind should use + * `interfaceGuardKit` instead. + * * If an `interfaceGuard` is provided, then the raw methods passed alongside * it are 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. - * `prepareExoClass` use this internally to protect their raw class methods + * In `defineDurableKind` and its siblings, this defaults to `undefined`. + * Exo classes use this internally to protect their raw class methods * using the provided interface. + * In absence, an exo is protected anyway, while a bare kind is + * not (detected by `!thisfulMethods`), + */ + interfaceGuard?: InterfaceGuard; + + /** + * Intended for internal use only. + * Only applicable if this is a class kit kind. A class kind should use + * `interfaceGuard` instead. + * + * If an `interfaceGuardKit` is provided, then each member of the + * interfaceGuardKit is used to guard the corresponding facet of the + * class kit. + * + * In `defineDurableKindMulti` and its siblings, this defaults to `undefined`. + * Exo class kits use this internally to protect their facets. + * In absence, an exo is protected anyway, while a bare kind is + * not (detected by `!thisfulMethods`), */ - interfaceGuard?: InterfaceGuard; + interfaceGuardKit?: InterfaceGuardKit; }; export type VatData = { diff --git a/packages/swingset-liveslots/src/virtualObjectManager.js b/packages/swingset-liveslots/src/virtualObjectManager.js index 301cc8f0e0f..782508d1cce 100644 --- a/packages/swingset-liveslots/src/virtualObjectManager.js +++ b/packages/swingset-liveslots/src/virtualObjectManager.js @@ -719,6 +719,7 @@ export const makeVirtualObjectManager = ( stateShape = undefined, thisfulMethods = false, interfaceGuard = undefined, + interfaceGuardKit = undefined, } = options; const statePrototype = {}; // Not frozen yet @@ -739,11 +740,19 @@ export const makeVirtualObjectManager = ( switch (assessFacetiousness(behavior)) { case 'one': { assert(!multifaceted); + interfaceGuardKit === undefined || + Fail`Use an interfaceGuard, not interfaceGuardKit, to protect class ${q( + tag, + )}`; proposedFacetNames = undefined; break; } case 'many': { assert(multifaceted); + interfaceGuard === undefined || + Fail`Use an interfaceGuardKit, not an interfaceGuard, to protect class kit ${q( + tag, + )}`; proposedFacetNames = ownKeys(behavior).sort(); break; } @@ -954,7 +963,7 @@ export const makeVirtualObjectManager = ( makeContextProviderKit(contextCache, getSlotForVal, facetNames), behavior, thisfulMethods, - interfaceGuard, + interfaceGuardKit, ); } else { proto = defendPrototype( diff --git a/packages/time/src/timeMath.js b/packages/time/src/timeMath.js index 143718491f9..ef676985184 100644 --- a/packages/time/src/timeMath.js +++ b/packages/time/src/timeMath.js @@ -4,13 +4,14 @@ import { RelativeTimeRecordShape, TimestampRecordShape } from './typeGuards.js'; const { Fail, quote: q } = assert; /** + * @typedef {import('@endo/marshal').RankComparison} RankComparison + * * @typedef {import('./types').TimerBrand} TimerBrand * @typedef {import('./types').Timestamp} Timestamp * @typedef {import('./types').RelativeTime} RelativeTime * @typedef {import('./types').RelativeTimeValue} RelativeTimeValue * @typedef {import('./types').TimestampValue} TimestampValue * @typedef {import('./types').TimeMathType} TimeMathType - * */ /** diff --git a/packages/time/src/types.d.ts b/packages/time/src/types.d.ts index b5f2b1522a1..a24d1732466 100644 --- a/packages/time/src/types.d.ts +++ b/packages/time/src/types.d.ts @@ -1,7 +1,7 @@ /* eslint-disable no-use-before-define, no-undef */ import type { ERef } from '@endo/eventual-send'; -import type { RankComparison } from '@agoric/store'; +import type { RankComparison } from '@endo/marshal'; /// diff --git a/packages/vat-data/src/exo-utils.js b/packages/vat-data/src/exo-utils.js index ddba5f0f109..46e1f3911ac 100644 --- a/packages/vat-data/src/exo-utils.js +++ b/packages/vat-data/src/exo-utils.js @@ -4,6 +4,11 @@ import { initEmpty } from '@agoric/store'; import { provide, VatData as globalVatData } from './vat-data-bindings.js'; +/** @typedef {import('@endo/patterns').MethodGuard} MethodGuard */ +/** + * @template {Record} [T=Record] + * @typedef {import('@endo/patterns').InterfaceGuard} InterfaceGuard + */ /** @template L,R @typedef {import('@endo/eventual-send').RemotableBrand} RemotableBrand */ /** @template T @typedef {import('@endo/far').ERef} ERef */ /** @typedef {import('@agoric/swingset-liveslots').Baggage} Baggage */ @@ -11,6 +16,7 @@ import { provide, VatData as globalVatData } from './vat-data-bindings.js'; /** @template T @typedef {import('@agoric/swingset-liveslots').KindFacet} KindFacet */ /** @template T @typedef {import('@agoric/swingset-liveslots').KindFacets} KindFacets */ /** @typedef {import('@agoric/swingset-liveslots').DurableKindHandle} DurableKindHandle */ +/** @typedef {import('@agoric/swingset-liveslots').InterfaceGuardKit} InterfaceGuardKit */ /** * Make a version of the argument function that takes a kind context but @@ -81,12 +87,11 @@ export const makeExoUtils = VatData => { ); harden(prepareKindMulti); - // TODO interfaceGuard type https://github.com/Agoric/agoric-sdk/issues/6206 /** * @template {(...args: any) => any} I init state function * @template T behavior * @param {string} tag - * @param {any} interfaceGuard + * @param {InterfaceGuard | undefined} interfaceGuard * @param {I} init * @param {T & ThisType<{ * self: T, @@ -106,12 +111,11 @@ export const makeExoUtils = VatData => { }); harden(defineVirtualExoClass); - // TODO interfaceGuard type https://github.com/Agoric/agoric-sdk/issues/6206 /** * @template {(...args: any) => any} I init state function * @template {Record>} T facets * @param {string} tag - * @param {any} interfaceGuardKit + * @param {InterfaceGuardKit | undefined} interfaceGuardKit * @param {I} init * @param {T & ThisType<{ * facets: T, @@ -133,16 +137,15 @@ export const makeExoUtils = VatData => { defineKindMulti(tag, init, facets, { ...options, thisfulMethods: true, - interfaceGuard: interfaceGuardKit, + interfaceGuardKit, }); harden(defineVirtualExoClassKit); - // TODO interfaceGuard type https://github.com/Agoric/agoric-sdk/issues/6206 /** * @template {(...args: any) => any} I init state function * @template {Record} T methods * @param {DurableKindHandle} kindHandle - * @param {any} interfaceGuard + * @param {InterfaceGuard | undefined} interfaceGuard * @param {I} init * @param {T & ThisType<{ * self: T, @@ -168,12 +171,11 @@ export const makeExoUtils = VatData => { }); harden(defineDurableExoClass); - // TODO interfaceGuard type https://github.com/Agoric/agoric-sdk/issues/6206 /** * @template {(...args: any) => any} I init state function * @template {Record>} T facets * @param {DurableKindHandle} kindHandle - * @param {any} interfaceGuardKit + * @param {InterfaceGuardKit | undefined} interfaceGuardKit * @param {I} init * @param {T & ThisType<{ * facets: T, @@ -195,17 +197,16 @@ export const makeExoUtils = VatData => { defineDurableKindMulti(kindHandle, init, facets, { ...options, thisfulMethods: true, - interfaceGuard: interfaceGuardKit, + interfaceGuardKit, }); harden(defineDurableExoClassKit); - // TODO interfaceGuard type https://github.com/Agoric/agoric-sdk/issues/6206 /** * @template {(...args: any) => any} I init state function * @template {Record} T methods * @param {Baggage} baggage * @param {string} kindName - * @param {any} interfaceGuard + * @param {InterfaceGuard | undefined} interfaceGuard * @param {I} init * @param {T & ThisType<{ * self: T, @@ -234,13 +235,12 @@ export const makeExoUtils = VatData => { ); harden(prepareExoClass); - // TODO interfaceGuard type https://github.com/Agoric/agoric-sdk/issues/6206 /** * @template {(...args: any) => any} I init state function * @template {Record>} T facets * @param {Baggage} baggage * @param {string} kindName - * @param {any} interfaceGuardKit + * @param {InterfaceGuardKit | undefined} interfaceGuardKit * @param {I} init * @param {T & ThisType<{ * facets: T, @@ -269,12 +269,11 @@ export const makeExoUtils = VatData => { ); harden(prepareExoClassKit); - // TODO interfaceGuard type https://github.com/Agoric/agoric-sdk/issues/6206 /** * @template {Record} M methods * @param {Baggage} baggage * @param {string} kindName - * @param {any} interfaceGuard + * @param {InterfaceGuard | undefined} interfaceGuard * @param {M} methods * @param {DefineKindOptions<{ self: M }>} [options] * @returns {M & RemotableBrand<{}, M>} diff --git a/packages/zoe/src/contractFacet/types.js b/packages/zoe/src/contractFacet/types.js index a22b3366e1a..942536c21c7 100644 --- a/packages/zoe/src/contractFacet/types.js +++ b/packages/zoe/src/contractFacet/types.js @@ -2,6 +2,7 @@ /// +/** @template T @typedef {import('@endo/pass-style').CopyRecord} CopyRecord */ /** @typedef {import('@agoric/ertp').IssuerOptionsRecord} IssuerOptionsRecord */ // XXX can be tighter than 'any'