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
} 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
,
56 const { prototype: functionPrototype
} = Function
;
60 } = functionPrototype
;
61 const { apply
: reflectApply
} = Reflect
;
64 defineProperty
: defineOwnProperty
,
65 defineProperties
: defineOwnProperties
,
66 hasOwn
: hasOwnProperty
,
67 getPrototypeOf
: getPrototype
,
68 setPrototypeOf
: setPrototype
,
70 const callBind
= reflectApply(functionBind
, functionCall
, [
73 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
75 next
: arrayIteratorNext
,
76 } = getPrototype([][ITERATOR
]());
77 const argumentIterablePrototype
= {
85 call(arrayIterator
, this.args
, []),
90 const applyBaseFunction
= ($, base
, lengthDelta
= 0) => {
91 if (base
=== undefined) {
92 // No base function was provided to apply.
95 // A base function was provided; apply it.
96 const { length
, name
, prototype } = base
;
97 if (getPrototype($) === functionPrototype
) {
98 setPrototype($, getPrototype(base
));
102 return applyProperties($, {
103 length
: +length
+ lengthDelta
,
109 const applyProperties
= ($, override
) => {
110 if (override
=== undefined) {
111 // No properties were provided to apply.
114 // Properties were provided; apply them.
115 const { length
, name
, prototype } = override
;
116 if (!hasOwnProperty($, "prototype") || prototype === undefined) {
117 // The provided function has no `.prototype` or no prototype
118 // value was provided.
120 // Do not modify the prototype property of the provided
124 // The provided function is a constructor and a prototype value
127 // Change the prototype property of the provided function to
129 defineOwnProperty($, "prototype", { value
: prototype });
131 return defineOwnProperties($, {
133 value
: toLength(length
=== undefined ? $.length
: length
),
136 value
: toFunctionName(
137 name
=== undefined ? $.name
?? "" : name
,
145 bind
: ($, boundThis
, boundArgs
) =>
150 argumentIterablePrototype
,
151 { args
: { value
: boundArgs
} },
154 createArrowFunction
: ($, propertyOverride
= undefined) =>
157 (...$s
) => reflectApply($, undefined, $s
),
162 createCallableFunction
: ($, propertyOverride
= undefined) =>
166 const iterator
= objectCreate(
167 argumentIterablePrototype
,
168 { args
: { value
: $s
} },
170 const { value
: thisValue
} = iterator
.next();
171 return reflectApply($, thisValue
, [...iterator
]);
178 createIllegalConstructor
: ($, propertyOverride
= undefined) =>
183 throw new TypeError("Illegal constructor");
196 * Calls the provided function with the provided this value and
199 * ☡ This is effectively an alias for `Reflect.apply`—the arguments
200 * must be passed as an arraylike.
202 export const call
= createArrowFunction(Reflect
.apply
, {
207 * Returns whether calling the provided function with no `this` value
208 * or arguments completes normally; that is, does not throw an error.
210 * ☡ This function will throw an error if the provided argument is not
213 export const completesNormally
= ($) => {
214 if (!isCallable($)) {
215 // The provided value is not callable; this is an error.
217 `Piscēs: Cannot determine completion of noncallable value: ${$}`,
220 // The provided value is callable.
222 // Attempt to call the function and return true if this succeeds.
226 // Calling the function did not succeed; return false.
233 * Constructs the provided function with the provided arguments list
236 * ☡ This is effectively an alias for `Reflect.construct`—the
237 * arguments must be passed as an arraylike.
239 export const construct
= createArrowFunction(Reflect
.construct
);
242 * Returns the provided value.
244 * ※ This function can be called as a constructor. When used in an
245 * `extends` clause and called via `super`, it will set the value of
246 * `this` to the provided value, enabling it to be extended with
247 * private class features.
249 export const identity = function ($) {
253 /** Returns whether the provided value is callable. */
254 export const isCallable
= ($) => typeof $ === "function";
256 /** Returns whether the provided value is a constructor. */
257 export const isConstructor
= ($) =>
258 completesNormally(() =>
259 // Try constructing a new object with the provided value as its
260 // `new.target`. This will throw if the provided value is not a
262 construct(function () {}, [], $)
266 * Returns whether the provided object inherits from the prototype of
267 * the provided function.
269 export const ordinaryHasInstance
= createCallableFunction(
270 Function
.prototype[Symbol
.hasInstance
],
271 { name
: "ordinaryHasInstance" },