Skip to content

Commit

Permalink
fix: sync with shadows in agoric-sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed Apr 3, 2023
1 parent 35b7ea0 commit 19e2833
Show file tree
Hide file tree
Showing 4 changed files with 63 additions and 43 deletions.
82 changes: 51 additions & 31 deletions packages/exo/src/exo-makers.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,24 +26,44 @@ export const initEmpty = () => emptyRecord;
*/

/**
* @template A args to init
* @template S state from init
* @template {Record<string | symbol, CallableFunction>} T methods
* @template [S = any]
* @template [F = any]
* @typedef {object} KitContext
* @property {S} state
* @property {F} facets
*/

/**
* @typedef {{[name: string]: Pattern}} StateShape
* It looks like a copyRecord pattern, but the interpretation is different.
* Each property is distinct, is checked and changed separately.
*/

/**
* @template C
* @typedef {object} FarClassOptions
* @property {(context: C) => void} [finish]
* @property {StateShape} [stateShape]
*/

/**
* @template {(...args: any[]) => any} I init function
* @template {Record<string | symbol, CallableFunction>} M methods
* @param {string} tag
* @param {any} interfaceGuard
* @param {(...args: A[]) => S} init
* @param {T & ThisType<{ self: T, state: S }>} methods
* @param {object} [options]
* @returns {(...args: A[]) => (T & import('@endo/eventual-send').RemotableBrand<{}, T>)}
* @param {I} init
* @param {M & ThisType<{ self: M, state: ReturnType<I> }>} methods
* @param {FarClassOptions<Context<ReturnType<I>, M>>} [options]
* @returns {(...args: Parameters<I>) => (M & import('@endo/eventual-send').RemotableBrand<{}, M>)}
*/
export const defineExoClass = (
tag,
interfaceGuard,
init,
methods,
options = undefined,
{ finish = undefined } = {},
) => {
/** @type {WeakMap<T,Context<S, T>>} */
/** @type {WeakMap<M,Context<ReturnType<I>, M>>} */
const contextMap = new WeakMap();
const prototype = defendPrototype(
tag,
Expand All @@ -52,21 +72,21 @@ export const defineExoClass = (
true,
interfaceGuard,
);
/**
* @param {Parameters<I>} args
*/
const makeInstance = (...args) => {
// Be careful not to freeze the state record
const state = seal(init(...args));
/** @type {T} */
/** @type {M} */
// @ts-expect-error could be instantiated with different subtype
const self = harden({ __proto__: prototype });
// Be careful not to freeze the state record
/** @type {Context<S,T>} */
/** @type {Context<ReturnType<I>,M>} */
const context = freeze({ state, self });
contextMap.set(self, context);
if (options) {
const { finish = undefined } = options;
if (finish) {
finish(context);
}
if (finish) {
finish(context);
}
return self;
};
Expand All @@ -76,22 +96,21 @@ export const defineExoClass = (
harden(defineExoClass);

/**
* @template A args to init
* @template S state from init
* @template {Record<string, Record<string | symbol, CallableFunction>>} F methods
* @template {(...args: any[]) => any} I init function
* @template {Record<string, Record<string | symbol, CallableFunction>>} F facet methods
* @param {string} tag
* @param {any} interfaceGuardKit
* @param {(...args: A[]) => S} init
* @param {F & ThisType<{ facets: F, state: S }> } methodsKit
* @param {object} [options]
* @returns {(...args: A[]) => F}
* @param {I} init
* @param {F & ThisType<{ facets: F, state: ReturnType<I> }> } methodsKit
* @param {FarClassOptions<KitContext<ReturnType<I>,F>>} [options]
* @returns {(...args: Parameters<I>) => F}
*/
export const defineExoClassKit = (
tag,
interfaceGuardKit,
init,
methodsKit,
options = undefined,
{ finish = undefined } = {},
) => {
const contextMapKit = objectMap(methodsKit, () => new WeakMap());
const getContextKit = objectMap(
Expand All @@ -105,6 +124,9 @@ export const defineExoClassKit = (
true,
interfaceGuardKit,
);
/**
* @param {Parameters<I>} args
*/
const makeInstanceKit = (...args) => {
// Be careful not to freeze the state record
const state = seal(init(...args));
Expand All @@ -118,11 +140,9 @@ export const defineExoClassKit = (
context.facets = facets;
// Be careful not to freeze the state record
freeze(context);
if (options) {
const { finish = undefined } = options;
if (finish) {
finish(context);
}
if (finish) {
// @ts-expect-error `facets` was added
finish(context);
}
return facets;
};
Expand All @@ -131,11 +151,11 @@ export const defineExoClassKit = (
harden(defineExoClassKit);

/**
* @template {Record<string, Method>} T
* @template {Record<string | symbol, CallableFunction>} T
* @param {string} tag
* @param {InterfaceGuard | undefined} interfaceGuard CAVEAT: static typing does not yet support `callWhen` transformation
* @param {T} methods
* @param {object} [options]
* @param {FarClassOptions<Context<{},T>>} [options]
* @returns {T & import('@endo/eventual-send').RemotableBrand<{}, T>}
*/
export const makeExo = (tag, interfaceGuard, methods, options = undefined) => {
Expand Down
1 change: 1 addition & 0 deletions packages/exo/src/exo-tools.js
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,7 @@ export const defendPrototype = (
methodGuards && methodGuards[prop],
);
}

return Far(tag, /** @type {T} */ (prototype));
};
harden(defendPrototype);
Expand Down
8 changes: 0 additions & 8 deletions packages/patterns/src/patterns/patternMatchers.js
Original file line number Diff line number Diff line change
Expand Up @@ -1696,12 +1696,4 @@ export const {
M,
} = makePatternKit();

/**
* A method guard, for inclusion in an interface guard, that enforces only that
* all arguments are passable and that the result is passable. (In far classes,
* "any" means any *passable*.) This is the least possible enforcement for a
* method guard, and is implied by all other method guards.
*/
export const MinMethodGuard = M.call().rest(M.any()).returns(M.any());

MM = M;
15 changes: 11 additions & 4 deletions packages/patterns/src/types.js
Original file line number Diff line number Diff line change
Expand Up @@ -493,10 +493,17 @@

/**
* @typedef {any} MethodGuardMaker
* a parameter list like foo(a, b, c = d, …e) => f should be guarded by
* something like
* foo: M.call(AShape, BShape).optional(CShape).rest(EShape).returns(FShape)
* optional is for optional (=) params. rest is for … (varargs) params
* A method name and parameter/return signature like:
* ```js
* foo(a, b, c = d, ...e) => f
* ```
* should be guarded by something like:
* ```js
* {
* ...otherMethodGuards,
* foo: M.call(AShape, BShape).optional(CShape).rest(EShape).returns(FShape),
* }
* ```
*/

/** @typedef {{ klass: 'methodGuard', callKind: 'sync' | 'async', returnGuard: unknown }} MethodGuard */
Expand Down

0 comments on commit 19e2833

Please sign in to comment.