Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Inconsistency between Remotable rules and InterfaceGuard pattern language #1728

Closed
erights opened this issue Aug 22, 2023 · 1 comment · Fixed by #1740
Closed

Inconsistency between Remotable rules and InterfaceGuard pattern language #1728

erights opened this issue Aug 22, 2023 · 1 comment · Fixed by #1740
Assignees
Labels
bug Something isn't working

Comments

@erights
Copy link
Contributor

erights commented Aug 22, 2023

(Originally reported at Agoric/agoric-sdk#8231 )

https://github.com/Agoric/agoric-sdk/blob/61b6c39a7c8cedf7c7b11a5f5352d82f351e5657/packages/notifier/src/publish-kit.js#L48-L53

 export const IterableEachTopicI = M.interface('IterableEachTopic', { 
   subscribeAfter: SubscriberI.methodGuards.subscribeAfter, 
   [Symbol.asyncIterator]: M.call().returns( 
     M.remotable('ForkableAsyncIterableIterator'), 
   ), 
 }); 

Remotables are allowed to have symbol-named methods. (The particular case that is already important for us is Symbol.asyncIterator as we see here.)

InterfaceGuards are supposed to be Passable descriptions of the API of an exo, including the guards and methods used do the first level of input validation of the exo's methods. The InterfaceGuard uses a CopyRecord of method guards, where each property name of that CopyRecord is supposed to be the name of the method being described/guarded.

However, a CopyRecord can only have string-named properties. Therefore, there is currently no way for an InterfaceGuard to provide a MethodGuard for describing/guarding a symbol-named method.

@gibson042
Copy link
Contributor

Anticipated fix foundation: update makeInterfaceGuard per Agoric/agoric-sdk#8232 (review)

 export const assertInterfaceGuard = specimen => {
   mustMatch(specimen, InterfaceGuardShape, 'interfaceGuard');
 };
 harden(assertInterfaceGuard);
+
+const { comparator: passableComparator } = makeComparatorKit();
 
 /**
  * @param {string} interfaceName
  * @param {Record<string, MethodGuard>} methodGuards
  * @param {{sloppy?: boolean}} [options]
  * @returns {InterfaceGuard}
  */
 const makeInterfaceGuard = (interfaceName, methodGuards, options = {}) => {
   const { sloppy = false } = options;
+  const stringMethodGuards = {};
+  const symbolMethodGuardsEntries = [];
+  for (const key of ownKeys(methodGuards)) {
+    const value = methodGuards[key];
+    if (typeof key === 'symbol') {
+      symbolMethodGuardsEntries.push([key, value]);
+    } else {
+      stringMethodGuards[key] = value;
+    }
+  }
+  // Ensure a consistent order.
+  symbolMethodGuardsEntries.sort(([k1], [k2]) => passableComparator(k1, k2));
   /** @type {InterfaceGuard} */
   const result = harden({
     klass: 'Interface',
     interfaceName,
-    methodGuards,
+    methodGuards: stringMethodGuards,
+    symbolMethodGuards: symbolMethodGuardsEntries,
     sloppy,
   });
   assertInterfaceGuard(result);
   return result;
 };

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

Successfully merging a pull request may close this issue.

2 participants