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
, toLength
, toPrimitive
} 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 string function name generated from the provided value
58 * and optional prefix.
62 // ☡ Because these functions are used to initialize module constants,
63 // they can’t depend on imports from elsewhere.
67 } = Function
.prototype;
68 const callBind
= Reflect
.apply(functionBind
, functionCall
, [
73 defineProperty
: defineOwnProperty
,
74 defineProperties
: defineOwnProperties
,
75 getOwnPropertyDescriptor
,
76 getPrototypeOf
: getPrototype
,
77 setPrototypeOf
: setPrototype
,
79 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
81 next
: arrayIteratorNext
,
82 } = getPrototype([][ITERATOR
]());
83 const argumentIterablePrototype
= {
91 call(arrayIterator
, this.args
, []),
96 const getSymbolDescription
= getOwnPropertyDescriptor(
100 const applyBaseFunction
= ($, base
, lengthDelta
= 0) => {
101 if (base
=== undefined) {
102 // No base function was provided to apply.
105 // A base function was provided; apply it.
106 const { length
, name
, prototype } = base
;
107 setPrototype($, getPrototype(base
));
108 return applyProperties($, {
109 length
: +length
+ lengthDelta
,
115 const applyProperties
= ($, override
) => {
116 if (override
=== undefined) {
117 // No properties were provided to apply.
120 // Properties were provided; apply them.
121 const { length
, name
, prototype } = override
;
122 if (!isConstructor($) || prototype === undefined) {
123 // The provided function is not a constructor or no prototype
124 // value was provided.
126 // Do not modify the prototype property of the provided
130 // The provided function is a constructor and a prototype value
133 // Change the prototype property of the provided function to
135 defineOwnProperty($, "prototype", { value
: prototype });
137 return defineOwnProperties($, {
139 value
: toLength(length
=== undefined ? $.length
: length
),
142 value
: toFunctionName(
143 name
=== undefined ? $.name
?? "" : name
,
151 bind
: ($, boundThis
, boundArgs
) =>
156 argumentIterablePrototype
,
157 { args
: { value
: boundArgs
} },
160 createArrowFunction
: ($, propertyOverride
= undefined) =>
165 argumentIterablePrototype
,
166 { args
: { value
: $s
} },
172 createCallableFunction
: ($, propertyOverride
= undefined) =>
176 const iterator
= objectCreate(
177 argumentIterablePrototype
,
178 { args
: { value
: $s
} },
180 const { value
: thisValue
} = iterator
.next();
181 return call($, thisValue
, [...iterator
]);
188 createIllegalConstructor
: ($, propertyOverride
= undefined) => {
189 const constructor = applyProperties(
192 throw new TypeError("Illegal constructor");
198 return defineOwnProperty(constructor, "prototype", {
202 toFunctionName
: ($, prefix
= undefined) => {
203 const key
= toPrimitive($, "string");
204 const name
= (() => {
205 if (typeof key
=== "symbol") {
206 // The provided value is a symbol; format its description.
207 const description
= call(getSymbolDescription
, key
, []);
208 return description
=== undefined ? "" : `[${description}]`;
210 // The provided value not a symbol; convert it to a string
215 return prefix
!== undefined ? `${prefix} ${name}` : name
;
222 * Calls the provided function with the provided this value and
225 * ☡ This is effectively an alias for `Reflect.apply`—the arguments
226 * must be passed as an arraylike.
231 * Constructs the provided function with the provided arguments list
234 * ☡ This is effectively an alias for `Reflect.construct`—the
235 * arguments must be passed as an arraylike.
239 /** Returns whether the provided value is a constructor. */
242 const { apply
, construct
} = Reflect
;
244 call
: (target
, thisArgument
, argumentsList
) =>
245 apply(target
, thisArgument
, argumentsList
),
246 construct
: (target
, argumentsList
, ...args
) =>
248 ? construct(target
, argumentsList
, args
[0])
249 : construct(target
, argumentsList
),
250 isConstructor
: ($) =>
251 completesNormally(() =>
252 // Try constructing a new object with the provided value as its
253 // `new.target`. This will throw if the provided value is not a
255 construct(function () {}, [], $)
261 * Returns whether calling the provided function with no `this` value
262 * or arguments completes normally; that is, does not throw an error.
264 * ☡ This function will throw an error if the provided argument is not
267 export const completesNormally
= ($) => {
268 if (!isCallable($)) {
269 // The provided value is not callable; this is an error.
271 `Piscēs: Cannot determine completion of noncallable value: ${$}`,
274 // The provided value is callable.
276 // Attempt to call the function and return true if this succeeds.
280 // Calling the function did not succeed; return false.
287 * Returns the provided value.
289 * ※ This function can be called as a constructor. When used in an
290 * `extends` clause and called via `super`, it will set the value of
291 * `this` to the provided value, enabling it to be extended with
292 * private class features.
294 export const identity = function ($) {
298 /** Returns whether the provided value is callable. */
299 export const isCallable
= ($) => typeof $ === "function";
302 * Returns whether the provided object inherits from the prototype of
303 * the provided function.
305 export const ordinaryHasInstance
= createCallableFunction(
306 Function
.prototype[Symbol
.hasInstance
],
307 { name
: "ordinaryHasInstance" },