]> Lady’s Gitweb - Pisces/commitdiff
Swap some things between value.js and object.js
authorLady <redacted>
Sat, 25 Nov 2023 17:28:29 +0000 (12:28 -0500)
committerLady <redacted>
Sat, 25 Nov 2023 17:28:29 +0000 (12:28 -0500)
- Property Descriptor record functions → object.js. Leaving
  `completePropertyDescriptor` in value.js for now, since it does not
  expect specifically a property descriptor record.

- `toPropertyKey` → value.js

object.js
object.test.js
value.js
value.test.js

index aa94bc571c469f3125b4d701dc07bacd6220ccdb..bd310ca87946e2990a2fb2a5731198c1f6f043fa 100644 (file)
--- a/object.js
+++ b/object.js
@@ -13,8 +13,6 @@ import {
   SPECIES,
   toFunctionName,
   toLength,
-  toPrimitive,
-  toPropertyDescriptor,
   type,
   UNDEFINED,
 } from "./value.js";
@@ -46,11 +44,13 @@ const {
 const {
   apply: call,
   deleteProperty,
+  defineProperty: reflectDefineProperty,
   get,
   getOwnPropertyDescriptor: reflectGetOwnPropertyDescriptor,
   has,
   ownKeys,
   set,
+  setPrototypeOf: reflectSetPrototypeOf,
 } = Reflect;
 
 /**
@@ -339,7 +339,9 @@ export const getMethod = (V, P) => {
  */
 export const getOwnPropertyDescriptor = (O, P) => {
   const desc = objectGetOwnPropertyDescriptor(O, P);
-  return desc === UNDEFINED ? UNDEFINED : toPropertyDescriptor(desc);
+  return desc === UNDEFINED
+    ? UNDEFINED
+    : toPropertyDescriptorRecord(desc);
 };
 
 /**
@@ -472,6 +474,220 @@ export const isConcatSpreadableObject = ($) => {
  */
 export const isExtensibleObject = (O) => isExtensible(O);
 
+export const {
+  /**
+   * Returns whether the provided value is a property descriptor record
+   * as created by `toPropertyDescriptor`.
+   *
+   * ※ This function is provided to enable inspection of whether an
+   * object uses the property descriptor record proxy implementation,
+   * not as a general test of whether an object satisfies the
+   * requirements for property descriptors. In most cases, a more
+   * specific test, like `isAccessorDescriptor`, `isDataDescriptor`, or
+   * `isGenericDescriptor`, is preferrable.
+   */
+  isPropertyDescriptorRecord,
+
+  /**
+   * Converts the provided value to a property descriptor record.
+   *
+   * ※ The prototype of a property descriptor record is always `null`.
+   *
+   * ※ Actually constructing a property descriptor object using this
+   * class is only necessary if you need strict guarantees about the
+   * types of its properties; the resulting object is proxied to ensure
+   * the types match what one would expect from composing
+   * FromPropertyDescriptor and ToPropertyDescriptor in the Ecmascript
+   * specification.
+   */
+  toPropertyDescriptorRecord,
+} = (() => {
+  const proxyConstructor = Proxy;
+  const { add: weakSetAdd, has: weakSetHas } = WeakSet.prototype;
+  const propertyDescriptorRecords = new WeakSet();
+  const coercePropertyDescriptorValue = (P, V) => {
+    switch (P) {
+      case "configurable":
+      case "enumerable":
+      case "writable":
+        return !!V;
+      case "value":
+        return V;
+      case "get":
+        if (V !== undefined && typeof V !== "function") {
+          throw new TypeError(
+            "Piscēs: Getters must be callable.",
+          );
+        } else {
+          return V;
+        }
+      case "set":
+        if (V !== undefined && typeof V !== "function") {
+          throw new TypeError(
+            "Piscēs: Setters must be callable.",
+          );
+        } else {
+          return V;
+        }
+      default:
+        return V;
+    }
+  };
+  const propertyDescriptorProxyHandler = objectFreeze(
+    assign(
+      create(null),
+      {
+        defineProperty(O, P, Desc) {
+          if (
+            P === "configurable" || P === "enumerable" ||
+            P === "writable" || P === "value" ||
+            P === "get" || P === "set"
+          ) {
+            // P is a property descriptor attribute.
+            const desc = assign(objectCreate(null), Desc);
+            if ("get" in desc || "set" in desc) {
+              // Desc is an accessor property descriptor.
+              throw new TypeError(
+                "Piscēs: Property descriptor attributes must be data properties.",
+              );
+            } else if ("value" in desc || !(P in O)) {
+              // Desc has a value or P does not already exist on O.
+              desc.value = coercePropertyDescriptorValue(
+                P,
+                desc.value,
+              );
+            } else {
+              // Desc is not an accessor property descriptor and has no
+              // value, but an existing value is present on O.
+              /* do nothing */
+            }
+            const isAccessorDescriptor = "get" === P || "set" === P ||
+              "get" in O || "set" in O;
+            const isDataDescriptor = "value" === P ||
+              "writable" === P ||
+              "value" in O || "writable" in O;
+            if (isAccessorDescriptor && isDataDescriptor) {
+              // Both accessor and data attributes will be present on O
+              // after defining P.
+              throw new TypeError(
+                "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+              );
+            } else {
+              // P can be safely defined on O.
+              return reflectDefineProperty(O, P, desc);
+            }
+          } else {
+            // P is not a property descriptor attribute.
+            return reflectDefineProperty(O, P, Desc);
+          }
+        },
+        setPrototypeOf(O, V) {
+          if (V !== null) {
+            // V is not the property descriptor prototype.
+            return false;
+          } else {
+            // V is the property descriptor prototype.
+            return reflectSetPrototypeOf(O, V);
+          }
+        },
+      },
+    ),
+  );
+
+  return {
+    isPropertyDescriptorRecord: ($) =>
+      call(weakSetHas, propertyDescriptorRecords, [$]),
+    toPropertyDescriptorRecord: (Obj) => {
+      if (type(Obj) !== "object") {
+        // The provided value is not an object.
+        throw new TypeError(
+          `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
+        );
+      } else {
+        // The provided value is an object.
+        const desc = create(null);
+        if ("enumerable" in Obj) {
+          // An enumerable property is specified.
+          defineOwnDataProperty(desc, "enumerable", !!Obj.enumerable);
+        } else {
+          // An enumerable property is not specified.
+          /* do nothing */
+        }
+        if ("configurable" in Obj) {
+          // A configurable property is specified.
+          defineOwnDataProperty(
+            desc,
+            "configurable",
+            !!Obj.configurable,
+          );
+        } else {
+          // A configurable property is not specified.
+          /* do nothing */
+        }
+        if ("value" in Obj) {
+          // A value property is specified.
+          defineOwnDataProperty(desc, "value", Obj.value);
+        } else {
+          // A value property is not specified.
+          /* do nothing */
+        }
+        if ("writable" in Obj) {
+          // A writable property is specified.
+          defineOwnDataProperty(desc, "writable", !!Obj.writable);
+        } else {
+          // A writable property is not specified.
+          /* do nothing */
+        }
+        if ("get" in Obj) {
+          // A get property is specified.
+          const getter = Obj.get;
+          if (getter !== UNDEFINED && typeof getter !== "function") {
+            // The getter is not callable.
+            throw new TypeError("Piscēs: Getters must be callable.");
+          } else {
+            // The getter is callable.
+            defineOwnDataProperty(desc, "get", Obj.get);
+          }
+        } else {
+          // A get property is not specified.
+          /* do nothing */
+        }
+        if ("set" in Obj) {
+          // A set property is specified.
+          const setter = Obj.set;
+          if (setter !== UNDEFINED && typeof setter !== "function") {
+            // The setter is not callable.
+            throw new TypeError("Piscēs: Setters must be callable.");
+          } else {
+            // The setter is callable.
+            defineOwnDataProperty(desc, "set", Obj.set);
+          }
+        } else {
+          // A set property is not specified.
+          /* do nothing */
+        }
+        if (
+          ("get" in desc || "set" in desc) &&
+          ("value" in desc || "writable" in desc)
+        ) {
+          // Both accessor and data attributes have been defined.
+          throw new TypeError(
+            "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
+          );
+        } else {
+          // The property descriptor is valid.
+          const record = new proxyConstructor(
+            desc,
+            propertyDescriptorProxyHandler,
+          );
+          call(weakSetAdd, propertyDescriptorRecords, [record]);
+          return record;
+        }
+      }
+    },
+  };
+})();
+
 /**
  * Returns whether the provided value is an unfrozen object.
  *
@@ -661,12 +877,3 @@ export const toObject = ($) => {
     return object($);
   }
 };
-
-/**
- * Returns the property key (symbol or string) corresponding to the
- * provided value.
- */
-export const toPropertyKey = ($) => {
-  const key = toPrimitive($, "string");
-  return typeof key === "symbol" ? key : `${key}`;
-};
index 3a0e1a73363a94d855ede03c3ed99ba7e67265f0..348d06da83332901c8047288658ab08c607cfa56 100644 (file)
@@ -10,6 +10,7 @@
 import {
   assert,
   assertEquals,
+  assertNotStrictEquals,
   assertSpyCall,
   assertSpyCalls,
   assertStrictEquals,
@@ -39,6 +40,7 @@ import {
   isArraylikeObject,
   isConcatSpreadableObject,
   isExtensibleObject,
+  isPropertyDescriptorRecord,
   isUnfrozenObject,
   isUnsealedObject,
   LazyLoader,
@@ -54,7 +56,7 @@ import {
   setPropertyValues,
   setPrototype,
   toObject,
-  toPropertyKey,
+  toPropertyDescriptorRecord,
 } from "./object.js";
 
 describe("LazyLoader", () => {
@@ -1245,6 +1247,45 @@ describe("isExtensibleObject", () => {
   });
 });
 
+describe("isPropertyDescriptorRecord", () => {
+  it("[[Call]] returns true for objects created by toPropertyDescriptorRecord", () => {
+    assertStrictEquals(
+      isPropertyDescriptorRecord(toPropertyDescriptorRecord({})),
+      true,
+    );
+  });
+
+  it("[[Get]] returns false for other objects", () => {
+    assertStrictEquals(
+      isPropertyDescriptorRecord(Object.create(null)),
+      false,
+    );
+  });
+
+  it("[[Get]] returns false for undefined", () => {
+    assertStrictEquals(isPropertyDescriptorRecord(undefined), false);
+  });
+
+  it("[[Construct]] throws an error", () => {
+    assertThrows(() => new isPropertyDescriptorRecord({}));
+  });
+
+  describe(".length", () => {
+    it("[[Get]] returns the correct length", () => {
+      assertStrictEquals(isPropertyDescriptorRecord.length, 1);
+    });
+  });
+
+  describe(".name", () => {
+    it("[[Get]] returns the correct name", () => {
+      assertStrictEquals(
+        isPropertyDescriptorRecord.name,
+        "isPropertyDescriptorRecord",
+      );
+    });
+  });
+});
+
 describe("isUnfrozenObject", () => {
   it("[[Call]] returns true for unfrozen objects", () => {
     assertStrictEquals(isUnfrozenObject({}), true);
@@ -1890,43 +1931,274 @@ describe("toObject", () => {
   });
 });
 
-describe("toPropertyKey", () => {
-  it("returns a string or symbol", () => {
-    const sym = Symbol();
-    assertStrictEquals(toPropertyKey(sym), sym);
-    assertStrictEquals(
-      toPropertyKey(new String("success")),
-      "success",
-    );
+describe("toPropertyDescriptorRecord", () => {
+  it("[[Call]] creates a new property descriptor record", () => {
+    const obj = {};
+    const desc = toPropertyDescriptorRecord(obj);
+    assertEquals(obj, desc);
+    assertNotStrictEquals(obj, desc);
   });
 
-  it("favours the `toString` representation", () => {
-    assertStrictEquals(
-      toPropertyKey({
-        toString() {
-          return "success";
-        },
-        valueOf() {
-          return "failure";
-        },
+  it("[[Call]] coerces the values", () => {
+    assertEquals(
+      toPropertyDescriptorRecord({
+        configurable: undefined,
+        enumerable: undefined,
+        writable: undefined,
       }),
-      "success",
+      { configurable: false, enumerable: false, writable: false },
     );
   });
 
+  it("[[Construct]] throws for primitives", () => {
+    assertThrows(() => toPropertyDescriptorRecord(undefined));
+    assertThrows(() => toPropertyDescriptorRecord("failure"));
+  });
+
   it("[[Construct]] throws an error", () => {
-    assertThrows(() => new toPropertyKey(""));
+    assertThrows(() => new toPropertyDescriptorRecord({}));
   });
 
   describe(".length", () => {
     it("[[Get]] returns the correct length", () => {
-      assertStrictEquals(toPropertyKey.length, 1);
+      assertStrictEquals(toPropertyDescriptorRecord.length, 1);
     });
   });
 
   describe(".name", () => {
     it("[[Get]] returns the correct name", () => {
-      assertStrictEquals(toPropertyKey.name, "toPropertyKey");
+      assertStrictEquals(
+        toPropertyDescriptorRecord.name,
+        "toPropertyDescriptorRecord",
+      );
+    });
+  });
+
+  describe("~configurable", () => {
+    it("[[DefineOwnProperty]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "configurable", {});
+      assertStrictEquals(desc.configurable, false);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "configurable", { get: undefined })
+      );
+    });
+
+    it("[[Set]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.configurable = undefined;
+      assertStrictEquals(desc.configurable, false);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ configurable: false });
+      delete desc.configurable;
+      assert(!("configurable" in desc));
+    });
+  });
+
+  describe("~enumerable", () => {
+    it("[[DefineOwnProperty]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "enumerable", {});
+      assertStrictEquals(desc.enumerable, false);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "enumerable", { get: undefined })
+      );
+    });
+
+    it("[[Set]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.enumerable = undefined;
+      assertStrictEquals(desc.enumerable, false);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ enumerable: false });
+      delete desc.enumerable;
+      assert(!("enumerable" in desc));
+    });
+  });
+
+  describe("~get", () => {
+    it("[[DefineOwnProperty]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "get", {});
+      assertStrictEquals(desc.get, undefined);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "get", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(
+        () => Object.defineProperty(desc, "get", { value: null }),
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => Object.defineProperty(desc, "get", {}));
+    });
+
+    it("[[Set]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      const fn = () => {};
+      desc.get = fn;
+      assertStrictEquals(desc.get, fn);
+    });
+
+    it("[[Set]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() => desc.get = null);
+    });
+
+    it("[[Set]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => desc.get = undefined);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      delete desc.get;
+      assert(!("get" in desc));
+    });
+  });
+
+  describe("~set", () => {
+    it("[[DefineOwnProperty]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "set", {});
+      assertStrictEquals(desc.set, undefined);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "set", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(
+        () => Object.defineProperty(desc, "set", { value: null }),
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => Object.defineProperty(desc, "set", {}));
+    });
+
+    it("[[Set]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      const fn = (_) => {};
+      desc.set = fn;
+      assertStrictEquals(desc.set, fn);
+    });
+
+    it("[[Set]] throws if not callable or undefined", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() => desc.set = null);
+    });
+
+    it("[[Set]] throws if a data property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      assertThrows(() => desc.set = undefined);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ set: undefined });
+      delete desc.set;
+      assert(!("set" in desc));
+    });
+  });
+
+  describe("~value", () => {
+    it("[[DefineOwnProperty]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "value", {});
+      assertStrictEquals(desc.value, undefined);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "value", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => Object.defineProperty(desc, "value", {}));
+    });
+
+    it("[[Set]] works", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.value = "success";
+      assertStrictEquals(desc.value, "success");
+    });
+
+    it("[[Set]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => desc.value = null);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ value: undefined });
+      delete desc.value;
+      assert(!("value" in desc));
+    });
+  });
+
+  describe("~writable", () => {
+    it("[[DefineOwnProperty]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      Object.defineProperty(desc, "writable", {});
+      assertStrictEquals(desc.writable, false);
+    });
+
+    it("[[DefineOwnProperty]] throws for accessor properties", () => {
+      const desc = toPropertyDescriptorRecord({});
+      assertThrows(() =>
+        Object.defineProperty(desc, "writable", { get: undefined })
+      );
+    });
+
+    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => Object.defineProperty(desc, "writable", {}));
+    });
+
+    it("[[Set]] coerces to a boolean", () => {
+      const desc = toPropertyDescriptorRecord({});
+      desc.writable = undefined;
+      assertStrictEquals(desc.writable, false);
+    });
+
+    it("[[Set]] throws if an accessor property is defined", () => {
+      const desc = toPropertyDescriptorRecord({ get: undefined });
+      assertThrows(() => desc.writable = false);
+    });
+
+    it("[[Delete]] works", () => {
+      const desc = toPropertyDescriptorRecord({ writable: false });
+      delete desc.writable;
+      assert(!("writable" in desc));
     });
   });
 });
index 1039a3ca9617d7a76b8493360eaf9a5e8456baea..cf858ad2a32bfc1b5e7445aebc67e4ee8a0f1577 100644 (file)
--- a/value.js
+++ b/value.js
@@ -260,256 +260,6 @@ export const isGenericDescriptor = (Desc) =>
   !("get" in Desc || "set" in Desc || "value" in Desc ||
     "writable" in Desc);
 
-export const {
-  /**
-   * Returns whether the provided value is a property descriptor record
-   * as created by `toPropertyDescriptor`.
-   *
-   * ※ This function is provided to enable inspection of whether an
-   * object uses the property descriptor record proxy implementation,
-   * not as a general test of whether an object satisfies the
-   * requirements for property descriptors. In most cases, a more
-   * specific test, like `isAccessorDescriptor`, `isDataDescriptor`, or
-   * `isGenericDescriptor`, is preferrable.
-   */
-  isPropertyDescriptorRecord,
-
-  /**
-   * Converts the provided value to a property descriptor record.
-   *
-   * ※ The prototype of a property descriptor record is always `null`.
-   *
-   * ※ Actually constructing a property descriptor object using this
-   * class is only necessary if you need strict guarantees about the
-   * types of its properties; the resulting object is proxied to ensure
-   * the types match what one would expect from composing
-   * FromPropertyDescriptor and ToPropertyDescriptor in the Ecmascript
-   * specification.
-   */
-  toPropertyDescriptor,
-} = (() => {
-  const {
-    assign: setPropertyValues,
-    create: objectCreate,
-    defineProperty: defineOwnProperty,
-  } = Object;
-  const {
-    apply: call,
-    defineProperty: reflectDefineProperty,
-    setPrototypeOf: reflectSetPrototypeOf,
-  } = Reflect;
-  const proxyConstructor = Proxy;
-  const { add: weakSetAdd, has: weakSetHas } = WeakSet.prototype;
-  const propertyDescriptorRecords = new WeakSet();
-  const coercePropertyDescriptorValue = (P, V) => {
-    switch (P) {
-      case "configurable":
-      case "enumerable":
-      case "writable":
-        return !!V;
-      case "value":
-        return V;
-      case "get":
-        if (V !== undefined && typeof V !== "function") {
-          throw new TypeError(
-            "Piscēs: Getters must be callable.",
-          );
-        } else {
-          return V;
-        }
-      case "set":
-        if (V !== undefined && typeof V !== "function") {
-          throw new TypeError(
-            "Piscēs: Setters must be callable.",
-          );
-        } else {
-          return V;
-        }
-      default:
-        return V;
-    }
-  };
-  const propertyDescriptorProxyHandler = Object.freeze(
-    Object.assign(
-      Object.create(null),
-      {
-        defineProperty(O, P, Desc) {
-          if (
-            P === "configurable" || P === "enumerable" ||
-            P === "writable" || P === "value" ||
-            P === "get" || P === "set"
-          ) {
-            // P is a property descriptor attribute.
-            const desc = setPropertyValues(objectCreate(null), Desc);
-            if ("get" in desc || "set" in desc) {
-              // Desc is an accessor property descriptor.
-              throw new TypeError(
-                "Piscēs: Property descriptor attributes must be data properties.",
-              );
-            } else if ("value" in desc || !(P in O)) {
-              // Desc has a value or P does not already exist on O.
-              desc.value = coercePropertyDescriptorValue(
-                P,
-                desc.value,
-              );
-            } else {
-              // Desc is not an accessor property descriptor and has no
-              // value, but an existing value is present on O.
-              /* do nothing */
-            }
-            const isAccessorDescriptor = "get" === P || "set" === P ||
-              "get" in O || "set" in O;
-            const isDataDescriptor = "value" === P ||
-              "writable" === P ||
-              "value" in O || "writable" in O;
-            if (isAccessorDescriptor && isDataDescriptor) {
-              // Both accessor and data attributes will be present on O
-              // after defining P.
-              throw new TypeError(
-                "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
-              );
-            } else {
-              // P can be safely defined on O.
-              return reflectDefineProperty(O, P, desc);
-            }
-          } else {
-            // P is not a property descriptor attribute.
-            return reflectDefineProperty(O, P, Desc);
-          }
-        },
-        setPrototypeOf(O, V) {
-          if (V !== null) {
-            // V is not the property descriptor prototype.
-            return false;
-          } else {
-            // V is the property descriptor prototype.
-            return reflectSetPrototypeOf(O, V);
-          }
-        },
-      },
-    ),
-  );
-
-  return {
-    isPropertyDescriptorRecord: ($) =>
-      call(weakSetHas, propertyDescriptorRecords, [$]),
-    toPropertyDescriptor: (Obj) => {
-      if (type(Obj) !== "object") {
-        // The provided value is not an object.
-        throw new TypeError(
-          `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
-        );
-      } else {
-        // The provided value is an object.
-        const desc = objectCreate(null);
-        if ("enumerable" in Obj) {
-          // An enumerable property is specified.
-          defineOwnProperty(desc, "enumerable", {
-            configurable: true,
-            enumerable: true,
-            value: !!Obj.enumerable,
-            writable: true,
-          });
-        } else {
-          // An enumerable property is not specified.
-          /* do nothing */
-        }
-        if ("configurable" in Obj) {
-          // A configurable property is specified.
-          defineOwnProperty(desc, "configurable", {
-            configurable: true,
-            enumerable: true,
-            value: !!Obj.configurable,
-            writable: true,
-          });
-        } else {
-          // A configurable property is not specified.
-          /* do nothing */
-        }
-        if ("value" in Obj) {
-          // A value property is specified.
-          defineOwnProperty(desc, "value", {
-            configurable: true,
-            enumerable: true,
-            value: Obj.value,
-            writable: true,
-          });
-        } else {
-          // A value property is not specified.
-          /* do nothing */
-        }
-        if ("writable" in Obj) {
-          // A writable property is specified.
-          defineOwnProperty(desc, "writable", {
-            configurable: true,
-            enumerable: true,
-            value: !!Obj.writable,
-            writable: true,
-          });
-        } else {
-          // A writable property is not specified.
-          /* do nothing */
-        }
-        if ("get" in Obj) {
-          // A get property is specified.
-          const getter = Obj.get;
-          if (getter !== UNDEFINED && typeof getter !== "function") {
-            // The getter is not callable.
-            throw new TypeError("Piscēs: Getters must be callable.");
-          } else {
-            // The getter is callable.
-            defineOwnProperty(desc, "get", {
-              configurable: true,
-              enumerable: true,
-              value: getter,
-              writable: true,
-            });
-          }
-        } else {
-          // A get property is not specified.
-          /* do nothing */
-        }
-        if ("set" in Obj) {
-          // A set property is specified.
-          const setter = Obj.set;
-          if (setter !== UNDEFINED && typeof setter !== "function") {
-            // The setter is not callable.
-            throw new TypeError("Piscēs: Setters must be callable.");
-          } else {
-            // The setter is callable.
-            defineOwnProperty(desc, "set", {
-              configurable: true,
-              enumerable: true,
-              value: setter,
-              writable: true,
-            });
-          }
-        } else {
-          // A set property is not specified.
-          /* do nothing */
-        }
-        if (
-          ("get" in desc || "set" in desc) &&
-          ("value" in desc || "writable" in desc)
-        ) {
-          // Both accessor and data attributes have been defined.
-          throw new TypeError(
-            "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
-          );
-        } else {
-          // The property descriptor is valid.
-          const record = new proxyConstructor(
-            desc,
-            propertyDescriptorProxyHandler,
-          );
-          call(weakSetAdd, propertyDescriptorRecords, [record]);
-          return record;
-        }
-      }
-    },
-  };
-})();
-
 export const {
   /**
    * Returns the primitive value of the provided object per its
@@ -573,20 +323,20 @@ export const {
         "Piscēs: Unable to convert object to primitive",
       );
     },
-    toFunctionName: ($, prefix = undefined) => {
+    toFunctionName: ($, prefix = UNDEFINED) => {
       const key = toPrimitive($, "string");
       const name = (() => {
         if (typeof key === "symbol") {
           // The provided value is a symbol; format its description.
           const description = call(getSymbolDescription, key, []);
-          return description === undefined ? "" : `[${description}]`;
+          return description === UNDEFINED ? "" : `[${description}]`;
         } else {
           // The provided value not a symbol; convert it to a string
           // property key.
           return `${key}`;
         }
       })();
-      return prefix !== undefined ? `${prefix} ${name}` : name;
+      return prefix !== UNDEFINED ? `${prefix} ${name}` : name;
     },
     toPrimitive: ($, preferredType = "default") => {
       const hint = `${preferredType}`;
@@ -600,8 +350,8 @@ export const {
         );
       } else if (type($) === "object") {
         // The provided value is an object.
-        const exoticToPrim = $[TO_PRIMITIVE] ?? undefined;
-        if (exoticToPrim !== undefined) {
+        const exoticToPrim = $[TO_PRIMITIVE] ?? UNDEFINED;
+        if (exoticToPrim !== UNDEFINED) {
           // The provided value has an exotic primitive conversion
           // method.
           if (typeof exoticToPrim !== "function") {
@@ -698,6 +448,15 @@ export const {
   };
 })();
 
+/**
+ * Returns the property key (symbol or string) corresponding to the
+ * provided value.
+ */
+export const toPropertyKey = ($) => {
+  const key = toPrimitive($, "string");
+  return typeof key === "symbol" ? key : `${key}`;
+};
+
 /**
  * Returns a lowercase string identifying the type of the provided
  * value.
index 0f95f22fda345a9841fc695a4c47b6dbaa8649a3..ce6464d07091e19fd89b84b578d7a0ede3cb258f 100644 (file)
@@ -8,9 +8,7 @@
 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
 
 import {
-  assert,
   assertEquals,
-  assertNotStrictEquals,
   assertStrictEquals,
   assertThrows,
   describe,
@@ -25,7 +23,6 @@ import {
   isDataDescriptor,
   isFullyPopulatedDescriptor,
   isGenericDescriptor,
-  isPropertyDescriptorRecord,
   ITERATOR,
   LN10,
   LN2,
@@ -57,7 +54,7 @@ import {
   toIndex,
   toLength,
   toPrimitive,
-  toPropertyDescriptor,
+  toPropertyKey,
   type,
   UNDEFINED,
   UNSCOPABLES,
@@ -505,45 +502,6 @@ describe("isGenericDescriptor", () => {
   });
 });
 
-describe("isPropertyDescriptorRecord", () => {
-  it("[[Call]] returns true for objects created by toPropertyDescriptor", () => {
-    assertStrictEquals(
-      isPropertyDescriptorRecord(toPropertyDescriptor({})),
-      true,
-    );
-  });
-
-  it("[[Get]] returns false for other objects", () => {
-    assertStrictEquals(
-      isPropertyDescriptorRecord(Object.create(null)),
-      false,
-    );
-  });
-
-  it("[[Get]] returns false for undefined", () => {
-    assertStrictEquals(isPropertyDescriptorRecord(undefined), false);
-  });
-
-  it("[[Construct]] throws an error", () => {
-    assertThrows(() => new isPropertyDescriptorRecord({}));
-  });
-
-  describe(".length", () => {
-    it("[[Get]] returns the correct length", () => {
-      assertStrictEquals(isPropertyDescriptorRecord.length, 1);
-    });
-  });
-
-  describe(".name", () => {
-    it("[[Get]] returns the correct name", () => {
-      assertStrictEquals(
-        isPropertyDescriptorRecord.name,
-        "isPropertyDescriptorRecord",
-      );
-    });
-  });
-});
-
 describe("ordinaryToPrimitive", () => {
   it("[[Call]] prefers `valueOf` by default", () => {
     const obj = {
@@ -1034,274 +992,43 @@ describe("toPrimitive", () => {
   });
 });
 
-describe("toPropertyDescriptor", () => {
-  it("[[Call]] creates a new property descriptor record", () => {
-    const obj = {};
-    const desc = toPropertyDescriptor(obj);
-    assertEquals(obj, desc);
-    assertNotStrictEquals(obj, desc);
+describe("toPropertyKey", () => {
+  it("returns a string or symbol", () => {
+    const sym = Symbol();
+    assertStrictEquals(toPropertyKey(sym), sym);
+    assertStrictEquals(
+      toPropertyKey(new String("success")),
+      "success",
+    );
   });
 
-  it("[[Call]] coerces the values", () => {
-    assertEquals(
-      toPropertyDescriptor({
-        configurable: undefined,
-        enumerable: undefined,
-        writable: undefined,
+  it("favours the `toString` representation", () => {
+    assertStrictEquals(
+      toPropertyKey({
+        toString() {
+          return "success";
+        },
+        valueOf() {
+          return "failure";
+        },
       }),
-      { configurable: false, enumerable: false, writable: false },
+      "success",
     );
   });
 
-  it("[[Construct]] throws for primitives", () => {
-    assertThrows(() => toPropertyDescriptor(undefined));
-    assertThrows(() => toPropertyDescriptor("failure"));
-  });
-
   it("[[Construct]] throws an error", () => {
-    assertThrows(() => new toPropertyDescriptor({}));
+    assertThrows(() => new toPropertyKey(""));
   });
 
   describe(".length", () => {
     it("[[Get]] returns the correct length", () => {
-      assertStrictEquals(toPropertyDescriptor.length, 1);
+      assertStrictEquals(toPropertyKey.length, 1);
     });
   });
 
   describe(".name", () => {
     it("[[Get]] returns the correct name", () => {
-      assertStrictEquals(
-        toPropertyDescriptor.name,
-        "toPropertyDescriptor",
-      );
-    });
-  });
-
-  describe("~configurable", () => {
-    it("[[DefineOwnProperty]] coerces to a boolean", () => {
-      const desc = toPropertyDescriptor({});
-      Object.defineProperty(desc, "configurable", {});
-      assertStrictEquals(desc.configurable, false);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "configurable", { get: undefined })
-      );
-    });
-
-    it("[[Set]] coerces to a boolean", () => {
-      const desc = toPropertyDescriptor({});
-      desc.configurable = undefined;
-      assertStrictEquals(desc.configurable, false);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = toPropertyDescriptor({ configurable: false });
-      delete desc.configurable;
-      assert(!("configurable" in desc));
-    });
-  });
-
-  describe("~enumerable", () => {
-    it("[[DefineOwnProperty]] coerces to a boolean", () => {
-      const desc = toPropertyDescriptor({});
-      Object.defineProperty(desc, "enumerable", {});
-      assertStrictEquals(desc.enumerable, false);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "enumerable", { get: undefined })
-      );
-    });
-
-    it("[[Set]] coerces to a boolean", () => {
-      const desc = toPropertyDescriptor({});
-      desc.enumerable = undefined;
-      assertStrictEquals(desc.enumerable, false);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = toPropertyDescriptor({ enumerable: false });
-      delete desc.enumerable;
-      assert(!("enumerable" in desc));
-    });
-  });
-
-  describe("~get", () => {
-    it("[[DefineOwnProperty]] works", () => {
-      const desc = toPropertyDescriptor({});
-      Object.defineProperty(desc, "get", {});
-      assertStrictEquals(desc.get, undefined);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "get", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(
-        () => Object.defineProperty(desc, "get", { value: null }),
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
-      const desc = toPropertyDescriptor({ value: undefined });
-      assertThrows(() => Object.defineProperty(desc, "get", {}));
-    });
-
-    it("[[Set]] works", () => {
-      const desc = toPropertyDescriptor({});
-      const fn = () => {};
-      desc.get = fn;
-      assertStrictEquals(desc.get, fn);
-    });
-
-    it("[[Set]] throws if not callable or undefined", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() => desc.get = null);
-    });
-
-    it("[[Set]] throws if a data property is defined", () => {
-      const desc = toPropertyDescriptor({ value: undefined });
-      assertThrows(() => desc.get = undefined);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = toPropertyDescriptor({ get: undefined });
-      delete desc.get;
-      assert(!("get" in desc));
-    });
-  });
-
-  describe("~set", () => {
-    it("[[DefineOwnProperty]] works", () => {
-      const desc = toPropertyDescriptor({});
-      Object.defineProperty(desc, "set", {});
-      assertStrictEquals(desc.set, undefined);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "set", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if not callable or undefined", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(
-        () => Object.defineProperty(desc, "set", { value: null }),
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if a data property is defined", () => {
-      const desc = toPropertyDescriptor({ value: undefined });
-      assertThrows(() => Object.defineProperty(desc, "set", {}));
-    });
-
-    it("[[Set]] works", () => {
-      const desc = toPropertyDescriptor({});
-      const fn = (_) => {};
-      desc.set = fn;
-      assertStrictEquals(desc.set, fn);
-    });
-
-    it("[[Set]] throws if not callable or undefined", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() => desc.set = null);
-    });
-
-    it("[[Set]] throws if a data property is defined", () => {
-      const desc = toPropertyDescriptor({ value: undefined });
-      assertThrows(() => desc.set = undefined);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = toPropertyDescriptor({ set: undefined });
-      delete desc.set;
-      assert(!("set" in desc));
-    });
-  });
-
-  describe("~value", () => {
-    it("[[DefineOwnProperty]] works", () => {
-      const desc = toPropertyDescriptor({});
-      Object.defineProperty(desc, "value", {});
-      assertStrictEquals(desc.value, undefined);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "value", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
-      const desc = toPropertyDescriptor({ get: undefined });
-      assertThrows(() => Object.defineProperty(desc, "value", {}));
-    });
-
-    it("[[Set]] works", () => {
-      const desc = toPropertyDescriptor({});
-      desc.value = "success";
-      assertStrictEquals(desc.value, "success");
-    });
-
-    it("[[Set]] throws if an accessor property is defined", () => {
-      const desc = toPropertyDescriptor({ get: undefined });
-      assertThrows(() => desc.value = null);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = toPropertyDescriptor({ value: undefined });
-      delete desc.value;
-      assert(!("value" in desc));
-    });
-  });
-
-  describe("~writable", () => {
-    it("[[DefineOwnProperty]] coerces to a boolean", () => {
-      const desc = toPropertyDescriptor({});
-      Object.defineProperty(desc, "writable", {});
-      assertStrictEquals(desc.writable, false);
-    });
-
-    it("[[DefineOwnProperty]] throws for accessor properties", () => {
-      const desc = toPropertyDescriptor({});
-      assertThrows(() =>
-        Object.defineProperty(desc, "writable", { get: undefined })
-      );
-    });
-
-    it("[[DefineOwnProperty]] throws if an accessor property is defined", () => {
-      const desc = toPropertyDescriptor({ get: undefined });
-      assertThrows(() => Object.defineProperty(desc, "writable", {}));
-    });
-
-    it("[[Set]] coerces to a boolean", () => {
-      const desc = toPropertyDescriptor({});
-      desc.writable = undefined;
-      assertStrictEquals(desc.writable, false);
-    });
-
-    it("[[Set]] throws if an accessor property is defined", () => {
-      const desc = toPropertyDescriptor({ get: undefined });
-      assertThrows(() => desc.writable = false);
-    });
-
-    it("[[Delete]] works", () => {
-      const desc = toPropertyDescriptor({ writable: false });
-      delete desc.writable;
-      assert(!("writable" in desc));
+      assertStrictEquals(toPropertyKey.name, "toPropertyKey");
     });
   });
 });
This page took 0.059094 seconds and 4 git commands to generate.