* ※ This is effectively an alias for `Function::call.bind`.
*/
makeCallable,
+
+ /**
+ * Returns a constructor which throws whenever it is called but has
+ * the same `.name` and `.prototype` as the provided value.
+ *
+ * If a second argument is provided, the returned constructor will
+ * use that as its prototype; otherwise, it will use the prototype of
+ * the provided value.
+ */
+ makeIllegalConstructor,
} = (() => {
// ☡ Because these functions are used to initialize module constants,
// they can’t depend on imports from elsewhere.
create: objectCreate,
defineProperties: defineOwnProperties,
getPrototypeOf: getPrototype,
+ setPrototypeOf: setPrototype,
} = Object;
const { [ITERATOR]: arrayIterator } = Array.prototype;
const {
length: { value: $.length + 1 },
name: { value: name ?? $.name ?? "" },
}),
+ makeIllegalConstructor: ($, proto = undefined) => {
+ const constructor = function () {
+ throw new TypeError("Illegal constructor");
+ };
+ if ($ == null && proto === undefined) {
+ // The provided argument is nullish and no explicit prototype
+ // was provided.
+ //
+ // Do not modify the prototype of the generated constructor.
+ /* do nothing */
+ } else {
+ // The provided argument is not nullish or an explicit
+ // prototype was provided.
+ //
+ // Set the prototype of the generated constructor to match.
+ setPrototype(
+ constructor,
+ proto === undefined ? getPrototype($) : proto,
+ );
+ }
+ return defineOwnProperties(constructor, {
+ name: { value: $?.name ?? "" },
+ prototype: { value: $?.prototype ?? {}, writable: false },
+ });
+ },
};
})();
// file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
import {
+ assert,
assertEquals,
assertStrictEquals,
assertThrows,
isCallable,
isConstructor,
makeCallable,
+ makeIllegalConstructor,
ordinaryHasInstance,
} from "./function.js";
});
});
+describe("makeIllegalConstructor", () => {
+ it("[[Call]] returns a constructor", () => {
+ assert(isConstructor(makeIllegalConstructor()));
+ });
+
+ it("[[Call]] works as expected when provided with no arguments", () => {
+ const constructor = makeIllegalConstructor();
+ assertStrictEquals(
+ Object.getPrototypeOf(constructor),
+ Function.prototype,
+ );
+ assertStrictEquals(
+ Object.getPrototypeOf(constructor.prototype),
+ Object.prototype,
+ );
+ assertEquals(constructor.prototype, {});
+ assert(
+ !Object.getOwnPropertyDescriptor(constructor, "prototype")
+ .writable,
+ );
+ assertStrictEquals(constructor.name, "");
+ });
+
+ it("[[Call]] returns a correctly‐formed constructor when provided one argument", () => {
+ const constructorPrototype = Object.create(null, {
+ name: { value: "etaoin" },
+ prototype: { value: {} },
+ });
+ const constructor = makeIllegalConstructor(
+ Object.create(constructorPrototype),
+ );
+ assert(isConstructor(constructor));
+ assertStrictEquals(
+ Object.getPrototypeOf(constructor),
+ constructorPrototype,
+ );
+ assert(Object.hasOwn(constructor, "prototype"));
+ assertEquals(
+ constructor.prototype,
+ constructorPrototype.prototype,
+ );
+ assert(
+ !Object.getOwnPropertyDescriptor(constructor, "prototype")
+ .writable,
+ );
+ assertStrictEquals(constructor.name, "etaoin");
+ });
+
+ it("[[Call]] allows the second argument to override the prototype of the constructor", () => {
+ const constructorPrototype = Object.create(null, {
+ name: { value: "etaoin" },
+ prototype: { value: {} },
+ });
+ const expectedPrototype = Object.create(null, {
+ name: { value: "shrdlu" },
+ prototype: { value: {} },
+ });
+ const constructor = makeIllegalConstructor(
+ Object.create(constructorPrototype),
+ expectedPrototype,
+ );
+ assert(isConstructor(constructor));
+ assertStrictEquals(
+ Object.getPrototypeOf(constructor),
+ expectedPrototype,
+ );
+ assert(Object.hasOwn(constructor, "prototype"));
+ assertEquals(
+ constructor.prototype,
+ constructorPrototype.prototype,
+ );
+ assert(
+ !Object.getOwnPropertyDescriptor(constructor, "prototype")
+ .writable,
+ );
+ assertStrictEquals(constructor.name, "etaoin");
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => new makeIllegalConstructor(function () {}));
+ });
+
+ describe("~", () => {
+ it("[[Call]] throws an error", () => {
+ assertThrows(() => {
+ makeIllegalConstructor(function () {})();
+ });
+ });
+
+ it("[[Construct]] throws an error", () => {
+ assertThrows(() => {
+ makeIllegalConstructor(function () {})();
+ });
+ });
+ });
+
+ describe(".name", () => {
+ it("[[Get]] returns the correct name", () => {
+ assertStrictEquals(
+ makeIllegalConstructor.name,
+ "makeIllegalConstructor",
+ );
+ });
+ });
+});
+
describe("ordinaryHasInstance", () => {
it("[[Call]] walks the prototype chain", () => {
const constructor = class {