-// ♓🌟 Piscēs ∷ function.js
-// ====================================================================
-//
-// Copyright © 2022‐2023 Lady [@ Lady’s Computer].
-//
-// This Source Code Form is subject to the terms of the Mozilla Public
-// License, v. 2.0. If a copy of the MPL was not distributed with this
-// file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+// SPDX-FileCopyrightText: 2022, 2023, 2025 Lady <https://www.ladys.computer/about/#lady>
+// SPDX-License-Identifier: MPL-2.0
+/**
+ * ⁌ ♓🧩 Piscēs ∷ function.js
+ *
+ * Copyright © 2022–2023, 2025 Lady [@ Ladys Computer].
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
+ */
import {
ITERATOR,
defineOwnProperty,
getOwnPropertyDescriptor,
getPrototype,
+ hasOwnProperty,
objectCreate,
setPropertyValues,
setPrototype,
} from "./object.js";
+const PISCĒS = "♓🧩 Piscēs";
+
export const {
/**
* Creates a bound function from the provided function using the
* provided this value and arguments list.
*
- * ☡ As with `call` and `construct`, the arguments must be passed as
+ * ☡ As with `call´ and `construct´, the arguments must be passed as
* an array.
*/
bind,
/**
- * Returns a new function which calls the provided function with its
- * first argument as the `this` value and the remaining arguments
- * passed through.
+ * Returns a new arrow function function which wraps the provided
+ * function and passes its arguments thru.
*
- * The `length`, `name`, and prototype of the provided function will
+ * The `length´, `name´, and prototype of the provided function will
* be preserved in the new one. A second argument may be used to
- * override `length` and `name`.
+ * override `length´ and `name´.
*/
createArrowFunction,
/**
- * Returns a new function which calls the provided function with its
- * first argument as the `this` value and the remaining arguments
- * passed through.
+ * Returns a new arrow function which wraps the provided function,
+ * using its first argument as the this value when calling the
+ * provided function and passing the remainder thru.
*
- * The `length`, `name`, and prototype of the provided function will
+ * The `length´, `name´, and prototype of the provided function will
* be preserved in the new one. A second argument may be used to
- * override `length` and `name`.
+ * override `length´ and `name´.
*
- * ※ This is effectively an alias for `Function::call.bind`.
+ * ※ This is effectively an alias for `Function::call.bind´.
*/
createCallableFunction,
/**
* Returns a constructor which throws whenever it is called but has
- * the same `.name` and `.prototype` as the provided value.
+ * the same `.length´, `.name´, and `.prototype´ as the provided
+ * value.
*
- * The `length`, `name`, `prototype`, and prototype of the provided
+ * The `length´, `name´, `prototype´, and prototype of the provided
* function will be preserved in the new one. A second argument may
- * be used to override `length`, `name`, and `prototype`.
+ * be used to override `length´, `name´, and `prototype´.
*/
createIllegalConstructor,
/**
- * Returns a constructor which produces a new constructor which wraps
+ * Returns a constructor which produces a new constructor that wraps
* the provided constructor, but returns a proxy of the result using
* the provided handler.
*
* The resulting constructor inherits from, and has the same basic
- * shape as, `Proxy`.
+ * shape as, `Proxy´.
*
- * If a base constructor is not provided, `Object` will be used.
+ * If a base constructor is not provided, `Object´ will be used.
*
* If a third argument is provided, it is used as the target for the
* provided constructor when it is constructed. This can be used to
* prevent leakage of the provided constructor to superclasses
- * through `new.target`.
+ * through `new.target´.
*
- * The `length` of the provided function will be preserved in the new
- * one. A fourth argument may be used to override `length` and
- * `name`.
+ * The `length´ of the provided function will be preserved in the new
+ * one. A fourth argument may be used to override `length´ and
+ * `name´.
*
- * ※ `.prototype` will be present, but undefined, on the resulting
- * constructor. This differs from the behaviour of `Proxy`, for which
- * `.prototype` is not present at all. It is not presently possible
- * to create a constructor with no `.prototype` property in
+ * ※ `.prototype´ will be present, but undefined, on the resulting
+ * constructor. This differs from the behaviour of `Proxy´, for which
+ * `.prototype´ is not present at all. It is not presently possible
+ * to create a constructor with no `.prototype´ property in
* Ecmascript code.
*/
createProxyConstructor,
},
next: callBind(
arrayIteratorNext,
- call(arrayIterator, this.args, []),
+ reflectApply(arrayIterator, this.args, []),
),
};
},
constructor,
]);
if (existing) {
+ // There is an existing values set for this constructor.
+ //
+ // It is returned.
return existing;
} else {
+ // There is no existing values set for this constructor so one
+ // must be created.
const result = new wsConstructor();
reflectApply(wmSet, proxyConstructorValuesMap, [
constructor,
// No base function was provided to apply.
return $;
} else {
- // A base function was provided; apply it.
- const { length, name, prototype } = base;
+ // A base function was provided.
+ //
+ // Apply it.
+ const overrides = objectCreate(null);
+ overrides.length = +base.length + lengthDelta;
+ overrides.name = base.name;
if (getPrototype($) === functionPrototype) {
+ // The provided function has the function prototype.
+ //
+ // Change it to match the base function.
setPrototype($, getPrototype(base));
} else {
+ // The provided function already does not have the function
+ // prototype, so its prototype is not changed.
/* do nothing */
}
- return applyProperties($, {
- length: +length + lengthDelta,
- name,
- prototype,
- });
+ if (hasOwnProperty($, "prototype") && "prototype" in base) {
+ // The provided function has a `.prototype´ property, and one
+ // was provided in the base function as well.
+ try {
+ // If this does not throw, the provided function is a
+ // constructor. Its `.prototype´ property is set alongside
+ // the others.
+ reflectConstruct(function () {}, [], $);
+ overrides.prototype = base.prototype;
+ } catch {
+ // The provided function is not a constructor.
+ /* do nothing */
+ }
+ } else {
+ // The provided function does not have a `.prototype´ property,
+ // or the base function did not have one.
+ /* do nothing */
+ }
+ return applyProperties($, overrides);
}
};
const applyProperties = ($, override) => {
// No properties were provided to apply.
return $;
} else {
- // Properties were provided; apply them.
- const { length, name, prototype } = override;
+ // Properties were provided.
+ //
+ // Apply them.
+ const { length, name } = override;
if (
- prototype === UNDEFINED ||
- !getOwnPropertyDescriptor($, "prototype")?.writable
+ !("prototype" in override)
+ || !getOwnPropertyDescriptor($, "prototype")?.writable
) {
- // The provided function has no `.prototype`, its prototype is
+ // The provided function has no `.prototype´, its prototype is
// not writable, or no prototype value was provided.
//
// Do not modify the prototype property of the provided
// function.
/* do nothing */
} else {
- // The provided function is a constructor and a prototype value
- // was provided.
+ // The provided function has a writable `.prototype´ and a
+ // prototype value was provided.
//
// Change the prototype property of the provided function to
// match.
defineOwnDataProperty(
objectCreate(null),
"value",
- prototype,
+ override.prototype,
),
);
}
if (!(type(handler) === "object")) {
// The provided handler is not an object; this is an error.
throw new TypeError(
- `Piscēs: Proxy handler must be an object, but got: ${handler}.`,
+ `${PISCĒS}: Proxy handler must be an object, but got: ${handler}.`,
);
} else if (!isConstructor(constructor)) {
// The provided constructor is not a constructor; this is an
// error.
throw new TypeError(
- "Piscēs: Cannot create proxy constructor from nonconstructible value.",
+ `${PISCĒS}: Cannot create proxy constructor from nonconstructible value.`,
);
} else if (!isConstructor(target)) {
// The provided new target is not a constructor; this is an
// error.
throw new TypeError(
- "Piscēs: New target must be a constructor.",
+ `${PISCĒS}: New target must be a constructor.`,
);
} else {
// The arguments are acceptable.
setPrototype(
function (...$s) {
if (new.target === UNDEFINED) {
- // The constructor was not called with new; this is an
- // error.
+ // The constructor was not called with new; this is
+ // an error.
throw new TypeError(
- `Piscēs: ${
+ `${PISCĒS}: ${
C.name ?? "Proxy"
} must be called with new.`,
);
} else {
- // The constructor was called with new; return the
- // appropriate proxy.
+ // The constructor was called with new.
+ //
+ // Return the appropriate proxy.
const O = reflectConstruct(
constructor,
$s,
return false;
} else {
// One or more values has been registered for the
- // current constructor; return whether the provided
- // argument is one.
+ // current constructor.
+ //
+ // Return whether the provided argument is one.
return reflectApply(wsHas, values, [$]);
}
},
* Calls the provided function with the provided this value and
* arguments list.
*
- * ☡ This is effectively an alias for `Reflect.apply`—the arguments
+ * ☡ This is effectively an alias for `Reflect.apply´—the arguments
* must be passed as an arraylike.
*/
export const call = createArrowFunction(Reflect.apply, {
});
/**
- * Returns whether calling the provided function with no `this` value
+ * Returns whether calling the provided function with no this value
* or arguments completes normally; that is, does not throw an error.
*
* ☡ This function will throw an error if the provided argument is not
if (!isCallable($)) {
// The provided value is not callable; this is an error.
throw new TypeError(
- `Piscēs: Cannot determine completion of noncallable value: ${$}`,
+ `${PISCĒS}: Cannot determine completion of noncallable value: ${$}.`,
);
} else {
// The provided value is callable.
try {
- // Attempt to call the function and return true if this succeeds.
+ // This will throw if calling the function throws.
+ //
+ // Otherwise, return true.
$();
return true;
} catch {
- // Calling the function did not succeed; return false.
+ // Calling the function did not succeed.
+ //
+ // Return false.
return false;
}
}
* Constructs the provided function with the provided arguments list
* and new target.
*
- * ☡ This is effectively an alias for `Reflect.construct`—the
+ * ☡ This is effectively an alias for `Reflect.construct´—the
* arguments must be passed as an arraylike.
*/
export const construct = createArrowFunction(Reflect.construct);
* Returns the provided value.
*
* ※ This function can be called as a constructor. When used in an
- * `extends` clause and called via `super`, it will set the value of
- * `this` to the provided value, enabling it to be extended with
+ * `extends´ clause and called via `super´, it will set the value of
+ * `this´ to the provided value, enabling it to be extended with
* private class features.
*/
export const identity = function ($) {
/** Returns whether the provided value is a constructor. */
export const isConstructor = ($) =>
completesNormally(() =>
- // Try constructing a new object with the provided value as its
- // `new.target`. This will throw if the provided value is not a
- // constructor.
+ // Try constructing a new object using the provided value as
+ // `new.target´.
+ //
+ // ☡ This will throw if the provided value is not a constructor.
construct(function () {}, [], $)
);