+export const {
+ /**
+ * Creates a bound function from the provided function using the
+ * provided this value and arguments list.
+ *
+ * ☡ As with `call` and `construct`, the arguments must be passed as
+ * an array.
+ */
+ bind,
+
+ /**
+ * Returns a new function which calls the provided function with its
+ * first argument as the `this` value and the remaining arguments
+ * passed through.
+ *
+ * The `length`, `name`, and prototype of the provided function will
+ * be preserved in the new one. A second argument may be used to
+ * override `length` and `name`.
+ */
+ createArrowFunction,
+
+ /**
+ * Returns a new function which calls the provided function with its
+ * first argument as the `this` value and the remaining arguments
+ * passed through.
+ *
+ * The `length`, `name`, and prototype of the provided function will
+ * be preserved in the new one. A second argument may be used to
+ * override `length` and `name`.
+ *
+ * ※ This is effectively an alias for `Function::call.bind`.
+ */
+ createCallableFunction,
+
+ /**
+ * Returns a constructor which throws whenever it is called but has
+ * the same `.name` and `.prototype` as the provided value.
+ *
+ * The `length`, `name`, `prototype`, and prototype of the provided
+ * function will be preserved in the new one. A second argument may
+ * be used to override `length`, `name`, and `prototype`.
+ */
+ createIllegalConstructor,
+
+ /**
+ * Returns a constructor which produces a new constructor which wraps
+ * the provided constructor, but returns a proxy of the result using
+ * the provided handler.
+ *
+ * The resulting constructor inherits from, and has the same basic
+ * shape as, `Proxy`.
+ *
+ * If a base constructor is not provided, `Object` will be used.
+ *
+ * If a third argument is provided, it is used as the target for the
+ * provided constructor when it is constructed. This can be used to
+ * prevent leakage of the provided constructor to superclasses
+ * through `new.target`.
+ *
+ * The `length` of the provided function will be preserved in the new
+ * one. A fourth argument may be used to override `length` and
+ * `name`.
+ *
+ * ※ `.prototype` will be present, but undefined, on the resulting
+ * constructor. This differs from the behaviour of `Proxy`, for which
+ * `.prototype` is not present at all. It is not presently possible
+ * to create a constructor with no `.prototype` property in
+ * Ecmascript code.
+ */
+ createProxyConstructor,
+} = (() => {
+ const { prototype: functionPrototype } = Function;
+ const {
+ bind: functionBind,
+ call: functionCall,
+ } = functionPrototype;
+ const objectConstructor = Object;
+ const proxyConstructor = Proxy;
+ const {
+ apply: reflectApply,
+ construct: reflectConstruct,
+ } = Reflect;
+ const callBind = reflectApply(functionBind, functionCall, [
+ functionBind,
+ ]);
+ const { revocable } = Proxy;
+ const { [ITERATOR]: arrayIterator } = Array.prototype;
+ const {
+ next: arrayIteratorNext,
+ } = getPrototype([][ITERATOR]());
+ const argumentIterablePrototype = {
+ [ITERATOR]() {
+ return {
+ [ITERATOR]() {
+ return this;
+ },
+ next: callBind(
+ arrayIteratorNext,
+ call(arrayIterator, this.args, []),
+ ),
+ };
+ },
+ };
+ 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.
+ return $;
+ } else {
+ // A base function was provided; apply it.
+ const { length, name, prototype } = base;
+ if (getPrototype($) === functionPrototype) {
+ setPrototype($, getPrototype(base));
+ } else {
+ /* do nothing */
+ }
+ return applyProperties($, {
+ length: +length + lengthDelta,
+ name,
+ prototype,
+ });
+ }
+ };
+ const applyProperties = ($, override) => {
+ if (override === UNDEFINED) {
+ // No properties were provided to apply.
+ return $;
+ } else {
+ // Properties were provided; apply them.
+ const { length, name, prototype } = override;
+ if (
+ prototype === UNDEFINED ||
+ !getOwnPropertyDescriptor($, "prototype")?.writable
+ ) {
+ // The provided function has no `.prototype`, its prototype is
+ // not writable, or no prototype value was provided.
+ //
+ // Do not modify the prototype property of the provided
+ // function.
+ /* do nothing */
+ } else {
+ // The provided function is a constructor and a prototype value
+ // was provided.
+ //
+ // Change the prototype property of the provided function to
+ // match.
+ defineOwnProperty(
+ $,
+ "prototype",
+ defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ prototype,
+ ),
+ );
+ }
+ return defineOwnProperties($, {
+ length: defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ toLength(length === UNDEFINED ? $.length : length),
+ ),
+ name: defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ toFunctionName(name === UNDEFINED ? $.name ?? "" : name),
+ ),
+ });
+ }
+ };
+
+ return {
+ bind: ($, boundThis, boundArgs) =>
+ callBind(
+ $,
+ boundThis,
+ ...defineOwnDataProperty(
+ objectCreate(argumentIterablePrototype),
+ "args",
+ boundArgs,
+ ),
+ ),
+ createArrowFunction: ($, propertyOverride = UNDEFINED) =>
+ applyProperties(
+ applyBaseFunction(
+ (...$s) => reflectApply($, UNDEFINED, $s),
+ $,
+ ),
+ propertyOverride,
+ ),
+ createCallableFunction: ($, propertyOverride = UNDEFINED) =>
+ applyProperties(
+ applyBaseFunction(
+ (...$s) => {
+ const iterator = defineOwnDataProperty(
+ objectCreate(argumentIterablePrototype),
+ "args",
+ $s,
+ )[ITERATOR]();
+ const { value: thisValue } = iterator.next();
+ return reflectApply($, thisValue, [...iterator]);
+ },
+ $,
+ 1,
+ ),
+ propertyOverride,
+ ),
+ createIllegalConstructor: ($, propertyOverride = UNDEFINED) =>
+ defineOwnProperty(
+ applyProperties(
+ applyBaseFunction(
+ function () {
+ throw new TypeError("Illegal constructor");
+ },
+ $,
+ ),
+ propertyOverride,
+ ),
+ "prototype",
+ { writable: false },
+ ),
+ createProxyConstructor: (
+ handler,
+ $,
+ newTarget = UNDEFINED,
+ propertyOverride = UNDEFINED,
+ ) => {
+ const constructor = $ === UNDEFINED
+ ? function ($) {
+ return new objectConstructor($);
+ }
+ : $;
+ const target = newTarget === UNDEFINED ? constructor : newTarget;
+ const len = toLength(constructor.length);
+ if (!(type(handler) === "object")) {
+ // The provided handler is not an object; this is an error.
+ throw new TypeError(
+ `Piscēs: Proxy handler must be an object, but got: ${handler}.`,
+ );
+ } else if (!isConstructor(constructor)) {
+ // The provided constructor is not a constructor; this is an
+ // error.
+ throw new TypeError(
+ "Piscēs: Cannot create proxy constructor from nonconstructible value.",
+ );
+ } else if (!isConstructor(target)) {
+ // The provided new target is not a constructor; this is an
+ // error.
+ throw new TypeError(
+ "Piscēs: New target must be a constructor.",
+ );
+ } else {
+ // The arguments are acceptable.
+ const C = applyProperties(
+ defineOwnProperties(
+ setPrototype(
+ function (...$s) {
+ if (new.target === UNDEFINED) {
+ // The constructor was not called with new; this is an
+ // error.
+ throw new TypeError(
+ `Piscēs: ${
+ C.name ?? "Proxy"
+ } must be called with new.`,
+ );
+ } else {
+ // The constructor was called with new; return the
+ // appropriate proxy.
+ const O = reflectConstruct(
+ constructor,
+ $s,
+ target,
+ );
+ const proxy = new proxyConstructor(O, handler);
+ return registerConstructedProxy(C, proxy);
+ }
+ },
+ proxyConstructor,
+ ),
+ {
+ length: defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ len,
+ ),
+ name: defineOwnDataProperty(
+ objectCreate(null),
+ "value",
+ `${toFunctionName(constructor.name ?? "")}Proxy`,
+ ),
+ prototype: setPropertyValues(objectCreate(null), {
+ configurable: false,
+ enumerable: false,
+ value: UNDEFINED,
+ writable: false,
+ }),
+ },
+ ),
+ 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,
+ }),
+ });
+ }
+ },
+ };