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 callBind
= Reflect
.apply(functionBind
, functionCall
, [
66 defineProperty
: defineOwnProperty
,
67 defineProperties
: defineOwnProperties
,
68 hasOwn
: hasOwnProperty
,
69 getPrototypeOf
: getPrototype
,
70 setPrototypeOf
: setPrototype
,
72 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
74 next
: arrayIteratorNext
,
75 } = getPrototype([][ITERATOR
]());
76 const argumentIterablePrototype
= {
84 call(arrayIterator
, this.args
, []),
89 const applyBaseFunction
= ($, base
, lengthDelta
= 0) => {
90 if (base
=== undefined) {
91 // No base function was provided to apply.
94 // A base function was provided; apply it.
95 const { length
, name
, prototype } = base
;
96 if (getPrototype($) === functionPrototype
) {
97 setPrototype($, getPrototype(base
));
101 return applyProperties($, {
102 length
: +length
+ lengthDelta
,
108 const applyProperties
= ($, override
) => {
109 if (override
=== undefined) {
110 // No properties were provided to apply.
113 // Properties were provided; apply them.
114 const { length
, name
, prototype } = override
;
116 !isConstructor($) || !hasOwnProperty($, "prototype") ||
117 prototype === undefined
119 // The provided function is not a constructor, has no
120 // `.prototype`, or no prototype value was provided.
122 // Do not modify the prototype property of the provided
126 // The provided function is a constructor and a prototype value
129 // Change the prototype property of the provided function to
131 defineOwnProperty($, "prototype", { value
: prototype });
133 return defineOwnProperties($, {
135 value
: toLength(length
=== undefined ? $.length
: length
),
138 value
: toFunctionName(
139 name
=== undefined ? $.name
?? "" : name
,
147 bind
: ($, boundThis
, boundArgs
) =>
152 argumentIterablePrototype
,
153 { args
: { value
: boundArgs
} },
156 createArrowFunction
: ($, propertyOverride
= undefined) =>
161 argumentIterablePrototype
,
162 { args
: { value
: $s
} },
168 createCallableFunction
: ($, propertyOverride
= undefined) =>
172 const iterator
= objectCreate(
173 argumentIterablePrototype
,
174 { args
: { value
: $s
} },
176 const { value
: thisValue
} = iterator
.next();
177 return call($, thisValue
, [...iterator
]);
184 createIllegalConstructor
: ($, propertyOverride
= undefined) => {
185 const constructor = applyProperties(
188 throw new TypeError("Illegal constructor");
194 return defineOwnProperty(constructor, "prototype", {
203 * Calls the provided function with the provided this value and
206 * ☡ This is effectively an alias for `Reflect.apply`—the arguments
207 * must be passed as an arraylike.
212 * Constructs the provided function with the provided arguments list
215 * ☡ This is effectively an alias for `Reflect.construct`—the
216 * arguments must be passed as an arraylike.
220 /** Returns whether the provided value is a constructor. */
223 const { apply
, construct
} = Reflect
;
225 call
: (target
, thisArgument
, argumentsList
) =>
226 apply(target
, thisArgument
, argumentsList
),
227 construct
: (target
, argumentsList
, ...args
) =>
229 ? construct(target
, argumentsList
, args
[0])
230 : construct(target
, argumentsList
),
231 isConstructor
: ($) =>
232 completesNormally(() =>
233 // Try constructing a new object with the provided value as its
234 // `new.target`. This will throw if the provided value is not a
236 construct(function () {}, [], $)
242 * Returns whether calling the provided function with no `this` value
243 * or arguments completes normally; that is, does not throw an error.
245 * ☡ This function will throw an error if the provided argument is not
248 export const completesNormally
= ($) => {
249 if (!isCallable($)) {
250 // The provided value is not callable; this is an error.
252 `Piscēs: Cannot determine completion of noncallable value: ${$}`,
255 // The provided value is callable.
257 // Attempt to call the function and return true if this succeeds.
261 // Calling the function did not succeed; return false.
268 * Returns the provided value.
270 * ※ This function can be called as a constructor. When used in an
271 * `extends` clause and called via `super`, it will set the value of
272 * `this` to the provided value, enabling it to be extended with
273 * private class features.
275 export const identity = function ($) {
279 /** Returns whether the provided value is callable. */
280 export const isCallable
= ($) => typeof $ === "function";
283 * Returns whether the provided object inherits from the prototype of
284 * the provided function.
286 export const ordinaryHasInstance
= createCallableFunction(
287 Function
.prototype[Symbol
.hasInstance
],
288 { name
: "ordinaryHasInstance" },