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
} 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 * ※ This is effectively an alias for `Function::call.bind`.
29 createCallableFunction
,
32 * Returns a constructor which throws whenever it is called but has
33 * the same `.name` and `.prototype` as the provided value.
35 * If a second argument is provided, the returned constructor will
36 * use that as its prototype; otherwise, it will use the prototype of
39 createIllegalConstructor
,
42 * Returns a string function name generated from the provided value
43 * and optional prefix.
47 // ☡ Because these functions are used to initialize module constants,
48 // they can’t depend on imports from elsewhere.
52 } = Function
.prototype;
53 const callBind
= Reflect
.apply(functionBind
, functionCall
, [
58 defineProperties
: defineOwnProperties
,
59 getOwnPropertyDescriptor
,
60 getPrototypeOf
: getPrototype
,
61 setPrototypeOf
: setPrototype
,
63 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
65 next
: arrayIteratorNext
,
66 } = getPrototype([][ITERATOR
]());
67 const argumentIterablePrototype
= {
72 call(arrayIterator
, this.args
, []),
77 const getSymbolDescription
= getOwnPropertyDescriptor(
83 bind
: ($, boundThis
, boundArgs
) =>
88 argumentIterablePrototype
,
89 { args
: { value
: boundArgs
} },
92 createCallableFunction
: ($, name
= undefined) =>
93 defineOwnProperties(callBind(functionCall
, $), {
94 length
: { value
: $.length
+ 1 },
96 value
: toFunctionName(
97 name
=== undefined ? $.name
?? "" : name
,
101 createIllegalConstructor
: ($, proto
= undefined) => {
102 const constructor = function () {
103 throw new TypeError("Illegal constructor");
105 if ($ == null && proto
=== undefined) {
106 // The provided argument is nullish and no explicit prototype
109 // Do not modify the prototype of the generated constructor.
112 // The provided argument is not nullish or an explicit
113 // prototype was provided.
115 // Set the prototype of the generated constructor to match.
118 proto
=== undefined ? getPrototype($) : proto
,
121 return defineOwnProperties(constructor, {
122 name
: { value
: $?.name
?? "" },
123 prototype: { value
: $?.prototype ?? {}, writable
: false },
126 toFunctionName
: ($, prefix
= undefined) => {
127 const name
= (() => {
128 if (typeof $ === "symbol") {
129 // The provided value is a symbol; format its description.
130 const description
= call(getSymbolDescription
, $, []);
131 return description
=== undefined ? "" : `[${description}]`;
133 // The provided value not a symbol; convert it to a string
138 return prefix
!== undefined ? `${prefix} ${name}` : name
;
145 * Calls the provided function with the provided this value and
148 * ☡ This is effectively an alias for `Reflect.apply`—the arguments
149 * must be passed as an arraylike.
154 * Constructs the provided function with the provided arguments list
157 * ☡ This is effectively an alias for `Reflect.construct`—the
158 * arguments must be passed as an arraylike.
162 /** Returns whether the provided value is a constructor. */
165 const { apply
, construct
} = Reflect
;
167 call
: (target
, thisArgument
, argumentsList
) =>
168 apply(target
, thisArgument
, argumentsList
),
169 construct
: (target
, argumentsList
, ...args
) =>
171 ? construct(target
, argumentsList
, args
[0])
172 : construct(target
, argumentsList
),
173 isConstructor
: ($) =>
174 completesNormally(() =>
175 // Try constructing a new object with the provided value as its
176 // `new.target`. This will throw if the provided value is not a
178 construct(function () {}, [], $)
184 * Returns whether calling the provided function with no `this` value
185 * or arguments completes normally; that is, does not throw an error.
187 * ☡ This function will throw an error if the provided argument is not
190 export const completesNormally
= ($) => {
191 if (!isCallable($)) {
192 // The provided value is not callable; this is an error.
194 `Piscēs: Cannot determine completion of noncallable value: ${$}`,
197 // The provided value is callable.
199 // Attempt to call the function and return true if this succeeds.
203 // Calling the function did not succeed; return false.
210 * Returns the provided value.
212 * ※ This function can be called as a constructor. When used in an
213 * `extends` clause and called via `super`, it will set the value of
214 * `this` to the provided value, enabling it to be extended with
215 * private class features.
217 export const identity = function ($) {
221 /** Returns whether the provided value is callable. */
222 export const isCallable
= ($) => typeof $ === "function";
225 * Returns whether the provided object inherits from the prototype of
226 * the provided function.
228 export const ordinaryHasInstance
= createCallableFunction(
229 Function
.prototype[Symbol
.hasInstance
],
230 "ordinaryHasInstance",