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