]> Lady’s Gitweb - Pisces/blob - object.js
Improve object.js a·p·i
[Pisces] / object.js
1 // ♓🌟 Piscēs ∷ object.js
2 // ====================================================================
3 //
4 // Copyright © 2022–2023 Lady [@ Lady’s Computer].
5 //
6 // This Source Code Form is subject to the terms of the Mozilla Public
7 // License, v. 2.0. If a copy of the MPL was not distributed with this
8 // file, You can obtain one at <https://mozilla.org/MPL/2.0/>.
9
10 import { bind, call, toFunctionName } from "./function.js";
11 import { ITERATOR, SPECIES, toPrimitive, type } from "./value.js";
12
13 /**
14 * An object whose properties are lazy‐loaded from the methods on the
15 * own properties of the provided object.
16 *
17 * This is useful when you are looking to reference properties on
18 * objects which, due to module dependency graphs, cannot be guaranteed
19 * to have been initialized yet.
20 *
21 * The resulting properties will have the same attributes (regarding
22 * configurability, enumerability, and writability) as the
23 * corresponding properties on the methods object. If a property is
24 * marked as writable, the method will never be called if it is set
25 * before it is gotten. By necessity, the resulting properties are all
26 * configurable before they are accessed for the first time.
27 *
28 * Methods will be called with the resulting object as their this
29 * value.
30 *
31 * `LazyLoader` objects have the same prototype as the passed methods
32 * object.
33 */
34 export class LazyLoader extends null {
35 /**
36 * Constructs a new `LazyLoader` object.
37 *
38 * ☡ This function throws if the provided value is not an object.
39 */
40 constructor(loadMethods) {
41 if (type(loadMethods) !== "object") {
42 // The provided value is not an object; throw an error.
43 throw new TypeError(
44 `Piscēs: Cannot construct LazyLoader: Provided argument is not an object: ${loadMethods}.`,
45 );
46 } else {
47 // The provided value is an object; process it and build the
48 // result.
49 const result = objectCreate(getPrototype(loadMethods));
50 const methodKeys = getOwnPropertyKeys(loadMethods);
51 for (let index = 0; index < methodKeys.length; ++index) {
52 // Iterate over the property keys of the provided object and
53 // define getters and setters appropriately on the result.
54 const methodKey = methodKeys[index];
55 const { configurable, enumerable, writable } =
56 getOwnPropertyDescriptor(loadMethods, methodKey);
57 defineOwnProperty(result, methodKey, {
58 configurable: true,
59 enumerable,
60 get: defineOwnProperty(
61 () => {
62 const value = call(loadMethods[methodKey], result, []);
63 defineOwnProperty(result, methodKey, {
64 configurable,
65 enumerable,
66 value,
67 writable,
68 });
69 return value;
70 },
71 "name",
72 { value: toFunctionName(methodKey, "get") },
73 ),
74 set: writable
75 ? defineOwnProperty(
76 ($) =>
77 defineOwnProperty(result, methodKey, {
78 configurable,
79 enumerable,
80 value: $,
81 writable,
82 }),
83 "name",
84 { value: toFunctionName(methodKey, "set") },
85 )
86 : void {},
87 });
88 }
89 return result;
90 }
91 }
92 }
93
94 /**
95 * A property descriptor object.
96 *
97 * Actually constructing a property descriptor object using this class
98 * is only necessary if you need strict guarantees about the types of
99 * its properties; the resulting object is proxied to ensure the types
100 * match what one would expect from composing FromPropertyDescriptor
101 * and ToPropertyDescriptor in the Ecmascript specification.
102 *
103 * Otherwise, the instance properties and methods are generic.
104 */
105 export const { PropertyDescriptor } = (() => {
106 class PropertyDescriptor extends null {
107 /**
108 * Constructs a new property descriptor object from the provided
109 * object.
110 *
111 * The resulting object is proxied to enforce types (for example,
112 * its `.enumerable` property, if defined, will always be a
113 * boolean).
114 */
115 constructor(O) {
116 if (type(O) !== "object") {
117 // The provided value is not an object.
118 throw new TypeError(
119 `Piscēs: Cannot convert primitive to property descriptor: ${O}.`,
120 );
121 } else {
122 // The provided value is an object.
123 const desc = objectCreate(propertyDescriptorPrototype);
124 if ("enumerable" in O) {
125 // An enumerable property is specified.
126 desc.enumerable = !!O.enumerable;
127 } else {
128 // An enumerable property is not specified.
129 /* do nothing */
130 }
131 if ("configurable" in O) {
132 // A configurable property is specified.
133 desc.configurable = !!O.configurable;
134 } else {
135 // A configurable property is not specified.
136 /* do nothing */
137 }
138 if ("value" in O) {
139 // A value property is specified.
140 desc.value = O.value;
141 } else {
142 // A value property is not specified.
143 /* do nothing */
144 }
145 if ("writable" in O) {
146 // A writable property is specified.
147 desc.writable = !!O.writable;
148 } else {
149 // A writable property is not specified.
150 /* do nothing */
151 }
152 if ("get" in O) {
153 // A get property is specified.
154 const getter = O.get;
155 if (getter !== undefined && typeof getter !== "function") {
156 // The getter is not callable.
157 throw new TypeError("Piscēs: Getters must be callable.");
158 } else {
159 // The getter is callable.
160 desc.get = getter;
161 }
162 } else {
163 // A get property is not specified.
164 /* do nothing */
165 }
166 if ("set" in O) {
167 // A set property is specified.
168 const setter = O.set;
169 if (setter !== undefined && typeof setter !== "function") {
170 // The setter is not callable.
171 throw new TypeError("Piscēs: Setters must be callable.");
172 } else {
173 // The setter is callable.
174 desc.set = setter;
175 }
176 } else {
177 // A set property is not specified.
178 /* do nothing */
179 }
180 if (
181 ("get" in desc || "set" in desc) &&
182 ("value" in desc || "writable" in desc)
183 ) {
184 // Both accessor and data attributes have been defined.
185 throw new TypeError(
186 "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
187 );
188 } else {
189 // The property descriptor is valid.
190 return new Proxy(desc, propertyDescriptorProxyHandler);
191 }
192 }
193 }
194
195 /**
196 * Completes this property descriptor by setting missing values to
197 * their defaults.
198 *
199 * This method modifies this object and returns undefined.
200 */
201 complete() {
202 if (this !== undefined && !("get" in this || "set" in this)) {
203 // This is a generic or data descriptor.
204 if (!("value" in this)) {
205 // `value` is not defined on this.
206 this.value = undefined;
207 } else {
208 // `value` is already defined on this.
209 /* do nothing */
210 }
211 if (!("writable" in this)) {
212 // `writable` is not defined on this.
213 this.writable = false;
214 } else {
215 // `writable` is already defined on this.
216 /* do nothing */
217 }
218 } else {
219 // This is not a generic or data descriptor.
220 if (!("get" in this)) {
221 // `get` is not defined on this.
222 this.get = undefined;
223 } else {
224 // `get` is already defined on this.
225 /* do nothing */
226 }
227 if (!("set" in this)) {
228 // `set` is not defined on this.
229 this.set = undefined;
230 } else {
231 // `set` is already defined on this.
232 /* do nothing */
233 }
234 }
235 if (!("enumerable" in this)) {
236 // `enumerable` is not defined on this.
237 this.enumerable = false;
238 } else {
239 // `enumerable` is already defined on this.
240 /* do nothing */
241 }
242 if (!("configurable" in this)) {
243 // `configurable` is not defined on this.
244 this.configurable = false;
245 } else {
246 // `configurable` is already defined on this.
247 /* do nothing */
248 }
249 }
250
251 /** Gets whether this is an accessor descrtiptor. */
252 get isAccessorDescriptor() {
253 return this !== undefined && ("get" in this || "set" in this);
254 }
255
256 /** Gets whether this is a data descrtiptor. */
257 get isDataDescriptor() {
258 return this !== undefined &&
259 ("value" in this || "writable" in this);
260 }
261
262 /** Gets whether this is a fully‐populated property descriptor. */
263 get isFullyPopulated() {
264 return this !== undefined &&
265 ("value" in this && "writable" in this ||
266 "get" in this && "set" in this) &&
267 "enumerable" in this && "configurable" in this;
268 }
269
270 /**
271 * Gets whether this is a generic (not accessor or data)
272 * descrtiptor.
273 */
274 get isGenericDescriptor() {
275 return this !== undefined &&
276 !("get" in this || "set" in this || "value" in this ||
277 "writable" in this);
278 }
279 }
280
281 const coercePropertyDescriptorValue = (P, V) => {
282 switch (P) {
283 case "configurable":
284 case "enumerable":
285 case "writable":
286 return !!V;
287 case "value":
288 return V;
289 case "get":
290 if (V !== undefined && typeof V !== "function") {
291 throw new TypeError(
292 "Piscēs: Getters must be callable.",
293 );
294 } else {
295 return V;
296 }
297 case "set":
298 if (V !== undefined && typeof V !== "function") {
299 throw new TypeError(
300 "Piscēs: Setters must be callable.",
301 );
302 } else {
303 return V;
304 }
305 default:
306 return V;
307 }
308 };
309
310 const {
311 prototype: propertyDescriptorPrototype,
312 } = PropertyDescriptor;
313
314 const propertyDescriptorProxyHandler = Object.assign(
315 Object.create(null),
316 {
317 defineProperty(O, P, Desc) {
318 if (
319 P === "configurable" || P === "enumerable" ||
320 P === "writable" || P === "value" ||
321 P === "get" || P === "set"
322 ) {
323 // P is a property descriptor attribute.
324 const desc = new PropertyDescriptor(Desc);
325 if ("get" in desc || "set" in desc) {
326 // Desc is an accessor property descriptor.
327 throw new TypeError(
328 "Piscēs: Property descriptor attributes must be data properties.",
329 );
330 } else if ("value" in desc || !(P in O)) {
331 // Desc has a value or P does not already exist on O.
332 desc.value = coercePropertyDescriptorValue(P, desc.value);
333 } else {
334 // Desc is not an accessor property descriptor and has no
335 // value.
336 /* do nothing */
337 }
338 const isAccessorDescriptor = "get" === P || "set" === P ||
339 "get" in O || "set" in O;
340 const isDataDescriptor = "value" === P || "writable" === P ||
341 "value" in O || "writable" in O;
342 if (isAccessorDescriptor && isDataDescriptor) {
343 // Both accessor and data attributes will be present on O
344 // after defining P.
345 throw new TypeError(
346 "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
347 );
348 } else {
349 // P can be safely defined on O.
350 return defineOwnProperty(O, P, desc);
351 }
352 } else {
353 // P is not a property descriptor attribute.
354 return defineOwnProperty(O, P, Desc);
355 }
356 },
357 set(O, P, V, Receiver) {
358 if (
359 P === "configurable" || P === "enumerable" ||
360 P === "writable" || P === "value" ||
361 P === "get" || P === "set"
362 ) {
363 // P is a property descriptor attribute.
364 const newValue = coercePropertyDescriptorValue(P, V);
365 const isAccessorDescriptor = "get" === P || "set" === P ||
366 "get" in O || "set" in O;
367 const isDataDescriptor = "value" === P || "writable" === P ||
368 "value" in O || "writable" in O;
369 if (isAccessorDescriptor && isDataDescriptor) {
370 // Both accessor and data attributes will be present on O
371 // after defining P.
372 throw new TypeError(
373 "Piscēs: Property descriptors cannot specify both accessor and data attributes.",
374 );
375 } else {
376 // P can be safely defined on O.
377 //
378 // ☡ Receiver will be the *proxied* object, so passing it
379 // through to setPropertyValue here would produce an
380 // infinite loop.
381 //
382 // ☡ This has implications on objects with a proxied
383 // PropertyDescriptor in their prototype.
384 return setPropertyValue(O, P, newValue, O);
385 }
386 } else {
387 return setPropertyValue(O, P, V, Receiver);
388 }
389 },
390 setPrototypeOf(O, V) {
391 if (V !== propertyDescriptorPrototype) {
392 // V is not the property descriptor prototype.
393 return false;
394 } else {
395 // V is the property descriptor prototype.
396 return setPrototype(O, V);
397 }
398 },
399 },
400 );
401
402 return { PropertyDescriptor };
403 })();
404
405 export const {
406 /**
407 * Defines an own property on the provided object on the provided
408 * property key using the provided property descriptor.
409 *
410 * ※ This is effectively an alias for `Object.defineProperty`.
411 */
412 defineOwnProperty,
413
414 /**
415 * Defines own properties on the provided object using the
416 * descriptors on the enumerable own properties of the provided
417 * additional objects.
418 *
419 * ※ This differs from `Object.defineProperties` in that it can take
420 * multiple source objects.
421 */
422 defineOwnProperties,
423
424 /**
425 * Marks the provided object as non·extensible and marks all its
426 * properties as nonconfigurable and (if data properties)
427 * nonwritable, and returns the object.
428 *
429 * ※ This is effectively an alias for `Object.freeze`.
430 */
431 freeze,
432
433 /**
434 * Returns the property descriptor for the own property with the
435 * provided property key on the provided object, or null if none
436 * exists.
437 *
438 * ※ This is effectively an alias for
439 * `Object.getOwnPropertyDescriptor`.
440 */
441 getOwnPropertyDescriptor,
442
443 /**
444 * Returns the property descriptors for the own properties on the
445 * provided object.
446 *
447 * ※ This is effectively an alias for
448 * `Object.getOwnPropertyDescriptors`.
449 */
450 getOwnPropertyDescriptors,
451
452 /**
453 * Returns an array of string‐valued own property keys on the
454 * provided object.
455 *
456 * ☡ This includes both enumerable and non·enumerable properties.
457 *
458 * ※ This is effectively an alias for `Object.getOwnPropertyNames`.
459 */
460 getOwnPropertyStrings,
461
462 /**
463 * Returns an array of symbol‐valued own property keys on the
464 * provided object.
465 *
466 * ☡ This includes both enumerable and non·enumerable properties.
467 *
468 * ※ This is effectively an alias for
469 * `Object.getOwnPropertySymbols`.
470 */
471 getOwnPropertySymbols,
472
473 /**
474 * Returns the prototype of the provided object.
475 *
476 * ※ This is effectively an alias for `Object.getPrototypeOf`.
477 */
478 getPrototype,
479
480 /**
481 * Returns whether the provided object has an own property with the
482 * provided property key.
483 *
484 * ※ This is effectively an alias for `Object.hasOwn`.
485 */
486 hasOwnProperty,
487
488 /**
489 * Returns whether the provided object is extensible.
490 *
491 * ※ This function returns false for nonobjects.
492 *
493 * ※ This is effectively an alias for `Object.isExtensible`.
494 */
495 isExtensibleObject,
496
497 /**
498 * Returns whether the provided object is frozen.
499 *
500 * ※ This function returns false for nonobjects.
501 *
502 * ※ This is effectively an alias for `!Object.isFrozen`.
503 */
504 isUnfrozenObject,
505
506 /**
507 * Returns whether the provided object is sealed.
508 *
509 * ※ This function returns false for nonobjects.
510 *
511 * ※ This is effectively an alias for `!Object.isSealed`.
512 */
513 isUnsealedObject,
514
515 /**
516 * Returns an array of key~value pairs for the enumerable,
517 * string‐valued property keys on the provided object.
518 *
519 * ※ This is effectively an alias for `Object.entries`.
520 */
521 namedEntries,
522
523 /**
524 * Returns an array of the enumerable, string‐valued property keys on
525 * the provided object.
526 *
527 * ※ This is effectively an alias for `Object.keys`.
528 */
529 namedKeys,
530
531 /**
532 * Returns an array of property values for the enumerable,
533 * string‐valued property keys on the provided object.
534 *
535 * ※ This is effectively an alias for `Object.values`.
536 */
537 namedValues,
538
539 /**
540 * Returns a new object with the provided prototype and property
541 * descriptors.
542 *
543 * ※ This is effectively an alias for `Object.create`.
544 */
545 objectCreate,
546
547 /**
548 * Returns a new object with the provided property keys and values.
549 *
550 * ※ This is effectively an alias for `Object.fromEntries`.
551 */
552 objectFromEntries,
553
554 /**
555 * Marks the provided object as non·extensible, and returns the
556 * object.
557 *
558 * ※ This is effectively an alias for `Object.preventExtensions`.
559 */
560 preventExtensions,
561
562 /**
563 * Marks the provided object as non·extensible and marks all its
564 * properties as nonconfigurable, and returns the object.
565 *
566 * ※ This is effectively an alias for `Object.seal`.
567 */
568 seal,
569
570 /**
571 * Sets the values of the enumerable own properties of the provided
572 * additional objects on the provided object.
573 *
574 * ※ This is effectively an alias for `Object.assign`.
575 */
576 setPropertyValues,
577
578 /**
579 * Sets the prototype of the provided object to the provided value
580 * and returns the object.
581 *
582 * ※ This is effectively an alias for `Object.setPrototypeOf`.
583 */
584 setPrototype,
585
586 /**
587 * Returns the provided value converted to an object.
588 *
589 * Existing objects are returned with no modification.
590 *
591 * ☡ This function throws if its argument is null or undefined.
592 */
593 toObject,
594 } = (() => {
595 const createObject = Object;
596 const {
597 assign,
598 create,
599 defineProperty,
600 defineProperties,
601 entries,
602 freeze,
603 fromEntries,
604 getOwnPropertyDescriptor,
605 getOwnPropertyDescriptors,
606 getOwnPropertyNames,
607 getOwnPropertySymbols,
608 getPrototypeOf,
609 hasOwn,
610 isExtensible,
611 isFrozen,
612 isSealed,
613 keys,
614 preventExtensions,
615 seal,
616 setPrototypeOf,
617 values,
618 } = Object;
619 const { [ITERATOR]: arrayIterator } = Array.prototype;
620 const arrayIteratorNext = getPrototypeOf([][ITERATOR]()).next;
621 const splatIterablePrototype = {
622 [ITERATOR]() {
623 return {
624 next: bind(
625 arrayIteratorNext,
626 call(arrayIterator, this.args, []),
627 [],
628 ),
629 };
630 },
631 };
632 const splatIterable = ($) =>
633 create(splatIterablePrototype, { args: { value: $ } });
634
635 return {
636 defineOwnProperty: (O, P, Attributes) =>
637 defineProperty(O, P, Attributes),
638 defineOwnProperties: (O, ...sources) => {
639 for (const source of splatIterable(sources)) {
640 defineProperties(O, source);
641 }
642 return O;
643 },
644 freeze: (O) => freeze(O),
645 getOwnPropertyDescriptor: (O, P) => getOwnPropertyDescriptor(O, P),
646 getOwnPropertyDescriptors: (O) => getOwnPropertyDescriptors(O),
647 getOwnPropertyStrings: (O) => getOwnPropertyNames(O),
648 getOwnPropertySymbols: (O) => getOwnPropertySymbols(O),
649 getPrototype: (O) => getPrototypeOf(O),
650 hasOwnProperty: (O, P) => hasOwn(O, P),
651 isExtensibleObject: (O) => isExtensible(O),
652 isUnfrozenObject: (O) => !isFrozen(O),
653 isUnsealedObject: (O) => !isSealed(O),
654 namedEntries: (O) => entries(O),
655 namedKeys: (O) => keys(O),
656 namedValues: (O) => values(O),
657 objectCreate: (O, Properties) => create(O, Properties),
658 objectFromEntries: (iterable) => fromEntries(iterable),
659 preventExtensions: (O) => preventExtensions(O),
660 seal: (O) => seal(O),
661 setPropertyValues: (target, source, ...sources) =>
662 assign(target, source, ...splatIterable(sources)),
663 setPrototype: (O, proto) => {
664 const obj = toObject(O);
665 if (O === obj) {
666 // The provided value is an object; set its prototype normally.
667 return setPrototypeOf(O, proto);
668 } else {
669 // The provided value is not an object; attempt to set the
670 // prototype on a coerced version with extensions prevented,
671 // then return the provided value.
672 //
673 // This will throw if the given prototype does not match the
674 // existing one on the coerced object.
675 setPrototypeOf(preventExtensions(obj), proto);
676 return O;
677 }
678 },
679 toObject: ($) => {
680 if ($ == null) {
681 // The provided value is nullish; this is an error.
682 throw new TypeError(
683 `Piscēs: Cannot convert ${$} into an object.`,
684 );
685 } else {
686 // The provided value is not nullish; coerce it to an object.
687 return createObject($);
688 }
689 },
690 };
691 })();
692
693 export const {
694 /**
695 * Removes the provided property key from the provided object and
696 * returns the object.
697 *
698 * ※ This function differs from `Reflect.deleteProperty` and the
699 * `delete` operator in that it throws if the deletion is
700 * unsuccessful.
701 *
702 * ☡ This function throws if the first argument is not an object.
703 */
704 deleteOwnProperty,
705
706 /**
707 * Returns an array of property keys on the provided object.
708 *
709 * ※ This is effectively an alias for `Reflect.ownKeys`, except that
710 * it does not require that the argument be an object.
711 */
712 getOwnPropertyKeys,
713
714 /**
715 * Returns the value of the provided property key on the provided
716 * object.
717 *
718 * ※ This is effectively an alias for `Reflect.get`, except that it
719 * does not require that the argument be an object.
720 */
721 getPropertyValue,
722
723 /**
724 * Returns whether the provided property key exists on the provided
725 * object.
726 *
727 * ※ This is effectively an alias for `Reflect.has`, except that it
728 * does not require that the argument be an object.
729 *
730 * ※ This includes properties present on the prototype chain.
731 */
732 hasProperty,
733
734 /**
735 * Sets the provided property key to the provided value on the
736 * provided object and returns the object.
737 *
738 * ※ This function differs from `Reflect.set` in that it throws if
739 * the setting is unsuccessful.
740 *
741 * ☡ This function throws if the first argument is not an object.
742 */
743 setPropertyValue,
744 } = (() => {
745 const { deleteProperty, get, has, ownKeys, set } = Reflect;
746
747 return {
748 deleteOwnProperty: (O, P) => {
749 if (type(O) !== "object") {
750 throw new TypeError(
751 `Piscēs: Tried to set property but provided value was not an object: ${V}`,
752 );
753 } else if (!deleteProperty(O, P)) {
754 throw new TypeError(
755 `Piscēs: Tried to delete property from object but [[Delete]] returned false: ${P}`,
756 );
757 } else {
758 return O;
759 }
760 },
761 getOwnPropertyKeys: (O) => ownKeys(toObject(O)),
762 getPropertyValue: (O, P, Receiver = O) =>
763 get(toObject(O), P, Receiver),
764 hasProperty: (O, P) => has(toObject(O), P),
765 setPropertyValue: (O, P, V, Receiver = O) => {
766 if (type(O) !== "object") {
767 throw new TypeError(
768 `Piscēs: Tried to set property but provided value was not an object: ${V}`,
769 );
770 } else if (!set(O, P, V, Receiver)) {
771 throw new TypeError(
772 `Piscēs: Tried to set property on object but [[Set]] returned false: ${P}`,
773 );
774 } else {
775 return O;
776 }
777 },
778 };
779 })();
780
781 export const {
782 /**
783 * Returns a new frozen shallow copy of the enumerable own properties
784 * of the provided object, according to the following rules :—
785 *
786 * - For data properties, create a nonconfigurable, nonwritable
787 * property with the same value.
788 *
789 * - For accessor properties, create a nonconfigurable accessor
790 * property with the same getter *and* setter.
791 *
792 * The prototype for the resulting object will be taken from the
793 * `.prototype` property of the provided constructor, or the
794 * `.prototype` of the `.constructor` of the provided object if the
795 * provided constructor is undefined. If the used constructor has a
796 * nonnullish `.[Symbol.species]`, that will be used instead. If the
797 * used constructor or species is nullish or does not have a
798 * `.prototype` property, the prototype is set to null.
799 *
800 * ※ The prototype of the provided object itself is ignored.
801 */
802 frozenCopy,
803 } = (() => {
804 const {
805 next: generatorIteratorNext,
806 } = getPrototype(function* () {}.prototype);
807 const propertyDescriptorEntryIterablePrototype = {
808 [ITERATOR]() {
809 return {
810 next: bind(generatorIteratorNext, this.generator(), []),
811 };
812 },
813 };
814 return {
815 frozenCopy: (O, constructor = O?.constructor) => {
816 if (O == null) {
817 // O is null or undefined.
818 throw new TypeError(
819 "Piscēs: Cannot copy properties of null or undefined.",
820 );
821 } else {
822 // O is not null or undefined.
823 //
824 // (If not provided, the constructor will be the value of
825 // getting the `.constructor` property of O.)
826 const species = constructor?.[SPECIES] ?? constructor;
827 return preventExtensions(
828 objectCreate(
829 species == null || !("prototype" in species)
830 ? null
831 : species.prototype,
832 objectFromEntries(
833 objectCreate(
834 propertyDescriptorEntryIterablePrototype,
835 {
836 generator: {
837 value: function* () {
838 const ownPropertyKeys = getOwnPropertyKeys(O);
839 for (
840 let i = 0;
841 i < ownPropertyKeys.length;
842 ++i
843 ) {
844 const P = ownPropertyKeys[i];
845 const Desc = getOwnPropertyDescriptor(O, P);
846 if (Desc.enumerable) {
847 // P is an enumerable property.
848 yield [
849 P,
850 "get" in Desc || "set" in Desc
851 ? {
852 configurable: false,
853 enumerable: true,
854 get: Desc.get,
855 set: Desc.set,
856 }
857 : {
858 configurable: false,
859 enumerable: true,
860 value: Desc.value,
861 writable: false,
862 },
863 ];
864 } else {
865 // P is not an enumerable property.
866 /* do nothing */
867 }
868 }
869 },
870 },
871 },
872 ),
873 ),
874 ),
875 );
876 }
877 },
878 };
879 })();
880
881 /**
882 * Returns the function on the provided value at the provided property
883 * key.
884 *
885 * ☡ This function throws if the provided property key does not have an
886 * associated value which is callable.
887 */
888 export const getMethod = (V, P) => {
889 const func = getPropertyValue(V, P);
890 if (func == null) {
891 return undefined;
892 } else if (typeof func !== "function") {
893 throw new TypeError(`Piscēs: Method not callable: ${P}`);
894 } else {
895 return func;
896 }
897 };
898
899 /**
900 * Returns the property key (symbol or string) corresponding to the
901 * provided value.
902 */
903 export const toPropertyKey = ($) => {
904 const key = toPrimitive($, "string");
905 return typeof key === "symbol" ? key : `${key}`;
906 };
This page took 0.157721 seconds and 5 git commands to generate.