X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/cf737f7ce78fdb1c6f13f8b60d53bf0900924b19..HEAD:/function.js diff --git a/function.js b/function.js index 71e4d09..2f242fe 100644 --- a/function.js +++ b/function.js @@ -128,6 +128,29 @@ export const { }; }, }; + const { get: wmGet, set: wmSet } = WeakMap.prototype; + const wsConstructor = WeakSet; + const { add: wsAdd, has: wsHas } = WeakSet.prototype; + const proxyConstructorValuesMap = new WeakMap(); + const registerConstructedProxy = (constructor, proxy) => { + const values = (() => { + const existing = reflectApply(wmGet, proxyConstructorValuesMap, [ + constructor, + ]); + if (existing) { + return existing; + } else { + const result = new wsConstructor(); + reflectApply(wmSet, proxyConstructorValuesMap, [ + constructor, + result, + ]); + return result; + } + })(); + reflectApply(wsAdd, values, [proxy]); + return proxy; + }; const applyBaseFunction = ($, base, lengthDelta = 0) => { if (base === UNDEFINED) { // No base function was provided to apply. @@ -251,7 +274,11 @@ export const { newTarget = UNDEFINED, propertyOverride = UNDEFINED, ) => { - const constructor = $ === UNDEFINED ? objectConstructor : $; + const constructor = $ === UNDEFINED + ? function ($) { + return new objectConstructor($); + } + : $; const target = newTarget === UNDEFINED ? constructor : newTarget; const len = toLength(constructor.length); if (!(type(handler) === "object")) { @@ -273,13 +300,13 @@ export const { ); } else { // The arguments are acceptable. - return applyProperties( + const C = applyProperties( defineOwnProperties( setPrototype( - function C(...$s) { + function (...$s) { if (new.target === UNDEFINED) { - // The constructor was not called with new; this is - // an error. + // The constructor was not called with new; this is an + // error. throw new TypeError( `Piscēs: ${ C.name ?? "Proxy" @@ -293,7 +320,8 @@ export const { $s, target, ); - return new proxyConstructor(O, handler); + const proxy = new proxyConstructor(O, handler); + return registerConstructedProxy(C, proxy); } }, proxyConstructor, @@ -315,37 +343,71 @@ export const { value: UNDEFINED, writable: false, }), - revocable: setPropertyValues(objectCreate(null), { - configurable: true, - enumerable: false, - value: defineOwnProperties( - (...$s) => { - const O = reflectConstruct( - constructor, - $s, - target, - ); - return revocable(O, handler); - }, - { - length: defineOwnDataProperty( - objectCreate(null), - "value", - len, - ), - name: defineOwnDataProperty( - objectCreate(null), - "value", - "revocable", - ), - }, - ), - writable: true, - }), }, ), propertyOverride, ); + const { name } = C; + return defineOwnProperties(C, { + revocable: setPropertyValues(objectCreate(null), { + configurable: true, + enumerable: false, + value: defineOwnProperties( + (...$s) => { + const O = reflectConstruct( + constructor, + $s, + target, + ); + const proxy = revocable(O, handler); + return registerConstructedProxy(C, proxy); + }, + { + length: defineOwnDataProperty( + objectCreate(null), + "value", + len, + ), + name: defineOwnDataProperty( + objectCreate(null), + "value", + "revocable", + ), + }, + ), + writable: true, + }), + [`is${name}`]: setPropertyValues(objectCreate(null), { + configurable: true, + enumerable: false, + value: defineOwnProperty( + ($) => { + const values = reflectApply( + wmGet, + proxyConstructorValuesMap, + [C], + ); + if (values === UNDEFINED) { + // No values have been registered for the current + // constructor. + return false; + } else { + // One or more values has been registered for the + // current constructor; return whether the provided + // argument is one. + return reflectApply(wsHas, values, [$]); + } + }, + "name", + defineOwnDataProperty( + objectCreate(null), + "value", + `is${name}`, + ), + ), + writable: true, + }), + }); } }, }; @@ -421,6 +483,13 @@ export const isConstructor = ($) => construct(function () {}, [], $) ); +/** + * Calls the provided callback with the provided argument if the + * provided argument is not nullish; otherwise, returns the provided + * argument unmodified. + */ +export const maybe = ($, callback) => $ == null ? $ : callback($); + /** * Returns whether the provided object inherits from the prototype of * the provided function.