From a154713f0be5df0f40c6373b9677f517fa8e44c9 Mon Sep 17 00:00:00 2001 From: "Mark S. Miller" Date: Mon, 11 Sep 2023 20:17:00 -0700 Subject: [PATCH] fix(exo)!: extra undeclared args dropped --- packages/exo/src/exo-tools.js | 26 +++++++++++++++++++++----- packages/exo/test/test-heap-classes.js | 13 +++++++++++++ 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/packages/exo/src/exo-tools.js b/packages/exo/src/exo-tools.js index f7a21aced6..d02f51fe06 100644 --- a/packages/exo/src/exo-tools.js +++ b/packages/exo/src/exo-tools.js @@ -32,6 +32,13 @@ const { defineProperties, fromEntries } = Object; */ const MinMethodGuard = M.call().rest(M.any()).returns(M.any()); +/** + * @param {Passable[]} args + * @param {MethodGuardPayload} methodGuardPayload + * @param {string | undefined} label + * @returns {Passable[]} Returns the args that should be passed to the + * raw method + */ const defendSyncArgs = (args, methodGuardPayload, label) => { const { argGuards, optionalArgGuards, restArgGuard } = methodGuardPayload; const paramsPattern = M.splitArray( @@ -40,6 +47,15 @@ const defendSyncArgs = (args, methodGuardPayload, label) => { restArgGuard, ); mustMatch(harden(args), paramsPattern, label); + if (restArgGuard !== undefined) { + return args; + } + const declaredLen = + argGuards.length + (optionalArgGuards ? optionalArgGuards.length : 0); + if (args.length <= declaredLen) { + return args; + } + return args.slice(0, declaredLen); }; /** @@ -53,8 +69,8 @@ const defendSyncMethod = (method, methodGuardPayload, label) => { const { syncMethod } = { // Note purposeful use of `this` and concise method syntax syncMethod(...args) { - defendSyncArgs(harden(args), methodGuardPayload, label); - const result = apply(method, this, args); + const realArgs = defendSyncArgs(harden(args), methodGuardPayload, label); + const result = apply(method, this, realArgs); mustMatch(harden(result), returnGuard, `${label}: result`); return result; }, @@ -83,9 +99,9 @@ const desync = methodGuardPayload => { return { awaitIndexes, rawMethodGuardPayload: { + ...methodGuardPayload, argGuards: rawArgGuards.slice(0, argGuards.length), optionalArgGuards: rawArgGuards.slice(argGuards.length), - restArgGuard, }, }; }; @@ -103,8 +119,8 @@ const defendAsyncMethod = (method, methodGuardPayload, label) => { for (let j = 0; j < awaitIndexes.length; j += 1) { rawArgs[awaitIndexes[j]] = awaitedArgs[j]; } - defendSyncArgs(rawArgs, rawMethodGuardPayload, label); - return apply(method, this, rawArgs); + const realArgs = defendSyncArgs(rawArgs, rawMethodGuardPayload, label); + return apply(method, this, realArgs); }); return E.when(resultP, result => { mustMatch(harden(result), returnGuard, `${label}: result`); diff --git a/packages/exo/test/test-heap-classes.js b/packages/exo/test/test-heap-classes.js index 636ce23d4e..73d33b14ac 100644 --- a/packages/exo/test/test-heap-classes.js +++ b/packages/exo/test/test-heap-classes.js @@ -10,6 +10,19 @@ import { } from '../src/exo-makers.js'; import { GET_INTERFACE_GUARD } from '../src/exo-tools.js'; +const NoExtraI = M.interface('NoExtra', { + foo: M.call().returns(), +}); + +test('what happens with extra arguments', t => { + const exo = makeExo('WithExtra', NoExtraI, { + foo(x) { + t.is(x, undefined); + }, + }); + exo.foo(8); +}); + const UpCounterI = M.interface('UpCounter', { incr: M.call() // TODO M.number() should not be needed to get a better error message