X-Git-Url: https://git.ladys.computer/Pisces/blobdiff_plain/50ab30fc3a257e46da6b8bdc0dad054f729e16e1..refs/heads/current:/value.js diff --git a/value.js b/value.js index 1039a3c..cf858ad 100644 --- 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.