Skip to content

Commit

Permalink
refactor(exo): patch exo to track endo #2247
Browse files Browse the repository at this point in the history
  • Loading branch information
erights committed May 6, 2024
1 parent 24f7f32 commit fa9766a
Showing 1 changed file with 276 additions and 0 deletions.
276 changes: 276 additions & 0 deletions patches/@endo+exo+1.4.0.patch
Original file line number Diff line number Diff line change
Expand Up @@ -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<T>} */ (
+ /** @type {T & import('./get-interface.js').GetInterfaceGuard<T>} */(
prototype
),
);

0 comments on commit fa9766a

Please sign in to comment.