// 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,
- toFunctionName,
-} from "./function.js";
+import { bind, call, createArrowFunction } from "./function.js";
import {
IS_CONCAT_SPREADABLE,
ITERATOR,
SPECIES,
+ toFunctionName,
toLength,
toPrimitive,
+ toPropertyDescriptor,
type,
+ UNDEFINED,
} 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).
- */
- 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.
*/
frozenCopy,
+ /**
+ * Returns the property descriptor record for the own property with
+ * the provided property key on the provided object, or null if none
+ * exists.
+ *
+ * ※ This is effectively an alias for
+ * `Object.getOwnPropertyDescriptor`, but the return value is a
+ * proxied object with null prototype.
+ */
+ getOwnPropertyDescriptor,
+
+ /**
+ * Returns the property descriptors for the own properties on the
+ * provided object.
+ *
+ * ※ This is effectively an alias for
+ * `Object.getOwnPropertyDescriptors`, but the values on the
+ * resulting object are proxied objects with null prototypes.
+ */
+ getOwnPropertyDescriptors,
+
/**
* Returns whether the provided object is frozen.
*
const {
create,
defineProperties,
+ getOwnPropertyDescriptor: objectGetOwnPropertyDescriptor,
getPrototypeOf,
isFrozen,
isSealed,
);
}
},
+ getOwnPropertyDescriptor: (O, P) => {
+ const desc = objectGetOwnPropertyDescriptor(O, P);
+ return desc === UNDEFINED
+ ? UNDEFINED
+ : toPropertyDescriptor(desc);
+ },
+ getOwnPropertyDescriptors: (O) => {
+ const obj = toObject(O);
+ const ownKeys = getOwnPropertyKeys(obj);
+ const descriptors = {};
+ for (let k = 0; k < ownKeys.length; ++k) {
+ const key = ownKeys[k];
+ defineOwnProperty(descriptors, key, {
+ configurable: true,
+ enumerable: true,
+ value: getOwnPropertyDescriptor(O, key),
+ writable: true,
+ });
+ }
+ return descriptors;
+ },
isUnfrozenObject: (O) => !isFrozen(O),
isUnsealedObject: (O) => !isSealed(O),
setPrototype: (O, proto) => {
}
};
-/**
- * Returns the property descriptor for the own property with the
- * provided property key on the provided object, or null if none
- * exists.
- *
- * ※ This is effectively an alias for
- * `Object.getOwnPropertyDescriptor`.
- */
-export const getOwnPropertyDescriptor = createArrowFunction(
- Object.getOwnPropertyDescriptor,
-);
-
-/**
- * Returns the property descriptors for the own properties on the
- * provided object.
- *
- * ※ This is effectively an alias for
- * `Object.getOwnPropertyDescriptors`.
- */
-export const getOwnPropertyDescriptors = createArrowFunction(
- Object.getOwnPropertyDescriptors,
-);
-
/**
* Returns an array of string‐valued own property keys on the
* provided object.