]> Lady’s Gitweb - Pisces/blobdiff - object.js
Add methods for own entries and values to object.js
[Pisces] / object.js
index 578f84f5201f9d46907cc77ba36a30d1c984ddcb..3221e1da360305d4da5fa5582355cee6cf896e03 100644 (file)
--- a/object.js
+++ b/object.js
 // ♓🌟 Piscēs ∷ object.js
 // ====================================================================
 //
-// Copyright © 2022 Lady [@ Lady’s Computer].
+// 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/>.
 
-import { bind, call } from "./function.js";
-import { toPrimitive, type } from "./value.js";
-
-/**
- * A property descriptor object.
- *
- * 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.
- *
- * Otherwise, the instance properties and methods are generic.
- */
-export const { PropertyDescriptor } = (() => {
-  class PropertyDescriptor extends null {
-    /**
-     * Constructs a new property descriptor object from the provided
-     * object.
-     *
-     * The resulting object is proxied to enforce types (for example,
-     * its `enumerable` property, if defined, will always be a
-     * boolean).
-     */
-    //deno-lint-ignore constructor-super
-    constructor(O) {
-      if (type(O) !== "object") {
+import {
+  IS_CONCAT_SPREADABLE,
+  isAccessorDescriptor,
+  isDataDescriptor,
+  SPECIES,
+  toFunctionName,
+  toLength,
+  type,
+  UNDEFINED,
+} from "./value.js";
+
+const createArray = Array;
+const { isArray } = Array;
+const object = Object;
+const {
+  assign,
+  create,
+  defineProperty,
+  defineProperties,
+  entries,
+  freeze: objectFreeze,
+  fromEntries,
+  getOwnPropertyDescriptor: objectGetOwnPropertyDescriptor,
+  getOwnPropertyNames,
+  getOwnPropertySymbols: objectGetOwnPropertySymbols,
+  getPrototypeOf,
+  hasOwn,
+  isExtensible,
+  isFrozen,
+  isSealed,
+  keys,
+  preventExtensions: objectPreventExtensions,
+  seal: objectSeal,
+  setPrototypeOf,
+  values,
+} = Object;
+const {
+  apply: call,
+  deleteProperty,
+  defineProperty: reflectDefineProperty,
+  get,
+  getOwnPropertyDescriptor: reflectGetOwnPropertyDescriptor,
+  has,
+  ownKeys,
+  set,
+  setPrototypeOf: reflectSetPrototypeOf,
+} = Reflect;
+
+/**
+ * An object whose properties are lazy‐loaded from the methods on the
+ * own properties of the provided object.
+ *
+ * This is useful when you are looking to reference properties on
+ * objects which, due to module dependency graphs, cannot be guaranteed
+ * to have been initialized yet.
+ *
+ * The resulting properties will have the same attributes (regarding
+ * configurability, enumerability, and writability) as the
+ * corresponding properties on the methods object. If a property is
+ * marked as writable, the method will never be called if it is set
+ * before it is gotten. By necessity, the resulting properties are all
+ * configurable before they are accessed for the first time.
+ *
+ * Methods will be called with the resulting object as their this
+ * value.
+ *
+ * `LazyLoader` objects have the same prototype as the passed methods
+ * object.
+ */
+export class LazyLoader extends null {
+  /**
+   * Constructs a new `LazyLoader` object.
+   *
+   * ☡ This function throws if the provided value is not an object.
+   */
+  constructor(loadMethods) {
+    if (type(loadMethods) !== "object") {
+      // The provided value is not an object; throw an error.
+      throw new TypeError(
+        `Piscēs: Cannot construct LazyLoader: Provided argument is not an object: ${loadMethods}.`,
+      );
+    } else {
+      // The provided value is an object; process it and build the
+      // result.
+      const result = create(getPrototypeOf(loadMethods));
+      const methodKeys = ownKeys(loadMethods);
+      for (let index = 0; index < methodKeys.length; ++index) {
+        // Iterate over the property keys of the provided object and
+        // define getters and setters appropriately on the result.
+        const methodKey = methodKeys[index];
+        const { configurable, enumerable, writable } =
+          getOwnPropertyDescriptor(loadMethods, methodKey);
+        defineProperty(
+          result,
+          methodKey,
+          assign(create(null), {
+            configurable: true,
+            enumerable,
+            get: defineProperty(
+              () => {
+                const value = call(loadMethods[methodKey], result, []);
+                defineProperty(
+                  result,
+                  methodKey,
+                  assign(create(null), {
+                    configurable,
+                    enumerable,
+                    value,
+                    writable,
+                  }),
+                );
+                return value;
+              },
+              "name",
+              assign(create(null), {
+                value: toFunctionName(methodKey, "get"),
+              }),
+            ),
+            set: writable
+              ? defineProperty(
+                ($) =>
+                  defineProperty(
+                    result,
+                    methodKey,
+                    assign(create(null), {
+                      configurable,
+                      enumerable,
+                      value: $,
+                      writable,
+                    }),
+                  ),
+                "name",
+                assign(create(null), {
+                  value: toFunctionName(methodKey, "set"),
+                }),
+              )
+              : UNDEFINED,
+          }),
+        );
+      }
+      return result;
+    }
+  }
+}
+
+/**
+ * Defines an own enumerable data property on the provided object with
+ * the provided property key and value.
+ */
+export const defineOwnDataProperty = (O, P, V) =>
+  defineProperty(
+    O,
+    P,
+    assign(create(null), {
+      configurable: true,
+      enumerable: true,
+      value: V,
+      writable: true,
+    }),
+  );
+
+/**
+ * Defines an own nonenumerable data property on the provided object
+ * with the provided property key and value.
+ */
+export const defineOwnNonenumerableDataProperty = (O, P, V) =>
+  defineProperty(
+    O,
+    P,
+    assign(create(null), {
+      configurable: true,
+      enumerable: false,
+      value: V,
+      writable: true,
+    }),
+  );
+
+/**
+ * Defines an own property on the provided object on the provided
+ * property key using the provided property descriptor.
+ *
+ * ※ This is effectively an alias for `Object.defineProperty`.
+ */
+export const defineOwnProperty = (O, P, Desc) =>
+  defineProperty(O, P, Desc);
+
+/**
+ * Defines own properties on the provided object using the descriptors
+ * on the enumerable own properties of the provided additional objects.
+ *
+ * ※ This differs from `Object.defineProperties` in that it can take
+ * multiple source objects.
+ */
+export const defineOwnProperties = (O, ...sources) => {
+  const { length } = sources;
+  for (let k = 0; k < length; ++k) {
+    defineProperties(O, sources[k]);
+  }
+  return O;
+};
+
+/**
+ * Removes the provided property key from the provided object and
+ * returns the object.
+ *
+ * ※ This function differs from `Reflect.deleteProperty` and the
+ * `delete` operator in that it throws if the deletion is
+ * unsuccessful.
+ *
+ * ☡ This function throws if the first argument is not an object.
+ */
+export const deleteOwnProperty = (O, P) => {
+  if (type(O) !== "object") {
+    throw new TypeError(
+      `Piscēs: Tried to set property but provided value was not an object: ${V}`,
+    );
+  } else if (!deleteProperty(O, P)) {
+    throw new TypeError(
+      `Piscēs: Tried to delete property from object but [[Delete]] returned false: ${P}`,
+    );
+  } else {
+    return O;
+  }
+};
+
+/**
+ * Marks the provided object as non·extensible and marks all its
+ * properties as nonconfigurable and (if data properties) nonwritable,
+ * and returns the object.
+ *
+ * ※ This is effectively an alias for `Object.freeze`.
+ */
+export const freeze = (O) => objectFreeze(O);
+
+/**
+ * Returns a new frozen shallow copy of the enumerable own properties
+ * of the provided object, according to the following rules :—
+ *
+ * - For data properties, create a nonconfigurable, nonwritable
+ *   property with the same value.
+ *
+ * - For accessor properties, create a nonconfigurable accessor
+ *   property with the same getter *and* setter.
+ *
+ * The prototype for the resulting object will be taken from the
+ * `.prototype` property of the provided constructor, or the
+ * `.prototype` of the `.constructor` of the provided object if the
+ * provided constructor is undefined. If the used constructor has a
+ * nonnullish `.[Symbol.species]`, that will be used instead. If the
+ * used constructor or species is nullish or does not have a
+ * `.prototype` property, the prototype is set to null.
+ *
+ * ※ The prototype of the provided object itself is ignored.
+ */
+export const frozenCopy = (O, constructor = O?.constructor) => {
+  if (O == null) {
+    // O is null or undefined.
+    throw new TypeError(
+      "Piscēs: Cannot copy properties of null or undefined.",
+    );
+  } else {
+    // O is not null or undefined.
+    //
+    // (If not provided, the constructor will be the value of getting
+    // the `.constructor` property of O.)
+    const species = constructor?.[SPECIES] ?? constructor;
+    const copy = create(
+      species == null || !("prototype" in species)
+        ? null
+        : species.prototype,
+    );
+    const keys = ownKeys(O);
+    for (let k = 0; k < keys.length; ++k) {
+      const P = keys[k];
+      const Desc = getOwnPropertyDescriptor(O, P);
+      if (Desc.enumerable) {
+        // P is an enumerable property.
+        defineProperty(
+          copy,
+          P,
+          assign(
+            create(null),
+            isAccessorDescriptor(Desc)
+              ? {
+                configurable: false,
+                enumerable: true,
+                get: Desc.get,
+                set: Desc.set,
+              }
+              : {
+                configurable: false,
+                enumerable: true,
+                value: Desc.value,
+                writable: false,
+              },
+          ),
+        );
+      } else {
+        // P is not an enumerable property.
+        /* do nothing */
+      }
+    }
+    return objectPreventExtensions(copy);
+  }
+};
+
+/**
+ * Returns the function on the provided value at the provided property
+ * key.
+ *
+ * ☡ This function throws if the provided property key does not have an
+ * associated value which is callable.
+ */
+export const getMethod = (V, P) => {
+  const func = V[P];
+  if (func == null) {
+    return undefined;
+  } else if (typeof func !== "function") {
+    throw new TypeError(`Piscēs: Method not callable: ${P}`);
+  } else {
+    return func;
+  }
+};
+
+/**
+ * Returns the property descriptor record for the own property with the
+ * provided property key on the provided value, or null if none exists.
+ *
+ * ※ This is effectively an alias for
+ * `Object.getOwnPropertyDescriptor`, but the return value is a proxied
+ * object with null prototype.
+ */
+export const getOwnPropertyDescriptor = (O, P) => {
+  const desc = objectGetOwnPropertyDescriptor(O, P);
+  return desc === UNDEFINED
+    ? UNDEFINED
+    : toPropertyDescriptorRecord(desc);
+};
+
+/**
+ * Returns the property descriptors for the own properties on the
+ * provided value.
+ *
+ * ※ This is effectively an alias for
+ * `Object.getOwnPropertyDescriptors`, but the values on the resulting
+ * object are proxied objects with null prototypes.
+ */
+export const getOwnPropertyDescriptors = (O) => {
+  const obj = toObject(O);
+  const keys = ownKeys(obj);
+  const descriptors = {};
+  for (let k = 0; k < keys.length; ++k) {
+    // Iterate over the keys of the provided object and collect its
+    // descriptors.
+    const key = keys[k];
+    defineOwnDataProperty(
+      descriptors,
+      key,
+      getOwnPropertyDescriptor(O, key),
+    );
+  }
+  return descriptors;
+};
+
+/**
+ * Returns an array of own property entries on the provided value,
+ * using the provided receiver if given.
+ */
+export const getOwnPropertyEntries = (O, Receiver = O) => {
+  const obj = toObject(O);
+  const keys = ownKeys(obj);
+  const target = Receiver === UNDEFINED ? obj : toObject(Receiver);
+  const result = createArray(keys.length);
+  for (let k = 0; k < keys.length; ++k) {
+    // Iterate over each key and add the corresponding entry to the
+    // result.
+    const key = keys[k];
+    defineOwnDataProperty(
+      result,
+      k,
+      [key, getOwnPropertyValue(obj, keys[k], target)],
+    );
+  }
+  return result;
+};
+
+/**
+ * Returns an array of own property keys on the provided value.
+ *
+ * ※ This is effectively an alias for `Reflect.ownKeys`, except that
+ * it does not require that the argument be an object.
+ */
+export const getOwnPropertyKeys = (O) => ownKeys(toObject(O));
+
+/**
+ * Returns an array of string‐valued own property keys on the
+ * provided value.
+ *
+ * ☡ This includes both enumerable and non·enumerable properties.
+ *
+ * ※ This is effectively an alias for `Object.getOwnPropertyNames`.
+ */
+export const getOwnPropertyStrings = (O) => getOwnPropertyNames(O);
+
+/**
+ * Returns an array of symbol‐valued own property keys on the
+ * provided value.
+ *
+ * ☡ This includes both enumerable and non·enumerable properties.
+ *
+ * ※ This is effectively an alias for
+ * `Object.getOwnPropertySymbols`.
+ */
+export const getOwnPropertySymbols = (O) =>
+  objectGetOwnPropertySymbols(O);
+
+/**
+ * Returns the value of the provided own property on the provided
+ * value using the provided receiver, or undefined if the provided
+ * property is not an own property on the provided value.
+ *
+ * ※ If the receiver is not provided, it defaults to the provided
+ * value.
+ */
+export const getOwnPropertyValue = (O, P, Receiver = UNDEFINED) => {
+  const obj = toObject(O);
+  const desc = getOwnPropertyDescriptor(O, P);
+  if (desc === UNDEFINED) {
+    // The provided property is not an own property on the provided
+    // value; return undefined.
+    return UNDEFINED;
+  }
+  if (isDataDescriptor(desc)) {
+    // The provided property is a data property; return its value.
+    return desc.value;
+  } else {
+    // The provided property is an accessor property; get its value
+    // using the appropriate receiver.
+    const target = Receiver === UNDEFINED ? obj : toObject(Receiver);
+    return call(desc.get, target, []);
+  }
+};
+
+/**
+ * Returns an array of own property values on the provided value, using
+ * the provided receiver if given.
+ */
+export const getOwnPropertyValues = (O, Receiver = O) => {
+  const obj = toObject(O);
+  const keys = ownKeys(obj);
+  const target = Receiver === UNDEFINED ? obj : toObject(Receiver);
+  const result = createArray(keys.length);
+  for (let k = 0; k < keys.length; ++k) {
+    // Iterate over each key and collect the values.
+    defineOwnDataProperty(
+      result,
+      k,
+      getOwnPropertyValue(obj, keys[k], target),
+    );
+  }
+  return result;
+};
+
+/**
+ * Returns the value of the provided property key on the provided
+ * value.
+ *
+ * ※ This is effectively an alias for `Reflect.get`, except that it
+ * does not require that the argument be an object.
+ */
+export const getPropertyValue = (O, P, Receiver = O) =>
+  get(toObject(O), P, Receiver);
+
+/**
+ * Returns the prototype of the provided value.
+ *
+ * ※ This is effectively an alias for `Object.getPrototypeOf`.
+ */
+export const getPrototype = (O) => getPrototypeOf(O);
+
+/**
+ * Returns whether the provided value has an own property with the
+ * provided property key.
+ *
+ * ※ This is effectively an alias for `Object.hasOwn`.
+ */
+export const hasOwnProperty = (O, P) => hasOwn(O, P);
+
+/**
+ * Returns whether the provided property key exists on the provided
+ * value.
+ *
+ * ※ This is effectively an alias for `Reflect.has`, except that it
+ * does not require that the argument be an object.
+ *
+ * ※ This includes properties present on the prototype chain.
+ */
+export const hasProperty = (O, P) => has(toObject(O), P);
+
+/** Returns whether the provided value is an arraylike object. */
+export const isArraylikeObject = ($) => {
+  if (type($) !== "object") {
+    return false;
+  } else {
+    try {
+      lengthOfArraylike($); // throws if not arraylike
+      return true;
+    } catch {
+      return false;
+    }
+  }
+};
+
+/**
+ * Returns whether the provided value is spreadable during array
+ * concatenation.
+ *
+ * This is also used to determine which things should be treated as
+ * collections.
+ */
+export const isConcatSpreadableObject = ($) => {
+  if (type($) !== "object") {
+    // The provided value is not an object.
+    return false;
+  } else {
+    // The provided value is an object.
+    const spreadable = $[IS_CONCAT_SPREADABLE];
+    return spreadable !== UNDEFINED ? !!spreadable : isArray($);
+  }
+};
+
+/**
+ * Returns whether the provided value is an extensible object.
+ *
+ * ※ This function returns false for nonobjects.
+ *
+ * ※ This is effectively an alias for `Object.isExtensible`.
+ */
+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.",
+          `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
         );
       } else {
         // The provided value is an object.
-        const desc = objectCreate(propertyDescriptorPrototype);
-        if ("enumerable" in O) {
+        const desc = create(null);
+        if ("enumerable" in Obj) {
           // An enumerable property is specified.
-          desc.enumerable = !!O.enumerable;
+          defineOwnDataProperty(desc, "enumerable", !!Obj.enumerable);
         } else {
           // An enumerable property is not specified.
           /* do nothing */
         }
-        if ("configurable" in O) {
+        if ("configurable" in Obj) {
           // A configurable property is specified.
-          desc.configurable = !!O.configurable;
+          defineOwnDataProperty(
+            desc,
+            "configurable",
+            !!Obj.configurable,
+          );
         } else {
           // A configurable property is not specified.
           /* do nothing */
         }
-        if ("value" in O) {
+        if ("value" in Obj) {
           // A value property is specified.
-          desc.value = O.value;
+          defineOwnDataProperty(desc, "value", Obj.value);
         } else {
           // A value property is not specified.
           /* do nothing */
         }
-        if ("writable" in O) {
+        if ("writable" in Obj) {
           // A writable property is specified.
-          desc.writable = !!O.writable;
+          defineOwnDataProperty(desc, "writable", !!Obj.writable);
         } else {
           // A writable property is not specified.
           /* do nothing */
         }
-        if ("get" in O) {
+        if ("get" in Obj) {
           // A get property is specified.
-          const getter = O.get;
-          if (getter !== undefined && typeof getter !== "function") {
+          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.
-            desc.get = getter;
+            defineOwnDataProperty(desc, "get", Obj.get);
           }
         } else {
           // A get property is not specified.
           /* do nothing */
         }
-        if ("set" in O) {
+        if ("set" in Obj) {
           // A set property is specified.
-          const setter = O.set;
-          if (setter !== undefined && typeof setter !== "function") {
+          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.
-            desc.set = setter;
+            defineOwnDataProperty(desc, "set", Obj.set);
           }
         } else {
           // A set property is not specified.
@@ -107,602 +749,204 @@ export const { PropertyDescriptor } = (() => {
           );
         } else {
           // The property descriptor is valid.
-          return new Proxy(desc, propertyDescriptorProxyHandler);
-        }
-      }
-    }
-
-    /**
-     * Completes this property descriptor by setting missing values to
-     * their defaults.
-     *
-     * This method modifies this object and returns undefined.
-     */
-    complete() {
-      if (this !== undefined && !("get" in this || "set" in this)) {
-        // This is a generic or data descriptor.
-        if (!("value" in this)) {
-          // `value` is not defined on this.
-          this.value = undefined;
-        } else {
-          // `value` is already defined on this.
-          /* do nothing */
-        }
-        if (!("writable" in this)) {
-          // `writable` is not defined on this.
-          this.writable = false;
-        } else {
-          // `writable` is already defined on this.
-          /* do nothing */
-        }
-      } else {
-        // This is not a generic or data descriptor.
-        if (!("get" in this)) {
-          // `get` is not defined on this.
-          this.get = undefined;
-        } else {
-          // `get` is already defined on this.
-          /* do nothing */
-        }
-        if (!("set" in this)) {
-          // `set` is not defined on this.
-          this.set = undefined;
-        } else {
-          // `set` is already defined on this.
-          /* do nothing */
-        }
-      }
-      if (!("enumerable" in this)) {
-        // `enumerable` is not defined on this.
-        this.enumerable = false;
-      } else {
-        // `enumerable` is already defined on this.
-        /* do nothing */
-      }
-      if (!("configurable" in this)) {
-        // `configurable` is not defined on this.
-        this.configurable = false;
-      } else {
-        // `configurable` is already defined on this.
-        /* do nothing */
-      }
-    }
-
-    /** Gets whether this is an accessor descrtiptor. */
-    get isAccessorDescriptor() {
-      return this !== undefined && ("get" in this || "set" in this);
-    }
-
-    /** Gets whether this is a data descrtiptor. */
-    get isDataDescriptor() {
-      return this !== undefined &&
-        ("value" in this || "writable" in this);
-    }
-
-    /** Gets whether this is a fully‐populated property descriptor. */
-    get isFullyPopulated() {
-      return this !== undefined &&
-        ("value" in this && "writable" in this ||
-          "get" in this && "set" in this) &&
-        "enumerable" in this && "configurable" in this;
-    }
-
-    /**
-     * Gets whether this is a generic (not accessor or data)
-     * descrtiptor.
-     */
-    get isGenericDescriptor() {
-      return this !== undefined &&
-        !("get" in this || "set" in this || "value" in this ||
-          "writable" in this);
-    }
-  }
-
-  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.",
+          const record = new proxyConstructor(
+            desc,
+            propertyDescriptorProxyHandler,
           );
-        } 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 {
-    prototype: propertyDescriptorPrototype,
-  } = PropertyDescriptor;
-
-  const propertyDescriptorProxyHandler = 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 = new PropertyDescriptor(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.
-            /* 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 defineOwnProperty(O, P, desc);
-          }
-        } else {
-          // P is not a property descriptor attribute.
-          return defineOwnProperty(O, P, Desc);
-        }
-      },
-      set(O, P, V, Receiver) {
-        if (
-          P === "configurable" || P === "enumerable" ||
-          P === "writable" || P === "value" ||
-          P === "get" || P === "set"
-        ) {
-          // P is a property descriptor attribute.
-          const newValue = coercePropertyDescriptorValue(P, V);
-          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.
-            //
-            // ☡ Receiver will be the *proxied* object, so passing it
-            // through to setPropertyValue here would produce an
-            // infinite loop.
-            //
-            // ☡ This has implications on objects with a proxied
-            // PropertyDescriptor in their prototype.
-            return setPropertyValue(O, P, newValue, O);
-          }
-        } else {
-          return setPropertyValue(O, P, V, Receiver);
-        }
-      },
-      setPrototypeOf(O, V) {
-        if (V !== propertyDescriptorPrototype) {
-          // V is not the property descriptor prototype.
-          return false;
-        } else {
-          // V is the property descriptor prototype.
-          return setPrototype(O, V);
+          call(weakSetAdd, propertyDescriptorRecords, [record]);
+          return record;
         }
-      },
-    },
-  );
-
-  return { PropertyDescriptor };
-})();
-
-export const {
-  /**
-   * Defines own properties on the provided object using the
-   * descriptors on the enumerable own properties of the provided
-   * additional objects.
-   *
-   * ※ This differs from Object.defineProperties in that it can take
-   * multiple source objects.
-   */
-  defineOwnProperties,
-} = (() => {
-  const { defineProperties } = Object;
-  const { forEach: arrayForEach } = Array.prototype;
-  return {
-    defineOwnProperties: (O, ...sources) => {
-      call(
-        arrayForEach,
-        sources,
-        [(source) => defineProperties(O, source)],
-      );
-      return O;
+      }
     },
   };
 })();
 
-export const {
-  /**
-   * Defines an own property on the provided object on the provided
-   * property key using the provided property descriptor.
-   *
-   * ※ This is an alias for Object.defineProperty.
-   */
-  defineProperty: defineOwnProperty,
-
-  /**
-   * Marks the provided object as non·extensible and marks all its
-   * properties as nonconfigurable and (if data properties)
-   * nonwritable, and returns the object.
-   *
-   * ※ This is an alias for Object.freeze.
-   */
-  freeze,
-
-  /**
-   * Returns the property descriptor for the own property with the
-   * provided property key on the provided object, or null if none
-   * exists.
-   *
-   * ※ This is an alias for Object.getOwnPropertyDescriptor.
-   */
-  getOwnPropertyDescriptor,
-
-  /**
-   * Returns the property descriptors for the own properties on the
-   * provided object.
-   *
-   * ※ This is an alias for Object.getOwnPropertyDescriptors.
-   */
-  getOwnPropertyDescriptors,
-
-  /**
-   * Returns an array of string‐valued own property keys on the
-   * provided object.
-   *
-   * ☡ This includes both enumerable and non·enumerable properties.
-   *
-   * ※ This is an alias for Object.getOwnPropertyNames.
-   */
-  getOwnPropertyNames: getOwnPropertyStrings,
-
-  /**
-   * Returns an array of symbol‐valued own property keys on the
-   * provided object.
-   *
-   * ☡ This includes both enumerable and non·enumerable properties.
-   *
-   * ※ This is an alias for Object.getOwnPropertySymbols.
-   */
-  getOwnPropertySymbols,
-
-  /**
-   * Returns the prototype of the provided object.
-   *
-   * ※ This is an alias for Object.getPrototypeOf.
-   */
-  getPrototypeOf: getPrototype,
-
-  /**
-   * Returns whether the provided object has an own property with the
-   * provided property key.
-   *
-   * ※ This is an alias for Object.hasOwn.
-   */
-  hasOwn: hasOwnProperty,
-
-  /**
-   * Returns whether the provided object is extensible.
-   *
-   * ※ This is an alias for Object.isExtensible.
-   */
-  isExtensible,
-
-  /**
-   * Returns whether the provided object is frozen.
-   *
-   * ※ This is an alias for Object.isFrozen.
-   */
-  isFrozen,
-
-  /**
-   * Returns whether the provided object is sealed.
-   *
-   * ※ This is an alias for Object.isSealed.
-   */
-  isSealed,
-
-  /**
-   * Returns an array of key~value pairs for the enumerable,
-   * string‐valued property keys on the provided object.
-   *
-   * ※ This is an alias for Object.entries.
-   */
-  entries: namedEntries,
-
-  /**
-   * Returns an array of the enumerable, string‐valued property keys on
-   * the provided object.
-   *
-   * ※ This is an alias for Object.keys.
-   */
-  keys: namedKeys,
+/**
+ * Returns whether the provided value is an unfrozen object.
+ *
+ * ※ This function returns false for nonobjects.
+ *
+ * ※ This is effectively an alias for `!Object.isFrozen`.
+ */
+export const isUnfrozenObject = (O) => !isFrozen(O);
 
-  /**
-   * Returns an array of property values for the enumerable,
-   * string‐valued property keys on the provided object.
-   *
-   * ※ This is an alias for Object.values.
-   */
-  values: namedValues,
+/**
+ * Returns whether the provided value is an unsealed object.
+ *
+ * ※ This function returns false for nonobjects.
+ *
+ * ※ This is effectively an alias for `!Object.isSealed`.
+ */
+export const isUnsealedObject = (O) => !isSealed(O);
 
-  /**
-   * Returns a new object with the provided prototype and property
-   * descriptors.
-   *
-   * ※ This is an alias for Object.create.
-   */
-  create: objectCreate,
+/**
+ * Returns the length of the provided arraylike value.
+ *
+ * This can produce larger lengths than can actually be stored in
+ * arrays, because no such restrictions exist on arraylike methods.
+ *
+ * ☡ This function throws if the provided value is not arraylike.
+ */
+export const lengthOfArraylike = ({ length }) => toLength(length);
 
-  /**
-   * Returns a new object with the provided property keys and values.
-   *
-   * ※ This is an alias for Object.fromEntries.
-   */
-  fromEntries: objectFromEntries,
+/**
+ * Returns an array of key~value pairs for the enumerable,
+ * string‐valued property keys on the provided value.
+ *
+ * ※ This is effectively an alias for `Object.entries`.
+ */
+export const namedEntries = (O) => entries(O);
 
-  /**
-   * Marks the provided object as non·extensible, and returns the
  * object.
  *
  * ※ This is an alias for Object.preventExtensions.
  */
-  preventExtensions,
+/**
+ * Returns an array of the enumerable, string‐valued property keys on
* the provided value.
+ *
* ※ This is effectively an alias for `Object.keys`.
+ */
+export const namedKeys = (O) => keys(O);
 
-  /**
-   * Marks the provided object as non·extensible and marks all its
  * properties as nonconfigurable, and returns the object.
  *
  * ※ This is an alias for Object.seal.
  */
-  seal,
+/**
+ * Returns an array of property values for the enumerable,
* string‐valued property keys on the provided value.
+ *
* ※ This is effectively an alias for `Object.values`.
+ */
+export const namedValues = (O) => values(O);
 
-  /**
-   * Sets the values of the enumerable own properties of the provided
  * additional objects on the provided object.
  *
  * ※ This is an alias for Object.assign.
  */
-  assign: setPropertyValues,
+/**
+ * Returns a new object with the provided prototype and property
* descriptors.
+ *
* ※ This is effectively an alias for `Object.create`.
+ */
+export const objectCreate = (O, Properties) => create(O, Properties);
 
-  /**
-   * Sets the prototype of the provided object to the provided value
-   * and returns the object.
-   *
-   * ※ This is an alias for Object.setPrototypeOf.
-   */
-  setPrototypeOf: setPrototype,
-} = Object;
+/**
+ * Returns a new object with property keys and values from the provided
+ * iterable value.
+ *
+ * ※ This is effectively an alias for `Object.fromEntries`.
+ */
+export const objectFromEntries = (iterable) => fromEntries(iterable);
 
-export const {
-  /**
-   * Removes the provided property key from the provided object and
-   * returns the object.
-   *
-   * ☡ This function differs from Reflect.deleteProperty and the
-   * `delete` operator in that it throws if the deletion is
-   * unsuccessful.
-   */
-  deleteOwnProperty,
+/**
+ * Marks the provided object as non·extensible, and returns the
+ * object.
+ *
+ * ※ This is effectively an alias for `Object.preventExtensions`.
+ */
+export const preventExtensions = (O) => objectPreventExtensions(O);
 
-  /**
-   * Sets the provided property key to the provided value on the
-   * provided object and returns the object.
-   *
-   * ※ This function differs from Reflect.set in that it throws if the
-   * setting is unsuccessful.
-   */
-  setPropertyValue,
-} = (() => {
-  const { deleteProperty, set } = Reflect;
+/**
+ * Marks the provided object as non·extensible and marks all its
+ * properties as nonconfigurable, and returns the object.
+ *
+ * ※ This is effectively an alias for `Object.seal`.
+ */
+export const seal = (O) => objectSeal(O);
 
-  return {
-    deleteOwnProperty: (O, P) => {
-      if (!deleteProperty(O, P)) {
-        throw new TypeError(
-          `Piscēs: Tried to delete property from object but [[Delete]] returned false: ${P}`,
-        );
-      } else {
-        return O;
-      }
-    },
-    setPropertyValue: (O, P, V, Receiver = O) => {
-      if (!set(O, P, V, Receiver)) {
-        throw new TypeError(
-          `Piscēs: Tried to set property on object but [[Set]] returned false: ${P}`,
-        );
-      } else {
-        return O;
-      }
-    },
-  };
-})();
+/**
+ * Sets the provided property key to the provided value on the provided
+ * object and returns the object.
+ *
+ * ※ This function differs from `Reflect.set` in that it throws if the
+ * setting is unsuccessful.
+ *
+ * ☡ This function throws if the first argument is not an object.
+ */
+export const setPropertyValue = (O, P, V, Receiver = O) => {
+  if (type(O) !== "object") {
+    throw new TypeError(
+      `Piscēs: Tried to set property but provided value was not an object: ${V}`,
+    );
+  } else if (!set(O, P, V, Receiver)) {
+    throw new TypeError(
+      `Piscēs: Tried to set property on object but [[Set]] returned false: ${P}`,
+    );
+  } else {
+    return O;
+  }
+};
 
-export const {
-  /**
-   * Returns a new frozen shallow copy of the enumerable own properties
-   * of the provided object, according to the following rules :—
-   *
-   * - For data properties, create a nonconfigurable, nonwritable
-   *   property with the same value.
-   *
-   * - For accessor properties, create a nonconfigurable accessor
-   *   property with the same getter *and* setter.
-   *
-   * The prototype for the resulting object will be taken from the
-   * `prototype` property of the provided constructor, or the
-   * `prototype` of the `constructor` of the provided object if the
-   * provided constructor is undefined. If the used constructor has a
-   * nonnullish `Symbol.species`, that will be used instead. If the
-   * used constructor or species is nullish or does not have a
-   * `prototype` property, the prototype is set to null.
-   */
-  frozenCopy,
-} = (() => {
-  const {
-    iterator: iteratorSymbol,
-    species: speciesSymbol,
-  } = Symbol;
-  const {
-    next: generatorIteratorNext,
-  } = getPrototype(function* () {}.prototype);
-  const propertyDescriptorEntryIterablePrototype = {
-    [iteratorSymbol]() {
-      return {
-        next: bind(generatorIteratorNext, this.generator(), []),
-      };
-    },
-  };
-  return {
-    frozenCopy: (O, constructor = O?.constructor) => {
-      if (O == null) {
-        // O is null or undefined.
-        throw new TypeError(
-          "Piscēs: Cannot copy properties of null or undefined.",
-        );
-      } else {
-        // O is not null or undefined.
-        //
-        // (If not provided, the constructor will be the value of
-        // getting the `constructor` property of O.)
-        const species = constructor?.[speciesSymbol] ?? constructor;
-        return preventExtensions(
-          objectCreate(
-            species == null || !("prototype" in species)
-              ? null
-              : species.prototype,
-            objectFromEntries(
-              objectCreate(
-                propertyDescriptorEntryIterablePrototype,
-                {
-                  generator: {
-                    value: function* () {
-                      const ownPropertyKeys = getOwnPropertyKeys(O);
-                      for (
-                        let i = 0;
-                        i < ownPropertyKeys.length;
-                        ++i
-                      ) {
-                        const P = ownPropertyKeys[i];
-                        const Desc = getOwnPropertyDescriptor(O, P);
-                        if (Desc.enumerable) {
-                          // P is an enumerable property.
-                          yield [
-                            P,
-                            "get" in Desc || "set" in Desc
-                              ? {
-                                configurable: false,
-                                enumerable: true,
-                                get: Desc.get,
-                                set: Desc.set,
-                              }
-                              : {
-                                configurable: false,
-                                enumerable: true,
-                                value: Desc.value,
-                                writable: false,
-                              },
-                          ];
-                        } else {
-                          // P is not an enumerable property.
-                          /* do nothing */
-                        }
-                      }
-                    },
-                  },
-                },
-              ),
-            ),
-          ),
-        );
+/**
+ * Sets the values of the enumerable own properties of the provided
+ * additional objects on the provided values.
+ *
+ * ※ This is effectively an alias for `Object.assign`.
+ */
+export const setPropertyValues = (target, source, ...sources) => {
+  const to = toObject(target);
+  for (let i = -1; i < sources.length; ++i) {
+    // Iterate over each source and set the appropriate property
+    // values.
+    const nextSource = i === -1 ? source : sources[i];
+    if (nextSource != null) {
+      // The current source is not nullish; handle its own properties.
+      const from = toObject(nextSource);
+      const keys = ownKeys(from);
+      for (let k = 0; k < keys.length; ++k) {
+        // Iterate over each key in the current source and set it in
+        // the target object if it is enumerable.
+        const nextKey = keys[k];
+        const desc = reflectGetOwnPropertyDescriptor(from, nextKey);
+        if (desc !== UNDEFINED && desc.enumerable) {
+          // The current key is present and enumerable; set it to its
+          // corresponding value.
+          const propValue = from[nextKey];
+          to[nextKey] = propValue;
+        } else {
+          // The current key is not present or not enumerable.
+          /* do nothing */
+        }
       }
-    },
-  };
-})();
-
-export const {
-  /**
-   * Returns an array of property keys on the provided object.
-   *
-   * ※ This is an alias for Reflect.ownKeys.
-   */
-  ownKeys: getOwnPropertyKeys,
-
-  /**
-   * Returns the value of the provided property key on the provided
-   * object.
-   *
-   * ※ This is an alias for Reflect.get.
-   */
-  get: getPropertyValue,
-
-  /**
-   * Returns whether the provided property key exists on the provided
-   * object.
-   *
-   * ※ This is an alias for Reflect.has.
-   *
-   * ※ This includes properties present on the prototype chain.
-   */
-  has: hasProperty,
-} = Reflect;
+    } else {
+      // The current source is nullish.
+      /* do nothing */
+    }
+  }
+  return to;
+};
 
 /**
- * Returns the provided value converted to an object.
+ * Sets the prototype of the provided object to the provided value and
+ * returns the object.
  *
- * Null and undefined are converted to a new, empty object. Other
- * primitives are wrapped. Existing objects are returned with no
- * modification.
- *
- * ※ This is effectively a nonconstructible version of the Object
- * constructor.
+ * ※ This is effectively an alias for `Object.setPrototypeOf`, but it
+ * won’t throw when setting the prototype of a primitive to its current
+ * value.
  */
-export const { toObject } = (() => {
-  const makeObject = Object;
-  return { toObject: ($) => makeObject($) };
-})();
+export const setPrototype = (O, proto) => {
+  const obj = toObject(O);
+  if (O === obj) {
+    // The provided value is an object; set its prototype normally.
+    return setPrototypeOf(O, proto);
+  } else {
+    // The provided value is not an object; attempt to set the
+    // prototype on a coerced version with extensions prevented, then
+    // return the provided value.
+    //
+    // This will throw if the given prototype does not match the
+    // existing one on the coerced object.
+    setPrototypeOf(objectPreventExtensions(obj), proto);
+    return O;
+  }
+};
 
 /**
- * Returns the property key (symbol or string) corresponding to the
- * provided value.
+ * Returns the provided value converted to an object.
+ *
+ * Existing objects are returned with no modification.
+ *
+ * ☡ This function throws if its argument is null or undefined.
  */
-export const toPropertyKey = ($) => {
-  const key = toPrimitive($, "string");
-  return typeof key == "symbol" ? key : `${key}`;
+export const toObject = ($) => {
+  if ($ == null) {
+    // The provided value is nullish; this is an error.
+    throw new TypeError(
+      `Piscēs: Cannot convert ${$} into an object.`,
+    );
+  } else {
+    // The provided value is not nullish; coerce it to an object.
+    return object($);
+  }
 };
This page took 0.278515 seconds and 4 git commands to generate.