boa_engine/object/operations.rs
1use super::internal_methods::InternalMethodPropertyContext;
2use crate::js_error;
3use crate::value::JsVariant;
4use crate::{
5 Context, JsExpect, JsResult, JsSymbol, JsValue,
6 builtins::{
7 Array, Proxy,
8 function::{BoundFunction, ClassFieldDefinition, OrdinaryFunction, set_function_name},
9 },
10 context::intrinsics::{StandardConstructor, StandardConstructors},
11 error::JsNativeError,
12 native_function::NativeFunctionObject,
13 object::{CONSTRUCTOR, JsObject, PROTOTYPE, PrivateElement, PrivateName},
14 property::{PropertyDescriptor, PropertyDescriptorBuilder, PropertyKey, PropertyNameKind},
15 realm::Realm,
16 string::StaticJsStrings,
17 value::Type,
18};
19
20/// Object integrity level.
21#[derive(Debug, Clone, Copy, PartialEq, Eq)]
22pub enum IntegrityLevel {
23 /// Sealed object integrity level.
24 ///
25 /// Preventing new properties from being added to it and marking all existing
26 /// properties as non-configurable. Values of present properties can still be
27 /// changed as long as they are writable.
28 Sealed,
29
30 /// Frozen object integrity level
31 ///
32 /// A frozen object can no longer be changed; freezing an object prevents new
33 /// properties from being added to it, existing properties from being removed,
34 /// prevents changing the enumerability, configurability, or writability of
35 /// existing properties, and prevents the values of existing properties from
36 /// being changed. In addition, freezing an object also prevents its prototype
37 /// from being changed.
38 Frozen,
39}
40
41impl IntegrityLevel {
42 /// Returns `true` if the integrity level is sealed.
43 #[must_use]
44 pub const fn is_sealed(&self) -> bool {
45 matches!(self, Self::Sealed)
46 }
47
48 /// Returns `true` if the integrity level is frozen.
49 #[must_use]
50 pub const fn is_frozen(&self) -> bool {
51 matches!(self, Self::Frozen)
52 }
53}
54
55impl JsObject {
56 /// Check if object is extensible.
57 ///
58 /// More information:
59 /// - [ECMAScript reference][spec]
60 ///
61 /// [spec]: https://tc39.es/ecma262/#sec-isextensible-o
62 #[inline]
63 pub fn is_extensible(&self, context: &mut Context) -> JsResult<bool> {
64 // 1. Return ? O.[[IsExtensible]]().
65 self.__is_extensible__(context)
66 }
67
68 /// Get property from object or throw.
69 ///
70 /// More information:
71 /// - [ECMAScript reference][spec]
72 ///
73 /// [spec]: https://tc39.es/ecma262/#sec-get-o-p
74 pub fn get<K>(&self, key: K, context: &mut Context) -> JsResult<JsValue>
75 where
76 K: Into<PropertyKey>,
77 {
78 // 1. Assert: Type(O) is Object.
79 // 2. Assert: IsPropertyKey(P) is true.
80 // 3. Return ? O.[[Get]](P, O).
81 self.__get__(
82 &key.into(),
83 self.clone().into(),
84 &mut InternalMethodPropertyContext::new(context),
85 )
86 }
87
88 /// set property of object or throw if bool flag is passed.
89 ///
90 /// More information:
91 /// - [ECMAScript reference][spec]
92 ///
93 /// [spec]: https://tc39.es/ecma262/#sec-set-o-p-v-throw
94 pub fn set<K, V>(&self, key: K, value: V, throw: bool, context: &mut Context) -> JsResult<bool>
95 where
96 K: Into<PropertyKey>,
97 V: Into<JsValue>,
98 {
99 let key = key.into();
100 // 1. Assert: Type(O) is Object.
101 // 2. Assert: IsPropertyKey(P) is true.
102 // 3. Assert: Type(Throw) is Boolean.
103 // 4. Let success be ? O.[[Set]](P, V, O).
104 let success = self.__set__(
105 key.clone(),
106 value.into(),
107 self.clone().into(),
108 &mut InternalMethodPropertyContext::new(context),
109 )?;
110 // 5. If success is false and Throw is true, throw a TypeError exception.
111 if !success && throw {
112 return Err(JsNativeError::typ()
113 .with_message(format!("cannot set non-writable property: {key}"))
114 .into());
115 }
116 // 6. Return success.
117 Ok(success)
118 }
119
120 /// Create data property
121 ///
122 /// More information:
123 /// - [ECMAScript reference][spec]
124 ///
125 /// [spec]: https://tc39.es/ecma262/#sec-createdataproperty
126 pub fn create_data_property<K, V>(
127 &self,
128 key: K,
129 value: V,
130 context: &mut Context,
131 ) -> JsResult<bool>
132 where
133 K: Into<PropertyKey>,
134 V: Into<JsValue>,
135 {
136 // 1. Assert: Type(O) is Object.
137 // 2. Assert: IsPropertyKey(P) is true.
138 // 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
139 let new_desc = PropertyDescriptor::builder()
140 .value(value)
141 .writable(true)
142 .enumerable(true)
143 .configurable(true);
144 // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
145 self.__define_own_property__(
146 &key.into(),
147 new_desc.into(),
148 &mut InternalMethodPropertyContext::new(context),
149 )
150 }
151
152 /// Create data property
153 ///
154 /// More information:
155 /// - [ECMAScript reference][spec]
156 ///
157 /// [spec]: https://tc39.es/ecma262/#sec-createdataproperty
158 pub(crate) fn create_data_property_with_slot<K, V>(
159 &self,
160 key: K,
161 value: V,
162 context: &mut InternalMethodPropertyContext<'_>,
163 ) -> JsResult<bool>
164 where
165 K: Into<PropertyKey>,
166 V: Into<JsValue>,
167 {
168 // 1. Assert: Type(O) is Object.
169 // 2. Assert: IsPropertyKey(P) is true.
170 // 3. Let newDesc be the PropertyDescriptor { [[Value]]: V, [[Writable]]: true, [[Enumerable]]: true, [[Configurable]]: true }.
171 let new_desc = PropertyDescriptor::builder()
172 .value(value)
173 .writable(true)
174 .enumerable(true)
175 .configurable(true);
176 // 4. Return ? O.[[DefineOwnProperty]](P, newDesc).
177 self.__define_own_property__(&key.into(), new_desc.into(), context)
178 }
179
180 /// `10.2.8 DefineMethodProperty ( homeObject, key, closure, enumerable )`
181 ///
182 /// Defines a method property on an object with the specified attributes.
183 ///
184 /// More information:
185 /// - [ECMAScript reference][spec]
186 ///
187 /// [spec]: https://tc39.es/ecma262/#sec-definemethodproperty
188 pub(crate) fn define_method_property<K, V>(
189 &self,
190 key: K,
191 value: V,
192 context: &mut InternalMethodPropertyContext<'_>,
193 ) -> JsResult<()>
194 where
195 K: Into<PropertyKey>,
196 V: Into<JsValue>,
197 {
198 // 1. Assert: homeObject is an ordinary, extensible object with no non-configurable properties.
199 // 2. If key is a Private Name, then
200 // a. Return PrivateElement { [[Key]]: key, [[Kind]]: method, [[Value]]: closure }.
201 // 3. Else,
202 // a. Let desc be the PropertyDescriptor { [[Value]]: closure, [[Writable]]: true, [[Enumerable]]: enumerable, [[Configurable]]: true }.
203 let new_desc = PropertyDescriptor::builder()
204 .value(value)
205 .writable(true)
206 .enumerable(false)
207 .configurable(true);
208
209 // b. Perform ! DefinePropertyOrThrow(homeObject, key, desc).
210 self.__define_own_property__(&key.into(), new_desc.into(), context)?;
211
212 // c. Return unused.
213 Ok(())
214 }
215
216 /// Create data property or throw
217 ///
218 /// More information:
219 /// - [ECMAScript reference][spec]
220 ///
221 /// [spec]: https://tc39.es/ecma262/#sec-createdatapropertyorthrow
222 pub fn create_data_property_or_throw<K, V>(
223 &self,
224 key: K,
225 value: V,
226 context: &mut Context,
227 ) -> JsResult<bool>
228 where
229 K: Into<PropertyKey>,
230 V: Into<JsValue>,
231 {
232 let key = key.into();
233 // 1. Assert: Type(O) is Object.
234 // 2. Assert: IsPropertyKey(P) is true.
235 // 3. Let success be ? CreateDataProperty(O, P, V).
236 let success = self.create_data_property(key.clone(), value, context)?;
237 // 4. If success is false, throw a TypeError exception.
238 if !success {
239 return Err(JsNativeError::typ()
240 .with_message(format!("cannot redefine property: {key}"))
241 .into());
242 }
243 // 5. Return success.
244 Ok(success)
245 }
246
247 /// Create non-enumerable data property or throw
248 ///
249 /// More information:
250 /// - [ECMAScript reference][spec]
251 ///
252 /// [spec]: https://tc39.es/ecma262/#sec-createnonenumerabledatapropertyinfallibly
253 pub(crate) fn create_non_enumerable_data_property_or_throw<K, V>(
254 &self,
255 key: K,
256 value: V,
257 context: &mut Context,
258 ) where
259 K: Into<PropertyKey>,
260 V: Into<JsValue>,
261 {
262 // 1. Assert: O is an ordinary, extensible object with no non-configurable properties.
263
264 // 2. Let newDesc be the PropertyDescriptor {
265 // [[Value]]: V,
266 // [[Writable]]: true,
267 // [[Enumerable]]: false,
268 // [[Configurable]]: true
269 // }.
270 let new_desc = PropertyDescriptorBuilder::new()
271 .value(value)
272 .writable(true)
273 .enumerable(false)
274 .configurable(true)
275 .build();
276
277 // 3. Perform ! DefinePropertyOrThrow(O, P, newDesc).
278 self.define_property_or_throw(key, new_desc, context)
279 .expect("should not fail according to spec");
280
281 // 4. Return unused.
282 }
283
284 /// Define property or throw.
285 ///
286 /// More information:
287 /// - [ECMAScript reference][spec]
288 ///
289 /// [spec]: https://tc39.es/ecma262/#sec-definepropertyorthrow
290 pub fn define_property_or_throw<K, P>(
291 &self,
292 key: K,
293 desc: P,
294 context: &mut Context,
295 ) -> JsResult<bool>
296 where
297 K: Into<PropertyKey>,
298 P: Into<PropertyDescriptor>,
299 {
300 let key = key.into();
301 // 1. Assert: Type(O) is Object.
302 // 2. Assert: IsPropertyKey(P) is true.
303 // 3. Let success be ? O.[[DefineOwnProperty]](P, desc).
304 let success = self.__define_own_property__(
305 &key,
306 desc.into(),
307 &mut InternalMethodPropertyContext::new(context),
308 )?;
309 // 4. If success is false, throw a TypeError exception.
310 if !success {
311 return Err(JsNativeError::typ()
312 .with_message(format!("cannot redefine property: {key}"))
313 .into());
314 }
315 // 5. Return success.
316 Ok(success)
317 }
318
319 /// Defines the property or throws a `TypeError` if the operation fails.
320 ///
321 /// More information:
322 /// - [ECMAScript reference][spec]
323 ///
324 /// [spec]: https://tc39.es/ecma262/#sec-deletepropertyorthrow
325 pub fn delete_property_or_throw<K>(&self, key: K, context: &mut Context) -> JsResult<bool>
326 where
327 K: Into<PropertyKey>,
328 {
329 let key = key.into();
330 // 1. Assert: Type(O) is Object.
331 // 2. Assert: IsPropertyKey(P) is true.
332 // 3. Let success be ? O.[[Delete]](P).
333 let success = self.__delete__(&key, &mut InternalMethodPropertyContext::new(context))?;
334 // 4. If success is false, throw a TypeError exception.
335 if !success {
336 return Err(JsNativeError::typ()
337 .with_message(format!("cannot delete non-configurable property: {key}"))
338 .into());
339 }
340 // 5. Return success.
341 Ok(success)
342 }
343
344 /// Check if object has property.
345 ///
346 /// More information:
347 /// - [ECMAScript reference][spec]
348 ///
349 /// [spec]: https://tc39.es/ecma262/#sec-hasproperty
350 pub fn has_property<K>(&self, key: K, context: &mut Context) -> JsResult<bool>
351 where
352 K: Into<PropertyKey>,
353 {
354 // 1. Assert: Type(O) is Object.
355 // 2. Assert: IsPropertyKey(P) is true.
356 // 3. Return ? O.[[HasProperty]](P).
357
358 self.__has_property__(
359 &key.into(),
360 &mut InternalMethodPropertyContext::new(context),
361 )
362 }
363
364 /// Abstract optimization operation.
365 ///
366 /// Check if an object has a property and get it if it exists.
367 /// This operation combines the abstract operations `HasProperty` and `Get`.
368 ///
369 /// More information:
370 /// - [ECMAScript reference HasProperty][spec0]
371 /// - [ECMAScript reference Get][spec1]
372 ///
373 /// [spec0]: https://tc39.es/ecma262/#sec-hasproperty
374 /// [spec1]: https://tc39.es/ecma262/#sec-get-o-p
375 pub(crate) fn try_get<K>(&self, key: K, context: &mut Context) -> JsResult<Option<JsValue>>
376 where
377 K: Into<PropertyKey>,
378 {
379 self.__try_get__(
380 &key.into(),
381 self.clone().into(),
382 &mut InternalMethodPropertyContext::new(context),
383 )
384 }
385
386 /// Check if object has an own property.
387 ///
388 /// More information:
389 /// - [ECMAScript reference][spec]
390 ///
391 /// [spec]: https://tc39.es/ecma262/#sec-hasownproperty
392 pub fn has_own_property<K>(&self, key: K, context: &mut Context) -> JsResult<bool>
393 where
394 K: Into<PropertyKey>,
395 {
396 let key = key.into();
397 // 1. Assert: Type(O) is Object.
398 // 2. Assert: IsPropertyKey(P) is true.
399 // 3. Let desc be ? O.[[GetOwnProperty]](P).
400 let desc =
401 self.__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;
402 // 4. If desc is undefined, return false.
403 // 5. Return true.
404 Ok(desc.is_some())
405 }
406
407 /// Get all the keys of the properties of this object.
408 ///
409 /// More information:
410 /// - [ECMAScript reference][spec]
411 ///
412 /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-ownpropertykeys
413 pub fn own_property_keys(&self, context: &mut Context) -> JsResult<Vec<PropertyKey>> {
414 self.__own_property_keys__(context)
415 }
416
417 /// `Call ( F, V [ , argumentsList ] )`
418 ///
419 /// # Panics
420 ///
421 /// Panics if the object is currently mutably borrowed.
422 ///
423 /// More information:
424 /// - [ECMAScript reference][spec]
425 ///
426 /// [spec]: https://tc39.es/ecma262/#sec-call
427 #[track_caller]
428 #[inline]
429 pub fn call(
430 &self,
431 this: &JsValue,
432 args: &[JsValue],
433 context: &mut Context,
434 ) -> JsResult<JsValue> {
435 // SKIP: 1. If argumentsList is not present, set argumentsList to a new empty List.
436 // SKIP: 2. If IsCallable(F) is false, throw a TypeError exception.
437 // NOTE(HalidOdat): For object's that are not callable we implement a special __call__ internal method
438 // that throws on call.
439
440 context.vm.stack.push(this.clone()); // this
441 context.vm.stack.push(self.clone()); // func
442 let argument_count = args.len();
443 context.vm.stack.calling_convention_push_arguments(args);
444
445 // 3. Return ? F.[[Call]](V, argumentsList).
446 let frame_index = context.vm.frames.len();
447 if self.__call__(argument_count).resolve(context)? {
448 return Ok(context.vm.stack.pop());
449 }
450
451 if frame_index + 1 == context.vm.frames.len() {
452 context.vm.frame_mut().set_exit_early(true);
453 } else {
454 context.vm.frames[frame_index + 1].set_exit_early(true);
455 }
456
457 context.vm.host_call_depth += 1;
458 let result = context.run().consume();
459 context.vm.host_call_depth = context.vm.host_call_depth.saturating_sub(1);
460
461 context.vm.pop_frame().js_expect("frame must exist")?;
462
463 result
464 }
465
466 /// `Construct ( F [ , argumentsList [ , newTarget ] ] )`
467 ///
468 /// Construct an instance of this object with the specified arguments.
469 ///
470 /// # Panics
471 ///
472 /// Panics if the object is currently mutably borrowed.
473 ///
474 /// More information:
475 /// - [ECMAScript reference][spec]
476 ///
477 /// [spec]: https://tc39.es/ecma262/#sec-construct
478 #[track_caller]
479 #[inline]
480 pub fn construct(
481 &self,
482 args: &[JsValue],
483 new_target: Option<&Self>,
484 context: &mut Context,
485 ) -> JsResult<Self> {
486 // 1. If newTarget is not present, set newTarget to F.
487 let new_target = new_target.unwrap_or(self);
488
489 context.vm.stack.push(JsValue::undefined());
490 context.vm.stack.push(self.clone()); // func
491 let argument_count = args.len();
492 context.vm.stack.calling_convention_push_arguments(args);
493 context.vm.stack.push(new_target.clone());
494
495 // 2. If argumentsList is not present, set argumentsList to a new empty List.
496 // 3. Return ? F.[[Construct]](argumentsList, newTarget).
497 let frame_index = context.vm.frames.len();
498
499 if self.__construct__(argument_count).resolve(context)? {
500 let result = context.vm.stack.pop();
501 return Ok(result
502 .as_object()
503 .js_expect("construct value should be an object")?
504 .clone());
505 }
506
507 if frame_index + 1 == context.vm.frames.len() {
508 context.vm.frame_mut().set_exit_early(true);
509 } else {
510 context.vm.frames[frame_index + 1].set_exit_early(true);
511 }
512
513 context.vm.host_call_depth += 1;
514 let result = context.run().consume();
515 context.vm.host_call_depth = context.vm.host_call_depth.saturating_sub(1);
516
517 context.vm.pop_frame().js_expect("frame must exist")?;
518
519 Ok(result?
520 .as_object()
521 .js_expect("should be an object")?
522 .clone())
523 }
524
525 /// Make the object [`sealed`][IntegrityLevel::Sealed] or [`frozen`][IntegrityLevel::Frozen].
526 ///
527 /// More information:
528 /// - [ECMAScript reference][spec]
529 ///
530 /// [spec]: https://tc39.es/ecma262/#sec-setintegritylevel
531 pub fn set_integrity_level(
532 &self,
533 level: IntegrityLevel,
534 context: &mut Context,
535 ) -> JsResult<bool> {
536 // 1. Assert: Type(O) is Object.
537 // 2. Assert: level is either sealed or frozen.
538
539 // 3. Let status be ? O.[[PreventExtensions]]().
540 let status =
541 self.__prevent_extensions__(&mut InternalMethodPropertyContext::new(context))?;
542 // 4. If status is false, return false.
543 if !status {
544 return Ok(false);
545 }
546
547 // 5. Let keys be ? O.[[OwnPropertyKeys]]().
548 let keys = self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;
549
550 match level {
551 // 6. If level is sealed, then
552 IntegrityLevel::Sealed => {
553 // a. For each element k of keys, do
554 for k in keys {
555 // i. Perform ? DefinePropertyOrThrow(O, k, PropertyDescriptor { [[Configurable]]: false }).
556 self.define_property_or_throw(
557 k,
558 PropertyDescriptor::builder().configurable(false).build(),
559 context,
560 )?;
561 }
562 }
563 // 7. Else,
564 // a. Assert: level is frozen.
565 IntegrityLevel::Frozen => {
566 // b. For each element k of keys, do
567 for k in keys {
568 // i. Let currentDesc be ? O.[[GetOwnProperty]](k).
569 let current_desc = self.__get_own_property__(
570 &k,
571 &mut InternalMethodPropertyContext::new(context),
572 )?;
573 // ii. If currentDesc is not undefined, then
574 if let Some(current_desc) = current_desc {
575 // 1. If IsAccessorDescriptor(currentDesc) is true, then
576 let desc = if current_desc.is_accessor_descriptor() {
577 // a. Let desc be the PropertyDescriptor { [[Configurable]]: false }.
578 PropertyDescriptor::builder().configurable(false).build()
579 // 2. Else,
580 } else {
581 // a. Let desc be the PropertyDescriptor { [[Configurable]]: false, [[Writable]]: false }.
582 PropertyDescriptor::builder()
583 .configurable(false)
584 .writable(false)
585 .build()
586 };
587 // 3. Perform ? DefinePropertyOrThrow(O, k, desc).
588 self.define_property_or_throw(k, desc, context)?;
589 }
590 }
591 }
592 }
593
594 // 8. Return true.
595 Ok(true)
596 }
597
598 /// Check if the object is [`sealed`][IntegrityLevel::Sealed] or [`frozen`][IntegrityLevel::Frozen].
599 ///
600 /// More information:
601 /// - [ECMAScript reference][spec]
602 ///
603 /// [spec]: https://tc39.es/ecma262/#sec-testintegritylevel
604 pub fn test_integrity_level(
605 &self,
606 level: IntegrityLevel,
607 context: &mut Context,
608 ) -> JsResult<bool> {
609 // 1. Assert: Type(O) is Object.
610 // 2. Assert: level is either sealed or frozen.
611
612 // 3. Let extensible be ? IsExtensible(O).
613 let extensible = self.is_extensible(context)?;
614
615 // 4. If extensible is true, return false.
616 if extensible {
617 return Ok(false);
618 }
619
620 // 5. NOTE: If the object is extensible, none of its properties are examined.
621 // 6. Let keys be ? O.[[OwnPropertyKeys]]().
622 let keys = self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;
623
624 // 7. For each element k of keys, do
625 for k in keys {
626 // a. Let currentDesc be ? O.[[GetOwnProperty]](k).
627 let current_desc =
628 self.__get_own_property__(&k, &mut InternalMethodPropertyContext::new(context))?;
629 // b. If currentDesc is not undefined, then
630 if let Some(current_desc) = current_desc {
631 // i. If currentDesc.[[Configurable]] is true, return false.
632 if current_desc.expect_configurable() {
633 return Ok(false);
634 }
635 // ii. If level is frozen and IsDataDescriptor(currentDesc) is true, then
636 if level.is_frozen() && current_desc.is_data_descriptor() {
637 // 1. If currentDesc.[[Writable]] is true, return false.
638 if current_desc.expect_writable() {
639 return Ok(false);
640 }
641 }
642 }
643 }
644 // 8. Return true.
645 Ok(true)
646 }
647
648 /// Abstract operation [`LengthOfArrayLike ( obj )`][spec].
649 ///
650 /// Returns the value of the "length" property of an array-like object.
651 ///
652 /// [spec]: https://tc39.es/ecma262/#sec-lengthofarraylike
653 pub(crate) fn length_of_array_like(&self, context: &mut Context) -> JsResult<u64> {
654 // 1. Assert: Type(obj) is Object.
655
656 // NOTE: This is an optimization, most of the cases that `LengthOfArrayLike` will be called
657 // is for arrays. The "length" property of an array is stored in the first index.
658 if self.is_array() {
659 let borrowed_object = self.borrow();
660 // NOTE: using `to_u32` instead of `to_length` is an optimization,
661 // since arrays are limited to [0, 2^32 - 1] range.
662 return borrowed_object.properties().storage[0]
663 .to_u32(context)
664 .map(u64::from);
665 }
666
667 // 2. Return ℝ(? ToLength(? Get(obj, "length"))).
668 self.get(StaticJsStrings::LENGTH, context)?
669 .to_length(context)
670 }
671
672 /// `7.3.22 SpeciesConstructor ( O, defaultConstructor )`
673 ///
674 /// The abstract operation `SpeciesConstructor` takes arguments `O` (an Object) and
675 /// `defaultConstructor` (a constructor). It is used to retrieve the constructor that should be
676 /// used to create new objects that are derived from `O`. `defaultConstructor` is the
677 /// constructor to use if a constructor `@@species` property cannot be found starting from `O`.
678 ///
679 /// More information:
680 /// - [ECMAScript reference][spec]
681 ///
682 /// [spec]: https://tc39.es/ecma262/#sec-speciesconstructor
683 pub(crate) fn species_constructor<F>(
684 &self,
685 default_constructor: F,
686 context: &mut Context,
687 ) -> JsResult<Self>
688 where
689 F: FnOnce(&StandardConstructors) -> &StandardConstructor,
690 {
691 // 1. Assert: Type(O) is Object.
692
693 // 2. Let C be ? Get(O, "constructor").
694 let c = self.get(CONSTRUCTOR, context)?;
695
696 // 3. If C is undefined, return defaultConstructor.
697 if c.is_undefined() {
698 return Ok(default_constructor(context.intrinsics().constructors()).constructor());
699 }
700
701 // 4. If Type(C) is not Object, throw a TypeError exception.
702 let c = c.as_object().ok_or_else(|| {
703 JsNativeError::typ().with_message("property 'constructor' is not an object")
704 })?;
705
706 // 5. Let S be ? Get(C, @@species).
707 let s = c.get(JsSymbol::species(), context)?;
708
709 // 6. If S is either undefined or null, return defaultConstructor.
710 if s.is_null_or_undefined() {
711 return Ok(default_constructor(context.intrinsics().constructors()).constructor());
712 }
713
714 // 7. If IsConstructor(S) is true, return S.
715 if let Some(s) = s.as_constructor() {
716 return Ok(s.clone());
717 }
718
719 // 8. Throw a TypeError exception.
720 Err(JsNativeError::typ()
721 .with_message("property 'constructor' is not a constructor")
722 .into())
723 }
724
725 /// It is used to iterate over names of object's keys.
726 ///
727 /// More information:
728 /// - [ECMAScript reference][spec]
729 ///
730 /// [spec]: https://tc39.es/ecma262/#sec-enumerableownpropertynames
731 pub(crate) fn enumerable_own_property_names(
732 &self,
733 kind: PropertyNameKind,
734 context: &mut Context,
735 ) -> JsResult<Vec<JsValue>> {
736 // 1. Assert: Type(O) is Object.
737 // 2. Let ownKeys be ? O.[[OwnPropertyKeys]]().
738 let own_keys =
739 self.__own_property_keys__(&mut InternalMethodPropertyContext::new(context))?;
740 // 3. Let properties be a new empty List.
741 let mut properties = Vec::with_capacity(own_keys.len());
742
743 // 4. For each element key of ownKeys, do
744 for key in own_keys {
745 // a. If Type(key) is String, then
746 let key_str = match &key {
747 PropertyKey::String(s) => Some(s.clone()),
748 PropertyKey::Index(i) => Some(i.get().to_string().into()),
749 PropertyKey::Symbol(_) => None,
750 };
751
752 if let Some(key_str) = key_str {
753 // i. Let desc be ? O.[[GetOwnProperty]](key).
754 let desc = self
755 .__get_own_property__(&key, &mut InternalMethodPropertyContext::new(context))?;
756 // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
757 if let Some(desc) = desc
758 && desc.expect_enumerable()
759 {
760 match kind {
761 // 1. If kind is key, append key to properties.
762 PropertyNameKind::Key => properties.push(key_str.into()),
763 // 2. Else,
764 // a. Let value be ? Get(O, key).
765 // b. If kind is value, append value to properties.
766 PropertyNameKind::Value => {
767 properties.push(self.get(key.clone(), context)?);
768 }
769 // c. Else,
770 // i. Assert: kind is key+value.
771 // ii. Let entry be ! CreateArrayFromList(« key, value »).
772 // iii. Append entry to properties.
773 PropertyNameKind::KeyAndValue => properties.push(
774 Array::create_array_from_list(
775 [key_str.into(), self.get(key.clone(), context)?],
776 context,
777 )
778 .into(),
779 ),
780 }
781 }
782 }
783 }
784
785 // 5. Return properties.
786 Ok(properties)
787 }
788
789 /// Abstract operation `GetMethod ( V, P )`
790 ///
791 /// Retrieves the value of a specific property, when the value of the property is expected to be a function.
792 ///
793 /// More information:
794 /// - [ECMAScript reference][spec]
795 ///
796 /// [spec]: https://tc39.es/ecma262/#sec-getmethod
797 pub(crate) fn get_method<K>(&self, key: K, context: &mut Context) -> JsResult<Option<Self>>
798 where
799 K: Into<PropertyKey>,
800 {
801 // Note: The spec specifies this function for JsValue.
802 // It is implemented for JsObject for convenience.
803
804 // 1. Assert: IsPropertyKey(P) is true.
805 // 2. Let func be ? GetV(V, P).
806
807 match self
808 .__get__(
809 &key.into(),
810 self.clone().into(),
811 &mut InternalMethodPropertyContext::new(context),
812 )?
813 .variant()
814 {
815 // 3. If func is either undefined or null, return undefined.
816 JsVariant::Undefined | JsVariant::Null => Ok(None),
817 // 5. Return func.
818 JsVariant::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())),
819 // 4. If IsCallable(func) is false, throw a TypeError exception.
820 _ => Err(JsNativeError::typ()
821 .with_message("value returned for property of object is not a function")
822 .into()),
823 }
824 }
825
826 /// Abstract operation `IsArray ( argument )`
827 ///
828 /// Check if a value is an array.
829 ///
830 /// More information:
831 /// - [ECMAScript reference][spec]
832 ///
833 /// [spec]: https://tc39.es/ecma262/#sec-isarray
834 pub(crate) fn is_array_abstract(&self) -> JsResult<bool> {
835 // Note: The spec specifies this function for JsValue.
836 // It is implemented for JsObject for convenience.
837
838 // 2. If argument is an Array exotic object, return true.
839 if self.is_array() {
840 return Ok(true);
841 }
842
843 // 3. If argument is a Proxy exotic object, then
844 if let Some(proxy) = self.downcast_ref::<Proxy>() {
845 // a. If argument.[[ProxyHandler]] is null, throw a TypeError exception.
846 // b. Let target be argument.[[ProxyTarget]].
847 let (target, _) = proxy.try_data()?;
848
849 // c. Return ? IsArray(target).
850 return target.is_array_abstract();
851 }
852
853 // 4. Return false.
854 Ok(false)
855 }
856
857 /// Abstract operation [`GetFunctionRealm`][spec].
858 ///
859 /// [spec]: https://tc39.es/ecma262/#sec-getfunctionrealm
860 pub(crate) fn get_function_realm(&self, context: &mut Context) -> JsResult<Realm> {
861 if let Some(fun) = self.downcast_ref::<OrdinaryFunction>() {
862 return Ok(fun.realm().clone());
863 }
864
865 if let Some(f) = self.downcast_ref::<NativeFunctionObject>() {
866 return Ok(f.realm.clone().unwrap_or_else(|| context.realm().clone()));
867 }
868
869 if let Some(bound) = self.downcast_ref::<BoundFunction>() {
870 let fun = bound.target_function().clone();
871 return fun.get_function_realm(context);
872 }
873
874 if let Some(proxy) = self.downcast_ref::<Proxy>() {
875 let (fun, _) = proxy.try_data()?;
876 return fun.get_function_realm(context);
877 }
878
879 Ok(context.realm().clone())
880 }
881
882 /// `7.3.26 CopyDataProperties ( target, source, excludedItems )`
883 ///
884 /// More information:
885 /// - [ECMAScript reference][spec]
886 ///
887 /// [spec]: https://tc39.es/ecma262/#sec-copydataproperties
888 pub fn copy_data_properties<K>(
889 &self,
890 source: &JsValue,
891 excluded_keys: Vec<K>,
892 context: &mut Context,
893 ) -> JsResult<()>
894 where
895 K: Into<PropertyKey>,
896 {
897 let context = &mut InternalMethodPropertyContext::new(context);
898
899 // 1. Assert: Type(target) is Object.
900 // 2. Assert: excludedItems is a List of property keys.
901 // 3. If source is undefined or null, return target.
902 if source.is_null_or_undefined() {
903 return Ok(());
904 }
905
906 // 4. Let from be ! ToObject(source).
907 let from = source
908 .to_object(context)
909 .expect("function ToObject should never complete abruptly here");
910
911 // 5. Let keys be ? from.[[OwnPropertyKeys]]().
912 // 6. For each element nextKey of keys, do
913 let excluded_keys: Vec<PropertyKey> = excluded_keys.into_iter().map(Into::into).collect();
914 for key in from.__own_property_keys__(context)? {
915 // a. Let excluded be false.
916 let mut excluded = false;
917
918 // b. For each element e of excludedItems, do
919 for e in &excluded_keys {
920 // i. If SameValue(e, nextKey) is true, then
921 if *e == key {
922 // 1. Set excluded to true.
923 excluded = true;
924 break;
925 }
926 }
927 // c. If excluded is false, then
928 if !excluded {
929 // i. Let desc be ? from.[[GetOwnProperty]](nextKey).
930 let desc = from.__get_own_property__(&key, context)?;
931
932 // ii. If desc is not undefined and desc.[[Enumerable]] is true, then
933 if let Some(desc) = desc
934 && let Some(enumerable) = desc.enumerable()
935 && enumerable
936 {
937 // 1. Let propValue be ? Get(from, nextKey).
938 let prop_value = from.__get__(&key, from.clone().into(), context)?;
939
940 // 2. Perform ! CreateDataPropertyOrThrow(target, nextKey, propValue).
941 self.create_data_property_or_throw(key, prop_value, context)
942 .expect("CreateDataPropertyOrThrow should never complete abruptly here");
943 }
944 }
945 }
946
947 // 7. Return target.
948 Ok(())
949 }
950
951 /// Abstract operation `PrivateElementFind ( O, P )`
952 ///
953 /// Get the private element from an object.
954 ///
955 /// More information:
956 /// - [ECMAScript specification][spec]
957 ///
958 /// [spec]: https://tc39.es/ecma262/#sec-privateelementfind
959 #[allow(clippy::similar_names)]
960 pub(crate) fn private_element_find(
961 &self,
962 name: &PrivateName,
963 is_getter: bool,
964 is_setter: bool,
965 ) -> Option<PrivateElement> {
966 // 1. If O.[[PrivateElements]] contains a PrivateElement whose [[Key]] is P, then
967 for (key, value) in &self.borrow().private_elements {
968 if key == name {
969 // a. Let entry be that PrivateElement.
970 // b. Return entry.
971 if let PrivateElement::Accessor { getter, setter } = value {
972 if getter.is_some() && is_getter || setter.is_some() && is_setter {
973 return Some(value.clone());
974 }
975 } else {
976 return Some(value.clone());
977 }
978 }
979 }
980
981 // 2. Return empty.
982 None
983 }
984
985 /// Abstract operation `PrivateFieldAdd ( O, P, value )`
986 ///
987 /// Add private field to an object.
988 ///
989 /// More information:
990 /// - [ECMAScript specification][spec]
991 ///
992 /// [spec]: https://tc39.es/ecma262/#sec-privatefieldadd
993 pub(crate) fn private_field_add(
994 &self,
995 name: &PrivateName,
996 value: JsValue,
997 context: &mut Context,
998 ) -> JsResult<()> {
999 // 1. If the host is a web browser, then
1000 // a. Perform ? HostEnsureCanAddPrivateElement(O).
1001 context
1002 .host_hooks()
1003 .ensure_can_add_private_element(self, context)?;
1004
1005 // 2. If ? IsExtensible(O) is false, throw a TypeError exception.
1006 // NOTE: From <https://tc39.es/proposal-nonextensible-applies-to-private/#sec-privatefieldadd>
1007 if !self.is_extensible(context)? {
1008 return Err(js_error!(
1009 TypeError: "cannot add private field to non-extensible class instance"
1010 ));
1011 }
1012
1013 // 3. Let entry be PrivateElementFind(O, P).
1014 let entry = self.private_element_find(name, false, false);
1015
1016 // 4. If entry is not empty, throw a TypeError exception.
1017 if entry.is_some() {
1018 return Err(js_error!(TypeError: "private field already exists on prototype"));
1019 }
1020
1021 // 5. Append PrivateElement { [[Key]]: P, [[Kind]]: field, [[Value]]: value } to O.[[PrivateElements]].
1022 self.borrow_mut()
1023 .private_elements
1024 .push((name.clone(), PrivateElement::Field(value)));
1025
1026 // 5. Return unused.
1027 Ok(())
1028 }
1029
1030 /// Abstract operation `PrivateMethodOrAccessorAdd ( O, method )`
1031 ///
1032 /// Add private method or accessor to an object.
1033 ///
1034 /// More information:
1035 /// - [ECMAScript specification][spec]
1036 ///
1037 /// [spec]: https://tc39.es/ecma262/#sec-privatemethodoraccessoradd
1038 pub(crate) fn private_method_or_accessor_add(
1039 &self,
1040 name: &PrivateName,
1041 method: &PrivateElement,
1042 context: &mut Context,
1043 ) -> JsResult<()> {
1044 // 1. Assert: method.[[Kind]] is either method or accessor.
1045 assert!(matches!(
1046 method,
1047 PrivateElement::Method(_) | PrivateElement::Accessor { .. }
1048 ));
1049
1050 let (getter, setter) = if let PrivateElement::Accessor { getter, setter } = method {
1051 (getter.is_some(), setter.is_some())
1052 } else {
1053 (false, false)
1054 };
1055
1056 // 2. If the host is a web browser, then
1057 // a. Perform ? HostEnsureCanAddPrivateElement(O).
1058 context
1059 .host_hooks()
1060 .ensure_can_add_private_element(self, context)?;
1061
1062 // 3. If ? IsExtensible(O) is false, throw a TypeError exception.
1063 // NOTE: From <https://tc39.es/proposal-nonextensible-applies-to-private/#sec-privatemethodoraccessoradd>
1064 if !self.is_extensible(context)? {
1065 return if getter || setter {
1066 Err(js_error!(
1067 TypeError: "cannot add private accessor to non-extensible class instance"
1068 ))
1069 } else {
1070 Err(js_error!(
1071 TypeError: "cannot add private method to non-extensible class instance"
1072 ))
1073 };
1074 }
1075
1076 // 3. Let entry be PrivateElementFind(O, method.[[Key]]).
1077 let entry = self.private_element_find(name, getter, setter);
1078
1079 // 4. If entry is not empty, throw a TypeError exception.
1080 if entry.is_some() {
1081 return if getter || setter {
1082 Err(js_error!(
1083 TypeError: "private accessor already exists on class instance"
1084 ))
1085 } else {
1086 Err(js_error!(
1087 TypeError: "private method already exists on class instance"
1088 ))
1089 };
1090 }
1091
1092 // 5. Append method to O.[[PrivateElements]].
1093 self.borrow_mut()
1094 .append_private_element(name.clone(), method.clone());
1095
1096 // 6. Return unused.
1097 Ok(())
1098 }
1099
1100 /// Abstract operation `PrivateGet ( O, P )`
1101 ///
1102 /// Get the value of a private element.
1103 ///
1104 /// More information:
1105 /// - [ECMAScript specification][spec]
1106 ///
1107 /// [spec]: https://tc39.es/ecma262/#sec-privateget
1108 pub(crate) fn private_get(
1109 &self,
1110 name: &PrivateName,
1111 context: &mut Context,
1112 ) -> JsResult<JsValue> {
1113 // 1. Let entry be PrivateElementFind(O, P).
1114 let entry = self.private_element_find(name, true, true);
1115
1116 match &entry {
1117 // 2. If entry is empty, throw a TypeError exception.
1118 None => Err(JsNativeError::typ()
1119 .with_message("Private element does not exist on object")
1120 .into()),
1121
1122 // 3. If entry.[[Kind]] is field or method, then
1123 // a. Return entry.[[Value]].
1124 Some(PrivateElement::Field(value)) => Ok(value.clone()),
1125 Some(PrivateElement::Method(value)) => Ok(value.clone().into()),
1126
1127 // 4. Assert: entry.[[Kind]] is accessor.
1128 Some(PrivateElement::Accessor { getter, .. }) => {
1129 // 5. If entry.[[Get]] is undefined, throw a TypeError exception.
1130 // 6. Let getter be entry.[[Get]].
1131 let getter = getter.as_ref().ok_or_else(|| {
1132 JsNativeError::typ()
1133 .with_message("private property was defined without a getter")
1134 })?;
1135
1136 // 7. Return ? Call(getter, O).
1137 getter.call(&self.clone().into(), &[], context)
1138 }
1139 }
1140 }
1141
1142 /// Abstract operation `PrivateSet ( O, P, value )`
1143 ///
1144 /// Set the value of a private element.
1145 ///
1146 /// More information:
1147 /// - [ECMAScript specification][spec]
1148 ///
1149 /// [spec]: https://tc39.es/ecma262/#sec-privateset
1150 pub(crate) fn private_set(
1151 &self,
1152 name: &PrivateName,
1153 value: JsValue,
1154 context: &mut Context,
1155 ) -> JsResult<()> {
1156 // 1. Let entry be PrivateElementFind(O, P).
1157 // Note: This function is inlined here for mutable access.
1158 let mut object_mut = self.borrow_mut();
1159 let entry = object_mut
1160 .private_elements
1161 .iter_mut()
1162 .find_map(|(key, value)| if key == name { Some(value) } else { None });
1163
1164 match entry {
1165 // 2. If entry is empty, throw a TypeError exception.
1166 None => {
1167 return Err(JsNativeError::typ()
1168 .with_message("Private element does not exist on object")
1169 .into());
1170 }
1171
1172 // 3. If entry.[[Kind]] is field, then
1173 // a. Set entry.[[Value]] to value.
1174 Some(PrivateElement::Field(field)) => {
1175 *field = value;
1176 }
1177
1178 // 4. Else if entry.[[Kind]] is method, then
1179 // a. Throw a TypeError exception.
1180 Some(PrivateElement::Method(_)) => {
1181 return Err(JsNativeError::typ()
1182 .with_message("private method is not writable")
1183 .into());
1184 }
1185
1186 // 5. Else,
1187 Some(PrivateElement::Accessor { setter, .. }) => {
1188 // a. Assert: entry.[[Kind]] is accessor.
1189 // b. If entry.[[Set]] is undefined, throw a TypeError exception.
1190 // c. Let setter be entry.[[Set]].
1191 let setter = setter.clone().ok_or_else(|| {
1192 JsNativeError::typ()
1193 .with_message("private property was defined without a setter")
1194 })?;
1195
1196 // d. Perform ? Call(setter, O, « value »).
1197 drop(object_mut);
1198 setter.call(&self.clone().into(), &[value], context)?;
1199 }
1200 }
1201
1202 // 6. Return unused.
1203 Ok(())
1204 }
1205
1206 /// Abstract operation `DefineField ( receiver, fieldRecord )`
1207 ///
1208 /// Define a field on an object.
1209 ///
1210 /// More information:
1211 /// - [ECMAScript specification][spec]
1212 ///
1213 /// [spec]: https://tc39.es/ecma262/#sec-definefield
1214 pub(crate) fn define_field(
1215 &self,
1216 field_record: &ClassFieldDefinition,
1217 context: &mut Context,
1218 ) -> JsResult<()> {
1219 // 2. Let initializer be fieldRecord.[[Initializer]].
1220 let initializer = match field_record {
1221 ClassFieldDefinition::Public(_, function, _)
1222 | ClassFieldDefinition::Private(_, function) => function,
1223 };
1224
1225 // 3. If initializer is not empty, then
1226 // a. Let initValue be ? Call(initializer, receiver).
1227 // 4. Else, let initValue be undefined.
1228 let init_value = initializer.call(&self.clone().into(), &[], context)?;
1229
1230 match field_record {
1231 // 1. Let fieldName be fieldRecord.[[Name]].
1232 // 5. If fieldName is a Private Name, then
1233 ClassFieldDefinition::Private(field_name, _) => {
1234 // a. Perform ? PrivateFieldAdd(receiver, fieldName, initValue).
1235 self.private_field_add(field_name, init_value, context)?;
1236 }
1237 // 1. Let fieldName be fieldRecord.[[Name]].
1238 // 6. Else,
1239 ClassFieldDefinition::Public(field_name, _, function_name) => {
1240 if let Some(function_name) = function_name {
1241 set_function_name(
1242 &init_value
1243 .as_object()
1244 .js_expect("init value must be a function object")?,
1245 function_name,
1246 None,
1247 context,
1248 )?;
1249 }
1250
1251 // a. Assert: IsPropertyKey(fieldName) is true.
1252 // b. Perform ? CreateDataPropertyOrThrow(receiver, fieldName, initValue).
1253 self.create_data_property_or_throw(field_name.clone(), init_value, context)?;
1254 }
1255 }
1256
1257 // 7. Return unused.
1258 Ok(())
1259 }
1260
1261 /// Abstract operation `InitializeInstanceElements ( O, constructor )`
1262 ///
1263 /// Add private methods and fields from a class constructor to an object.
1264 ///
1265 /// More information:
1266 /// - [ECMAScript specification][spec]
1267 ///
1268 /// [spec]: https://tc39.es/ecma262/#sec-initializeinstanceelements
1269 pub(crate) fn initialize_instance_elements(
1270 &self,
1271 constructor: &Self,
1272 context: &mut Context,
1273 ) -> JsResult<()> {
1274 let constructor_function = constructor
1275 .downcast_ref::<OrdinaryFunction>()
1276 .js_expect("class constructor must be function object")?;
1277
1278 // 1. Let methods be the value of constructor.[[PrivateMethods]].
1279 // 2. For each PrivateElement method of methods, do
1280 for (name, method) in constructor_function.get_private_methods() {
1281 // a. Perform ? PrivateMethodOrAccessorAdd(O, method).
1282 self.private_method_or_accessor_add(name, method, context)?;
1283 }
1284
1285 // 3. Let fields be the value of constructor.[[Fields]].
1286 // 4. For each element fieldRecord of fields, do
1287 for field_record in constructor_function.get_fields() {
1288 // a. Perform ? DefineField(O, fieldRecord).
1289 self.define_field(field_record, context)?;
1290 }
1291
1292 // 5. Return unused.
1293 Ok(())
1294 }
1295
1296 /// Abstract operation `Invoke ( V, P [ , argumentsList ] )`
1297 ///
1298 /// Calls a method property of an ECMAScript object.
1299 ///
1300 /// Equivalent to the [`JsValue::invoke`] method, but specialized for objects.
1301 ///
1302 /// More information:
1303 /// - [ECMAScript reference][spec]
1304 ///
1305 /// [spec]: https://tc39.es/ecma262/#sec-invoke
1306 pub(crate) fn invoke<K>(
1307 &self,
1308 key: K,
1309 args: &[JsValue],
1310 context: &mut Context,
1311 ) -> JsResult<JsValue>
1312 where
1313 K: Into<PropertyKey>,
1314 {
1315 let this_value: JsValue = self.clone().into();
1316
1317 // 1. If argumentsList is not present, set argumentsList to a new empty List.
1318 // 2. Let func be ? GetV(V, P).
1319 let func = self.__get__(
1320 &key.into(),
1321 this_value.clone(),
1322 &mut InternalMethodPropertyContext::new(context),
1323 )?;
1324
1325 // 3. Return ? Call(func, V, argumentsList)
1326 func.call(&this_value, args, context)
1327 }
1328}
1329
1330impl JsValue {
1331 /// Abstract operation `GetV ( V, P )`.
1332 ///
1333 /// Retrieves the value of a specific property of an ECMAScript language value. If the value is
1334 /// not an object, the property lookup is performed using a wrapper object appropriate for the
1335 /// type of the value.
1336 ///
1337 /// More information:
1338 /// - [ECMAScript reference][spec]
1339 ///
1340 /// [spec]: https://tc39.es/ecma262/#sec-getv
1341 pub(crate) fn get_v<K>(&self, key: K, context: &mut Context) -> JsResult<Self>
1342 where
1343 K: Into<PropertyKey>,
1344 {
1345 // 1. Let O be ? ToObject(V).
1346 let o = self.to_object(context)?;
1347
1348 // 2. Return ? O.[[Get]](P, V).
1349
1350 o.__get__(
1351 &key.into(),
1352 self.clone(),
1353 &mut InternalMethodPropertyContext::new(context),
1354 )
1355 }
1356
1357 /// Abstract operation `GetMethod ( V, P )`
1358 ///
1359 /// Retrieves the value of a specific property, when the value of the property is expected to be a function.
1360 ///
1361 /// More information:
1362 /// - [ECMAScript reference][spec]
1363 ///
1364 /// [spec]: https://tc39.es/ecma262/#sec-getmethod
1365 pub(crate) fn get_method<K>(&self, key: K, context: &mut Context) -> JsResult<Option<JsObject>>
1366 where
1367 K: Into<PropertyKey>,
1368 {
1369 // Note: The spec specifies this function for JsValue.
1370 // The main part of the function is implemented for JsObject.
1371 // 1. Let func be ? GetV(V, P).
1372 match self.get_v(key, context)?.variant() {
1373 // 3. If func is either undefined or null, return undefined.
1374 JsVariant::Undefined | JsVariant::Null => Ok(None),
1375 // 5. Return func.
1376 JsVariant::Object(obj) if obj.is_callable() => Ok(Some(obj.clone())),
1377 // 4. If IsCallable(func) is false, throw a TypeError exception.
1378 _ => Err(JsNativeError::typ()
1379 .with_message("value returned for property of object is not a function")
1380 .into()),
1381 }
1382 }
1383
1384 /// It is used to create List value whose elements are provided by the indexed properties of
1385 /// self.
1386 ///
1387 /// More information:
1388 /// - [ECMAScript reference][spec]
1389 ///
1390 /// [spec]: https://tc39.es/ecma262/#sec-createlistfromarraylike
1391 pub(crate) fn create_list_from_array_like(
1392 &self,
1393 element_types: &[Type],
1394 context: &mut Context,
1395 ) -> JsResult<Vec<Self>> {
1396 // 1. If elementTypes is not present, set elementTypes to « Undefined, Null, Boolean, String, Symbol, Number, BigInt, Object ».
1397 let types = if element_types.is_empty() {
1398 &[
1399 Type::Undefined,
1400 Type::Null,
1401 Type::Boolean,
1402 Type::String,
1403 Type::Symbol,
1404 Type::Number,
1405 Type::BigInt,
1406 Type::Object,
1407 ]
1408 } else {
1409 element_types
1410 };
1411
1412 // 2. If Type(obj) is not Object, throw a TypeError exception.
1413 let obj = self.as_object().ok_or_else(|| {
1414 JsNativeError::typ().with_message("cannot create list from a primitive")
1415 })?;
1416
1417 // 3. Let len be ? LengthOfArrayLike(obj).
1418 let len = obj.length_of_array_like(context)?;
1419
1420 // 4. Let list be a new empty List.
1421 let mut list = Vec::with_capacity(len as usize);
1422
1423 // 5. Let index be 0.
1424 // 6. Repeat, while index < len,
1425 for index in 0..len {
1426 // a. Let indexName be ! ToString(𝔽(index)).
1427 // b. Let next be ? Get(obj, indexName).
1428 let next = obj.get(index, context)?;
1429 // c. If Type(next) is not an element of elementTypes, throw a TypeError exception.
1430 if !types.contains(&next.get_type()) {
1431 return Err(JsNativeError::typ().with_message("bad type").into());
1432 }
1433 // d. Append next as the last element of list.
1434 list.push(next.clone());
1435 // e. Set index to index + 1.
1436 }
1437
1438 // 7. Return list.
1439 Ok(list)
1440 }
1441
1442 /// Abstract operation [`Call ( F, V [ , argumentsList ] )`][call].
1443 ///
1444 /// Calls this value if the value is a callable object.
1445 ///
1446 /// # Note
1447 ///
1448 /// It is almost always better to try to obtain a callable object first with [`JsValue::as_callable`],
1449 /// then calling [`JsObject::call`], since that allows reusing the unwrapped function for other
1450 /// operations. This method is only an utility method for when the spec directly uses `Call`
1451 /// without using the value as a proper object.
1452 ///
1453 /// [call]: https://tc39.es/ecma262/#sec-call
1454 #[inline]
1455 #[cfg_attr(feature = "native-backtrace", track_caller)]
1456 pub(crate) fn call(&self, this: &Self, args: &[Self], context: &mut Context) -> JsResult<Self> {
1457 let Some(object) = self.as_object() else {
1458 return Err(JsNativeError::typ()
1459 .with_message(format!(
1460 "value with type `{}` is not callable",
1461 self.type_of()
1462 ))
1463 .into());
1464 };
1465
1466 object.call(this, args, context)
1467 }
1468
1469 /// Abstract operation `( V, P [ , argumentsList ] )`
1470 ///
1471 /// Calls a method property of an ECMAScript language value.
1472 ///
1473 /// More information:
1474 /// - [ECMAScript reference][spec]
1475 ///
1476 /// [spec]: https://tc39.es/ecma262/#sec-invoke
1477 #[cfg_attr(feature = "native-backtrace", track_caller)]
1478 pub(crate) fn invoke<K>(&self, key: K, args: &[Self], context: &mut Context) -> JsResult<Self>
1479 where
1480 K: Into<PropertyKey>,
1481 {
1482 // 1. If argumentsList is not present, set argumentsList to a new empty List.
1483 // 2. Let func be ? GetV(V, P).
1484 let func = self.get_v(key, context)?;
1485
1486 // 3. Return ? Call(func, V, argumentsList)
1487 func.call(self, args, context)
1488 }
1489
1490 /// Abstract operation `OrdinaryHasInstance ( C, O )`
1491 ///
1492 /// More information:
1493 /// - [ECMAScript reference][spec]
1494 ///
1495 /// [spec]: https://tc39.es/ecma262/#sec-ordinaryhasinstance
1496 pub fn ordinary_has_instance(
1497 function: &Self,
1498 object: &Self,
1499 context: &mut Context,
1500 ) -> JsResult<bool> {
1501 // 1. If IsCallable(C) is false, return false.
1502 let Some(function) = function.as_callable() else {
1503 return Ok(false);
1504 };
1505
1506 // 2. If C has a [[BoundTargetFunction]] internal slot, then
1507 if let Some(bound_function) = function.downcast_ref::<BoundFunction>() {
1508 // a. Let BC be C.[[BoundTargetFunction]].
1509 // b. Return ? InstanceofOperator(O, BC).
1510 return object.instance_of(&bound_function.target_function().clone().into(), context);
1511 }
1512
1513 let Some(mut object) = object.as_object() else {
1514 // 3. If Type(O) is not Object, return false.
1515 return Ok(false);
1516 };
1517
1518 // 4. Let P be ? Get(C, "prototype").
1519 let prototype = function.get(PROTOTYPE, context)?;
1520
1521 // 5. If Type(P) is not Object, throw a TypeError exception.
1522 let prototype = prototype.as_object().ok_or_else(|| {
1523 JsNativeError::typ()
1524 .with_message("function has non-object prototype in instanceof check")
1525 })?;
1526
1527 // 6. Repeat,
1528 loop {
1529 // a. Set O to ? O.[[GetPrototypeOf]]().
1530 object = match object
1531 .__get_prototype_of__(&mut InternalMethodPropertyContext::new(context))?
1532 {
1533 Some(obj) => obj,
1534 // b. If O is null, return false.
1535 None => return Ok(false),
1536 };
1537
1538 // c. If SameValue(P, O) is true, return true.
1539 if JsObject::equals(&object, &prototype) {
1540 return Ok(true);
1541 }
1542 }
1543 }
1544}