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/>.
18 defineOwnDataProperty
,
21 getOwnPropertyDescriptor
,
30 * Creates a bound function from the provided function using the
31 * provided this value and arguments list.
33 * ☡ As with `call` and `construct`, the arguments must be passed as
39 * Returns a new function which calls the provided function with its
40 * first argument as the `this` value and the remaining arguments
43 * The `length`, `name`, and prototype of the provided function will
44 * be preserved in the new one. A second argument may be used to
45 * override `length` and `name`.
50 * Returns a new function which calls the provided function with its
51 * first argument as the `this` value and the remaining arguments
54 * The `length`, `name`, and prototype of the provided function will
55 * be preserved in the new one. A second argument may be used to
56 * override `length` and `name`.
58 * ※ This is effectively an alias for `Function::call.bind`.
60 createCallableFunction
,
63 * Returns a constructor which throws whenever it is called but has
64 * the same `.name` and `.prototype` as the provided value.
66 * The `length`, `name`, `prototype`, and prototype of the provided
67 * function will be preserved in the new one. A second argument may
68 * be used to override `length`, `name`, and `prototype`.
70 createIllegalConstructor
,
73 * Returns a constructor which produces a new constructor which wraps
74 * the provided constructor, but returns a proxy of the result using
75 * the provided handler.
77 * The resulting constructor inherits from, and has the same basic
80 * If a base constructor is not provided, `Object` will be used.
82 * If a third argument is provided, it is used as the target for the
83 * provided constructor when it is constructed. This can be used to
84 * prevent leakage of the provided constructor to superclasses
85 * through `new.target`.
87 * The `length` of the provided function will be preserved in the new
88 * one. A fourth argument may be used to override `length` and
91 * ※ `.prototype` will be present, but undefined, on the resulting
92 * constructor. This differs from the behaviour of `Proxy`, for which
93 * `.prototype` is not present at all. It is not presently possible
94 * to create a constructor with no `.prototype` property in
97 createProxyConstructor
,
99 const { prototype: functionPrototype
} = Function
;
103 } = functionPrototype
;
104 const objectConstructor
= Object
;
105 const proxyConstructor
= Proxy
;
108 construct
: reflectConstruct
,
110 const callBind
= reflectApply(functionBind
, functionCall
, [
113 const { revocable
} = Proxy
;
114 const { [ITERATOR
]: arrayIterator
} = Array
.prototype;
116 next
: arrayIteratorNext
,
117 } = getPrototype([][ITERATOR
]());
118 const argumentIterablePrototype
= {
126 call(arrayIterator
, this.args
, []),
131 const applyBaseFunction
= ($, base
, lengthDelta
= 0) => {
132 if (base
=== UNDEFINED
) {
133 // No base function was provided to apply.
136 // A base function was provided; apply it.
137 const { length
, name
, prototype } = base
;
138 if (getPrototype($) === functionPrototype
) {
139 setPrototype($, getPrototype(base
));
143 return applyProperties($, {
144 length
: +length
+ lengthDelta
,
150 const applyProperties
= ($, override
) => {
151 if (override
=== UNDEFINED
) {
152 // No properties were provided to apply.
155 // Properties were provided; apply them.
156 const { length
, name
, prototype } = override
;
158 prototype === UNDEFINED
||
159 !getOwnPropertyDescriptor($, "prototype")?.writable
161 // The provided function has no `.prototype`, its prototype is
162 // not writable, or no prototype value was provided.
164 // Do not modify the prototype property of the provided
168 // The provided function is a constructor and a prototype value
171 // Change the prototype property of the provided function to
176 defineOwnDataProperty(
183 return defineOwnProperties($, {
184 length
: defineOwnDataProperty(
187 toLength(length
=== UNDEFINED
? $.length
: length
),
189 name
: defineOwnDataProperty(
192 toFunctionName(name
=== UNDEFINED
? $.name
?? "" : name
),
199 bind
: ($, boundThis
, boundArgs
) =>
203 ...defineOwnDataProperty(
204 objectCreate(argumentIterablePrototype
),
209 createArrowFunction
: ($, propertyOverride
= UNDEFINED
) =>
212 (...$s
) => reflectApply($, UNDEFINED
, $s
),
217 createCallableFunction
: ($, propertyOverride
= UNDEFINED
) =>
221 const iterator
= defineOwnDataProperty(
222 objectCreate(argumentIterablePrototype
),
226 const { value
: thisValue
} = iterator
.next();
227 return reflectApply($, thisValue
, [...iterator
]);
234 createIllegalConstructor
: ($, propertyOverride
= UNDEFINED
) =>
239 throw new TypeError("Illegal constructor");
248 createProxyConstructor
: (
251 newTarget
= UNDEFINED
,
252 propertyOverride
= UNDEFINED
,
254 const constructor = $ === UNDEFINED
? objectConstructor
: $;
255 const target
= newTarget
=== UNDEFINED
? constructor : newTarget
;
256 const len
= toLength(constructor.length
);
257 if (!(type(handler
) === "object")) {
258 // The provided handler is not an object; this is an error.
260 `Piscēs: Proxy handler must be an object, but got: ${handler}.`,
262 } else if (!isConstructor(constructor)) {
263 // The provided constructor is not a constructor; this is an
266 "Piscēs: Cannot create proxy constructor from nonconstructible value.",
268 } else if (!isConstructor(target
)) {
269 // The provided new target is not a constructor; this is an
272 "Piscēs: New target must be a constructor.",
275 // The arguments are acceptable.
276 return applyProperties(
280 if (new.target
=== UNDEFINED
) {
281 // The constructor was not called with new; this is
286 } must be called with new.`,
289 // The constructor was called with new; return the
290 // appropriate proxy.
291 const O
= reflectConstruct(
296 return new proxyConstructor(O
, handler
);
302 length
: defineOwnDataProperty(
307 name
: defineOwnDataProperty(
310 `${toFunctionName(constructor.name ?? "")}Proxy`,
312 prototype: setPropertyValues(objectCreate(null), {
318 revocable
: setPropertyValues(objectCreate(null), {
321 value
: defineOwnProperties(
323 const O
= reflectConstruct(
328 return revocable(O
, handler
);
331 length
: defineOwnDataProperty(
336 name
: defineOwnDataProperty(
355 * Calls the provided function with the provided this value and
358 * ☡ This is effectively an alias for `Reflect.apply`—the arguments
359 * must be passed as an arraylike.
361 export const call
= createArrowFunction(Reflect
.apply
, {
366 * Returns whether calling the provided function with no `this` value
367 * or arguments completes normally; that is, does not throw an error.
369 * ☡ This function will throw an error if the provided argument is not
372 export const completesNormally
= ($) => {
373 if (!isCallable($)) {
374 // The provided value is not callable; this is an error.
376 `Piscēs: Cannot determine completion of noncallable value: ${$}`,
379 // The provided value is callable.
381 // Attempt to call the function and return true if this succeeds.
385 // Calling the function did not succeed; return false.
392 * Constructs the provided function with the provided arguments list
395 * ☡ This is effectively an alias for `Reflect.construct`—the
396 * arguments must be passed as an arraylike.
398 export const construct
= createArrowFunction(Reflect
.construct
);
401 * Returns the provided value.
403 * ※ This function can be called as a constructor. When used in an
404 * `extends` clause and called via `super`, it will set the value of
405 * `this` to the provided value, enabling it to be extended with
406 * private class features.
408 export const identity = function ($) {
412 /** Returns whether the provided value is callable. */
413 export const isCallable
= ($) => typeof $ === "function";
415 /** Returns whether the provided value is a constructor. */
416 export const isConstructor
= ($) =>
417 completesNormally(() =>
418 // Try constructing a new object with the provided value as its
419 // `new.target`. This will throw if the provided value is not a
421 construct(function () {}, [], $)
425 * Calls the provided callback with the provided argument if the
426 * provided argument is not nullish; otherwise, returns the provided
427 * argument unmodified.
429 export const maybe
= ($, callback
) => $ == null ? $ : callback($);
432 * Returns whether the provided object inherits from the prototype of
433 * the provided function.
435 export const ordinaryHasInstance
= createCallableFunction(
436 Function
.prototype[Symbol
.hasInstance
],
437 { name
: "ordinaryHasInstance" },