From 0e527f5fbea87956a91e88a5faac4aec12f94cf7 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Sat, 27 Apr 2024 17:26:08 -0700 Subject: [PATCH] refactor(exo): patch exo to track endo #2247 --- patches/@endo+exo+1.4.0.patch | 276 ++++++++++++++++++++++++++++++++++ 1 file changed, 276 insertions(+) diff --git a/patches/@endo+exo+1.4.0.patch b/patches/@endo+exo+1.4.0.patch index aad2781fa3b..91d08875538 100644 --- a/patches/@endo+exo+1.4.0.patch +++ b/patches/@endo+exo+1.4.0.patch @@ -8,3 +8,279 @@ index 3bc696d..55df34e 100644 +export * from "./src/types.js"; //# sourceMappingURL=index.d.ts.map \ No newline at end of file +diff --git a/node_modules/@endo/exo/src/exo-tools.js b/node_modules/@endo/exo/src/exo-tools.js +index e091adc..20bf28d 100644 +--- a/node_modules/@endo/exo/src/exo-tools.js ++++ b/node_modules/@endo/exo/src/exo-tools.js +@@ -18,7 +18,7 @@ import { GET_INTERFACE_GUARD } from './get-interface.js'; + + /** + * @import {InterfaceGuard, Method, MethodGuard, MethodGuardPayload} from '@endo/patterns' +- * @import {ContextProvider, FacetName, KitContextProvider, MatchConfig, Methods} from './types.js' ++ * @import {ClassContext, ContextProvider, FacetName, KitContext, KitContextProvider, MatchConfig, Methods} from './types.js' + */ + + const { apply, ownKeys } = Reflect; +@@ -146,21 +146,28 @@ const buildMatchConfig = methodGuardPayload => { + }; + + /** +- * @param {Method} method ++ * @param {(representative: any) => ClassContext | KitContext} getContext ++ * @param {CallableFunction} behaviorMethod + * @param {MethodGuardPayload} methodGuardPayload + * @param {string} label + * @returns {Method} + */ +-const defendSyncMethod = (method, methodGuardPayload, label) => { ++const defendSyncMethod = ( ++ getContext, ++ behaviorMethod, ++ methodGuardPayload, ++ label, ++) => { + const { returnGuard } = methodGuardPayload; + const isRawReturn = isRawGuard(returnGuard); + const matchConfig = buildMatchConfig(methodGuardPayload); + const { syncMethod } = { + // Note purposeful use of `this` and concise method syntax + syncMethod(...syncArgs) { ++ const context = getContext(this); + // Only harden args and return value if not dealing with a raw value guard. + const realArgs = defendSyncArgs(syncArgs, matchConfig, label); +- const result = apply(method, this, realArgs); ++ const result = apply(behaviorMethod, context, realArgs); + if (!isRawReturn) { + mustMatch(harden(result), returnGuard, `${label}: result`); + } +@@ -202,11 +209,17 @@ const desync = methodGuardPayload => { + }; + + /** +- * @param {(...args: unknown[]) => any} method ++ * @param {(representative: any) => ClassContext | KitContext} getContext ++ * @param {CallableFunction} behaviorMethod + * @param {MethodGuardPayload} methodGuardPayload + * @param {string} label + */ +-const defendAsyncMethod = (method, methodGuardPayload, label) => { ++const defendAsyncMethod = ( ++ getContext, ++ behaviorMethod, ++ methodGuardPayload, ++ label, ++) => { + const { returnGuard } = methodGuardPayload; + const isRawReturn = isRawGuard(returnGuard); + +@@ -231,8 +244,11 @@ const defendAsyncMethod = (method, methodGuardPayload, label) => { + for (let j = 0; j < awaitedArgs.length; j += 1) { + syncArgs[awaitIndexes[j]] = awaitedArgs[j]; + } ++ // Get the context after all waiting in case we ever do revocation ++ // by removing the context entry. Avoid TOCTTOU! ++ const context = getContext(this); + const realArgs = defendSyncArgs(syncArgs, matchConfig, label); +- return apply(method, this, realArgs); ++ return apply(behaviorMethod, context, realArgs); + }, + ); + if (isRawReturn) { +@@ -249,18 +265,29 @@ const defendAsyncMethod = (method, methodGuardPayload, label) => { + + /** + * +- * @param {Method} method ++ * @param {(representative: any) => ClassContext | KitContext} getContext ++ * @param {CallableFunction} behaviorMethod + * @param {MethodGuard} methodGuard + * @param {string} label + */ +-const defendMethod = (method, methodGuard, label) => { ++const defendMethod = (getContext, behaviorMethod, methodGuard, label) => { + const methodGuardPayload = getMethodGuardPayload(methodGuard); + const { callKind } = methodGuardPayload; + if (callKind === 'sync') { +- return defendSyncMethod(method, methodGuardPayload, label); ++ return defendSyncMethod( ++ getContext, ++ behaviorMethod, ++ methodGuardPayload, ++ label, ++ ); + } else { + assert(callKind === 'async'); +- return defendAsyncMethod(method, methodGuardPayload, label); ++ return defendAsyncMethod( ++ getContext, ++ behaviorMethod, ++ methodGuardPayload, ++ label, ++ ); + } + }; + +@@ -268,74 +295,43 @@ const defendMethod = (method, methodGuard, label) => { + * @param {string} methodTag + * @param {ContextProvider} contextProvider + * @param {CallableFunction} behaviorMethod +- * @param {boolean} [thisfulMethods] +- * @param {MethodGuard} [methodGuard] +- * @param {import('@endo/patterns').DefaultGuardType} [defaultGuards] ++ * @param {MethodGuard} methodGuard + */ + const bindMethod = ( + methodTag, + contextProvider, + behaviorMethod, +- thisfulMethods = false, +- methodGuard = undefined, +- defaultGuards = undefined, ++ methodGuard, + ) => { + assert.typeof(behaviorMethod, 'function'); + +- const getContext = self => { +- const context = contextProvider(self); +- context || +- Fail`${q(methodTag)} may only be applied to a valid instance: ${self}`; ++ /** ++ * @param {any} representative ++ * @returns {ClassContext | KitContext} ++ */ ++ const getContext = representative => { ++ representative || ++ // separate line to ease breakpointing ++ Fail`Method ${methodTag} called without 'this' object`; ++ const context = contextProvider(representative); ++ if (context === undefined) { ++ throw Fail`${q( ++ methodTag, ++ )} may only be applied to a valid instance: ${representative}`; ++ } + return context; + }; + +- // Violating all Jessie rules to create representatives that inherit +- // methods from a shared prototype. The bound method therefore needs +- // to mention `this`. We define it using concise method syntax +- // so that it will be `this` sensitive but not constructable. +- // +- // We normally consider `this` unsafe because of the hazard of a +- // method of one abstraction being applied to an instance of +- // another abstraction. To prevent that attack, the bound method +- // checks that its `this` is in the map in which its representatives +- // are registered. +- let { method } = thisfulMethods +- ? { +- method(...args) { +- this || +- Fail`thisful method ${methodTag} called without 'this' object`; +- const context = getContext(this); +- return apply(behaviorMethod, context, args); +- }, +- } +- : { +- method(...args) { +- const context = getContext(this); +- return apply(behaviorMethod, null, [context, ...args]); +- }, +- }; +- if (!methodGuard && thisfulMethods) { +- switch (defaultGuards) { +- case undefined: +- case 'passable': +- methodGuard = PassableMethodGuard; +- break; +- case 'raw': +- methodGuard = RawMethodGuard; +- break; +- default: +- throw Fail`Unrecognized defaultGuards ${q(defaultGuards)}`; +- } +- } +- if (methodGuard) { +- method = defendMethod(method, methodGuard, methodTag); +- } ++ const method = defendMethod( ++ getContext, ++ behaviorMethod, ++ methodGuard, ++ methodTag, ++ ); + + defineProperties(method, { + name: { value: methodTag }, +- length: { +- value: thisfulMethods ? behaviorMethod.length : behaviorMethod.length - 1, +- }, ++ length: { value: behaviorMethod.length }, + }); + return method; + }; +@@ -402,14 +398,43 @@ export const defendPrototype = ( + } + + for (const prop of methodNames) { ++ const originalMethod = behaviorMethods[prop]; ++ const { shiftedMethod } = { ++ shiftedMethod(...args) { ++ return originalMethod(this, ...args); ++ }, ++ }; ++ const behaviorMethod = thisfulMethods ? originalMethod : shiftedMethod; ++ // TODO some tool does not yet understand the `?.[` syntax ++ let methodGuard = methodGuards && methodGuards[prop]; ++ if (!methodGuard) { ++ switch (defaultGuards) { ++ case undefined: { ++ if (thisfulMethods) { ++ methodGuard = PassableMethodGuard; ++ } else { ++ methodGuard = RawMethodGuard; ++ } ++ break; ++ } ++ case 'passable': { ++ methodGuard = PassableMethodGuard; ++ break; ++ } ++ case 'raw': { ++ methodGuard = RawMethodGuard; ++ break; ++ } ++ default: { ++ throw Fail`Unrecognized defaultGuards ${q(defaultGuards)}`; ++ } ++ } ++ } + prototype[prop] = bindMethod( + `In ${q(prop)} method of (${tag})`, + contextProvider, +- behaviorMethods[prop], +- thisfulMethods, +- // TODO some tool does not yet understand the `?.[` syntax +- methodGuards && methodGuards[prop], +- defaultGuards, ++ behaviorMethod, ++ methodGuard, + ); + } + +@@ -424,14 +449,13 @@ export const defendPrototype = ( + `In ${q(GET_INTERFACE_GUARD)} method of (${tag})`, + contextProvider, + getInterfaceGuardMethod, +- thisfulMethods, +- undefined, ++ PassableMethodGuard, + ); + } + + return Far( + tag, +- /** @type {T & import('./get-interface.js').GetInterfaceGuard} */ ( ++ /** @type {T & import('./get-interface.js').GetInterfaceGuard} */( + prototype + ), + );