]> Lady’s Gitweb - Pisces/blobdiff - object.js
De‐classify property descriptors; move to value.js
[Pisces] / object.js
index 9c0c973b6fd11c9bc6b9871035c4081a95a878a4..aa3538d28cf821d91c72f16244db4668f6f3fda7 100644 (file)
--- a/object.js
+++ b/object.js
@@ -7,15 +7,12 @@
 // 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, createArrowFunction } from "./function.js";
 import {
-  bind,
-  call,
-  createArrowFunction,
-  toFunctionName,
-} from "./function.js";
-import {
+  IS_CONCAT_SPREADABLE,
   ITERATOR,
   SPECIES,
+  toFunctionName,
   toLength,
   toPrimitive,
   type,
@@ -102,317 +99,6 @@ export class LazyLoader extends null {
   }
 }
 
-/**
- * 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).
-     */
-    constructor(O) {
-      if (type(O) !== "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(propertyDescriptorPrototype);
-        if ("enumerable" in O) {
-          // An enumerable property is specified.
-          desc.enumerable = !!O.enumerable;
-        } else {
-          // An enumerable property is not specified.
-          /* do nothing */
-        }
-        if ("configurable" in O) {
-          // A configurable property is specified.
-          desc.configurable = !!O.configurable;
-        } else {
-          // A configurable property is not specified.
-          /* do nothing */
-        }
-        if ("value" in O) {
-          // A value property is specified.
-          desc.value = O.value;
-        } else {
-          // A value property is not specified.
-          /* do nothing */
-        }
-        if ("writable" in O) {
-          // A writable property is specified.
-          desc.writable = !!O.writable;
-        } else {
-          // A writable property is not specified.
-          /* do nothing */
-        }
-        if ("get" in O) {
-          // A get property is specified.
-          const getter = O.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;
-          }
-        } else {
-          // A get property is not specified.
-          /* do nothing */
-        }
-        if ("set" in O) {
-          // A set property is specified.
-          const setter = O.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;
-          }
-        } 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.
-          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.",
-          );
-        } 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);
-        }
-      },
-    },
-  );
-
-  return { PropertyDescriptor };
-})();
-
 /**
  * Defines an own property on the provided object on the provided
  * property key using the provided property descriptor.
@@ -810,6 +496,32 @@ export const isArraylikeObject = ($) => {
   }
 };
 
+export const {
+  /**
+   * Returns whether the provided value is spreadable during array
+   * concatenation.
+   *
+   * This is also used to determine which things should be treated as
+   * collections.
+   */
+  isConcatSpreadableObject,
+} = (() => {
+  const { isArray } = Array;
+
+  return {
+    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 object is extensible.
  *
This page took 0.029642 seconds and 4 git commands to generate.