1 // ♓🌟 Piscēs ∷ function.js
2 // ====================================================================
4 // Copyright © 2022‐2023 Lady [@ Lady’s Computer].
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
10 import { ITERATOR
, toFunctionName
, toLength
, type
} from "./value.js";
14 * Creates a bound function from the provided function using the
15 * provided this value and arguments list.
17 * ☡ As with `call` and `construct`, the arguments must be passed as
23 * Returns a new function which calls the provided function with its
24 * first argument as the `this` value and the remaining arguments
27 * The `length`, `name`, and prototype of the provided function will
28 * be preserved in the new one. A second argument may be used to
29 * override `length` and `name`.
34 * Returns a new function which calls the provided function with its
35 * first argument as the `this` value and the remaining arguments
38 * The `length`, `name`, and prototype of the provided function will
39 * be preserved in the new one. A second argument may be used to
40 * override `length` and `name`.
42 * ※ This is effectively an alias for `Function::call.bind`.
44 createCallableFunction
,
47 * Returns a constructor which throws whenever it is called but has
48 * the same `.name` and `.prototype` as the provided value.
50 * The `length`, `name`, `prototype`, and prototype of the provided
51 * function will be preserved in the new one. A second argument may
52 * be used to override `length`, `name`, and `prototype`.
54 createIllegalConstructor
,
57 * Returns a constructor which produces a new constructor which wraps
58 * the provided constructor, but returns a proxy of the result using
59 * the provided handler.
61 * The resulting constructor inherits from, and has the same basic
64 * If a base constructor is not provided, `Object` will be used.
66 * If a third argument is provided, it is used as the target for the
67 * provided constructor when it is constructed. This can be used to
68 * prevent leakage of the provided constructor to superclasses
69 * through `new.target`.
71 * The `length` of the provided function will be preserved in the new
72 * one. A fourth argument may be used to override `length` and
75 * ※ `.prototype` will be present, but undefined, on the resulting
76 * constructor. This differs from the behaviour of `Proxy`, for which
77 * `.prototype` is not present at all. It is not presently possible
78 * to create a constructor with no `.prototype` property in
81 createProxyConstructor
,
83 const { prototype: functionPrototype
} = Function
;
87 } = functionPrototype
;
88 const objectConstructor
= Object
;
89 const proxyConstructor
= Proxy
;
92 defineProperty
: defineOwnProperty
,
93 defineProperties
: defineOwnProperties
,
94 getOwnPropertyDescriptor
,
95 getPrototypeOf
: getPrototype
,
96 setPrototypeOf
: setPrototype
,
100 construct
: reflectConstruct
,
102 const callBind
= reflectApply(functionBind
, functionCall
, [
105 const { revocable
} = Proxy
;
106 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
108 next
: arrayIteratorNext
,
109 } = getPrototype([][ITERATOR
]());
110 const argumentIterablePrototype
= {
118 call(arrayIterator
, this.args
, []),
123 const applyBaseFunction
= ($, base
, lengthDelta
= 0) => {
124 if (base
=== undefined) {
125 // No base function was provided to apply.
128 // A base function was provided; apply it.
129 const { length
, name
, prototype } = base
;
130 if (getPrototype($) === functionPrototype
) {
131 setPrototype($, getPrototype(base
));
135 return applyProperties($, {
136 length
: +length
+ lengthDelta
,
142 const applyProperties
= ($, override
) => {
143 if (override
=== undefined) {
144 // No properties were provided to apply.
147 // Properties were provided; apply them.
148 const { length
, name
, prototype } = override
;
150 !getOwnPropertyDescriptor($, "prototype")?.writable
||
151 prototype === undefined
153 // The provided function has no `.prototype`, its prototype is
154 // not writable, or no prototype value was provided.
156 // Do not modify the prototype property of the provided
160 // The provided function is a constructor and a prototype value
163 // Change the prototype property of the provided function to
165 defineOwnProperty($, "prototype", { value
: prototype });
167 return defineOwnProperties($, {
169 value
: toLength(length
=== undefined ? $.length
: length
),
172 value
: toFunctionName(
173 name
=== undefined ? $.name
?? "" : name
,
181 bind
: ($, boundThis
, boundArgs
) =>
186 argumentIterablePrototype
,
187 { args
: { value
: boundArgs
} },
190 createArrowFunction
: ($, propertyOverride
= undefined) =>
193 (...$s
) => reflectApply($, undefined, $s
),
198 createCallableFunction
: ($, propertyOverride
= undefined) =>
202 const iterator
= objectCreate(
203 argumentIterablePrototype
,
204 { args
: { value
: $s
} },
206 const { value
: thisValue
} = iterator
.next();
207 return reflectApply($, thisValue
, [...iterator
]);
214 createIllegalConstructor
: ($, propertyOverride
= undefined) =>
219 throw new TypeError("Illegal constructor");
228 createProxyConstructor
: (
231 newTarget
= undefined,
232 propertyOverride
= undefined,
234 const constructor = $ === undefined ? objectConstructor
: $;
235 const target
= newTarget
=== undefined ? constructor : newTarget
;
236 const len
= toLength(constructor.length
);
237 if (!(type(handler
) === "object")) {
239 `Piscēs: Proxy handler must be an object, but got: ${handler}.`,
241 } else if (!isConstructor(constructor)) {
243 "Piscēs: Cannot create proxy constructor from nonconstructible value.",
245 } else if (!isConstructor(target
)) {
247 "Piscēs: New target must be a constructor.",
250 return applyProperties(
254 if (new.target
=== undefined) {
258 } must be called with new.`,
261 const O
= reflectConstruct(
266 return new proxyConstructor(O
, handler
);
272 length
: { value
: len
},
275 toFunctionName(constructor.name ?? "")
287 value
: defineOwnProperties(
289 const O
= reflectConstruct(
294 return revocable(O
, handler
);
297 length
: { value
: len
},
298 name
: { value
: "revocable" },
313 * Calls the provided function with the provided this value and
316 * ☡ This is effectively an alias for `Reflect.apply`—the arguments
317 * must be passed as an arraylike.
319 export const call
= createArrowFunction(Reflect
.apply
, {
324 * Returns whether calling the provided function with no `this` value
325 * or arguments completes normally; that is, does not throw an error.
327 * ☡ This function will throw an error if the provided argument is not
330 export const completesNormally
= ($) => {
331 if (!isCallable($)) {
332 // The provided value is not callable; this is an error.
334 `Piscēs: Cannot determine completion of noncallable value: ${$}`,
337 // The provided value is callable.
339 // Attempt to call the function and return true if this succeeds.
343 // Calling the function did not succeed; return false.
350 * Constructs the provided function with the provided arguments list
353 * ☡ This is effectively an alias for `Reflect.construct`—the
354 * arguments must be passed as an arraylike.
356 export const construct
= createArrowFunction(Reflect
.construct
);
359 * Returns the provided value.
361 * ※ This function can be called as a constructor. When used in an
362 * `extends` clause and called via `super`, it will set the value of
363 * `this` to the provided value, enabling it to be extended with
364 * private class features.
366 export const identity = function ($) {
370 /** Returns whether the provided value is callable. */
371 export const isCallable
= ($) => typeof $ === "function";
373 /** Returns whether the provided value is a constructor. */
374 export const isConstructor
= ($) =>
375 completesNormally(() =>
376 // Try constructing a new object with the provided value as its
377 // `new.target`. This will throw if the provided value is not a
379 construct(function () {}, [], $)
383 * Returns whether the provided object inherits from the prototype of
384 * the provided function.
386 export const ordinaryHasInstance
= createCallableFunction(
387 Function
.prototype[Symbol
.hasInstance
],
388 { name
: "ordinaryHasInstance" },