From: Lady Date: Sat, 25 Nov 2023 18:01:45 +0000 (-0500) Subject: Build function.js on top of object.js infra X-Git-Url: https://git.ladys.computer/Pisces/commitdiff_plain/cf737f7ce78fdb1c6f13f8b60d53bf0900924b19 Build function.js on top of object.js infra This allows us to be a little safer about our property descriptors. --- diff --git a/function.js b/function.js index 10a3f89..71e4d09 100644 --- a/function.js +++ b/function.js @@ -7,7 +7,23 @@ // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at . -import { ITERATOR, toFunctionName, toLength, type } from "./value.js"; +import { + ITERATOR, + toFunctionName, + toLength, + type, + UNDEFINED, +} from "./value.js"; +import { + defineOwnDataProperty, + defineOwnProperties, + defineOwnProperty, + getOwnPropertyDescriptor, + getPrototype, + objectCreate, + setPropertyValues, + setPrototype, +} from "./object.js"; export const { /** @@ -87,14 +103,6 @@ export const { } = functionPrototype; const objectConstructor = Object; const proxyConstructor = Proxy; - const { - create: objectCreate, - defineProperty: defineOwnProperty, - defineProperties: defineOwnProperties, - getOwnPropertyDescriptor, - getPrototypeOf: getPrototype, - setPrototypeOf: setPrototype, - } = Object; const { apply: reflectApply, construct: reflectConstruct, @@ -121,7 +129,7 @@ export const { }, }; const applyBaseFunction = ($, base, lengthDelta = 0) => { - if (base === undefined) { + if (base === UNDEFINED) { // No base function was provided to apply. return $; } else { @@ -140,15 +148,15 @@ export const { } }; const applyProperties = ($, override) => { - if (override === undefined) { + if (override === UNDEFINED) { // No properties were provided to apply. return $; } else { // Properties were provided; apply them. const { length, name, prototype } = override; if ( - !getOwnPropertyDescriptor($, "prototype")?.writable || - prototype === undefined + prototype === UNDEFINED || + !getOwnPropertyDescriptor($, "prototype")?.writable ) { // The provided function has no `.prototype`, its prototype is // not writable, or no prototype value was provided. @@ -162,17 +170,27 @@ export const { // // Change the prototype property of the provided function to // match. - defineOwnProperty($, "prototype", { value: prototype }); + defineOwnProperty( + $, + "prototype", + defineOwnDataProperty( + objectCreate(null), + "value", + prototype, + ), + ); } return defineOwnProperties($, { - length: { - value: toLength(length === undefined ? $.length : length), - }, - name: { - value: toFunctionName( - name === undefined ? $.name ?? "" : name, - ), - }, + length: defineOwnDataProperty( + objectCreate(null), + "value", + toLength(length === UNDEFINED ? $.length : length), + ), + name: defineOwnDataProperty( + objectCreate(null), + "value", + toFunctionName(name === UNDEFINED ? $.name ?? "" : name), + ), }); } }; @@ -182,26 +200,28 @@ export const { callBind( $, boundThis, - ...objectCreate( - argumentIterablePrototype, - { args: { value: boundArgs } }, + ...defineOwnDataProperty( + objectCreate(argumentIterablePrototype), + "args", + boundArgs, ), ), - createArrowFunction: ($, propertyOverride = undefined) => + createArrowFunction: ($, propertyOverride = UNDEFINED) => applyProperties( applyBaseFunction( - (...$s) => reflectApply($, undefined, $s), + (...$s) => reflectApply($, UNDEFINED, $s), $, ), propertyOverride, ), - createCallableFunction: ($, propertyOverride = undefined) => + createCallableFunction: ($, propertyOverride = UNDEFINED) => applyProperties( applyBaseFunction( (...$s) => { - const iterator = objectCreate( - argumentIterablePrototype, - { args: { value: $s } }, + const iterator = defineOwnDataProperty( + objectCreate(argumentIterablePrototype), + "args", + $s, )[ITERATOR](); const { value: thisValue } = iterator.next(); return reflectApply($, thisValue, [...iterator]); @@ -211,7 +231,7 @@ export const { ), propertyOverride, ), - createIllegalConstructor: ($, propertyOverride = undefined) => + createIllegalConstructor: ($, propertyOverride = UNDEFINED) => defineOwnProperty( applyProperties( applyBaseFunction( @@ -228,36 +248,46 @@ export const { createProxyConstructor: ( handler, $, - newTarget = undefined, - propertyOverride = undefined, + newTarget = UNDEFINED, + propertyOverride = UNDEFINED, ) => { - const constructor = $ === undefined ? objectConstructor : $; - const target = newTarget === undefined ? constructor : newTarget; + const constructor = $ === UNDEFINED ? objectConstructor : $; + const target = newTarget === UNDEFINED ? constructor : newTarget; const len = toLength(constructor.length); if (!(type(handler) === "object")) { + // The provided handler is not an object; this is an error. throw new TypeError( `Piscēs: Proxy handler must be an object, but got: ${handler}.`, ); } else if (!isConstructor(constructor)) { + // The provided constructor is not a constructor; this is an + // error. throw new TypeError( "Piscēs: Cannot create proxy constructor from nonconstructible value.", ); } else if (!isConstructor(target)) { + // The provided new target is not a constructor; this is an + // error. throw new TypeError( "Piscēs: New target must be a constructor.", ); } else { + // The arguments are acceptable. return applyProperties( defineOwnProperties( setPrototype( function C(...$s) { - if (new.target === undefined) { + if (new.target === UNDEFINED) { + // The constructor was not called with new; this is + // an error. throw new TypeError( `Piscēs: ${ C.name ?? "Proxy" } must be called with new.`, ); } else { + // The constructor was called with new; return the + // appropriate proxy. const O = reflectConstruct( constructor, $s, @@ -269,19 +299,23 @@ export const { proxyConstructor, ), { - length: { value: len }, - name: { - value: `${ - toFunctionName(constructor.name ?? "") - }Proxy`, - }, - prototype: { + length: defineOwnDataProperty( + objectCreate(null), + "value", + len, + ), + name: defineOwnDataProperty( + objectCreate(null), + "value", + `${toFunctionName(constructor.name ?? "")}Proxy`, + ), + prototype: setPropertyValues(objectCreate(null), { configurable: false, enumerable: false, - value: undefined, + value: UNDEFINED, writable: false, - }, - revocable: { + }), + revocable: setPropertyValues(objectCreate(null), { configurable: true, enumerable: false, value: defineOwnProperties( @@ -294,12 +328,20 @@ export const { return revocable(O, handler); }, { - length: { value: len }, - name: { value: "revocable" }, + length: defineOwnDataProperty( + objectCreate(null), + "value", + len, + ), + name: defineOwnDataProperty( + objectCreate(null), + "value", + "revocable", + ), }, ), writable: true, - }, + }), }, ), propertyOverride,