// 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,
}
}
-/**
- * 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.
}
};
+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.
*