From 648256880447bc1cf30be6eba77af96dee949a72 Mon Sep 17 00:00:00 2001 From: naugtur Date: Tue, 27 Aug 2024 09:48:25 +0200 Subject: [PATCH] feat(trampoline): apply minimal defensive coding to generator iterations in trampoline --- packages/trampoline/src/trampoline.js | 29 ++++++++++++++++++--------- 1 file changed, 19 insertions(+), 10 deletions(-) diff --git a/packages/trampoline/src/trampoline.js b/packages/trampoline/src/trampoline.js index 228e95312d..68afa2266d 100644 --- a/packages/trampoline/src/trampoline.js +++ b/packages/trampoline/src/trampoline.js @@ -2,6 +2,15 @@ /** * @import {AnyGenerator, SyncTrampolineGeneratorFn, AsyncTrampolineGeneratorFn } from './types.js' */ +const { getPrototypeOf } = Object; +const { bind } = Function.prototype; +const uncurryThis = bind.bind(bind.call); // eslint-disable-line @endo/no-polymorphic-call +export const { prototype: generatorPrototype } = getPrototypeOf( + // eslint-disable-next-line no-empty-function, func-names + function* () {}, +); +const generatorNext = uncurryThis(generatorPrototype.next); +const generatorThrow = uncurryThis(generatorPrototype.throw); /** * Trampoline on {@link TrampolineGeneratorFn generatorFn} synchronously. @@ -9,18 +18,18 @@ * @template {readonly any[]} [TArgs=unknown[]] Parameters for `generatorFn` * @template [TResult=unknown] Type of the return value of the `generatorFn` * @template {Generator} [TGenerator=Generator] Type of the generator function - * @param {SyncTrampolineGeneratorFn} generatorFn Generator-returning function accepting a thunk and optionally an initial value - * @param {TArgs} args Initial args + * @param {SyncTrampolineGeneratorFn} generatorFn Generator-returning function accepting any arguments + * @param {TArgs} args Arguments to pass to the generatorFn call * @returns {TResult} */ export function syncTrampoline(generatorFn, ...args) { const iterator = generatorFn(...args); - let result = iterator.next(); + let result = generatorNext(iterator); while (!result.done) { try { - result = iterator.next(result.value); + result = generatorNext(iterator, result.value); } catch (err) { - result = iterator.throw(err); + result = generatorThrow(iterator, err); } } return result.value; @@ -32,20 +41,20 @@ export function syncTrampoline(generatorFn, ...args) { * @template {readonly any[]} [TArgs=unknown[]] Parameters for `generatorFn` * @template [TResult=unknown] Type of the return value of the `generatorFn` * @template {Generator} [TGenerator=Generator] Type of the generator function - * @param {AsyncTrampolineGeneratorFn} generatorFn Generator-returning function accepting a thunk and optionally an initial value - * @param {TArgs} args Initial args + * @param {AsyncTrampolineGeneratorFn} generatorFn Generator-returning function accepting any arguments + * @param {TArgs} args Arguments to pass to the generatorFn call * @returns {Promise} */ export async function trampoline(generatorFn, ...args) { const iterator = generatorFn(...args); - let result = iterator.next(); + let result = generatorNext(iterator); while (!result.done) { try { // eslint-disable-next-line no-await-in-loop const val = await result.value; - result = iterator.next(val); + result = generatorNext(iterator, val); } catch (err) { - result = iterator.throw(err); + result = generatorThrow(iterator, err); } } return result.value;