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