1use datatypes::ObjectData;
6pub use jsobject::{RecursionLimiter, Ref, RefMut};
7pub use operations::IntegrityLevel;
8pub use property_map::*;
9use thin_vec::ThinVec;
10
11use self::{internal_methods::ORDINARY_INTERNAL_METHODS, shape::Shape};
12use crate::{
13 Context, JsString, JsSymbol, JsValue,
14 builtins::{OrdinaryObject, function::ConstructorKind},
15 context::intrinsics::StandardConstructor,
16 js_string,
17 native_function::{NativeFunction, NativeFunctionObject},
18 property::{Attribute, PropertyDescriptor, PropertyKey},
19 realm::Realm,
20 string::StaticJsStrings,
21};
22
23use boa_gc::{Finalize, Trace};
24use std::{
25 any::{Any, TypeId},
26 fmt::Debug,
27};
28
29#[cfg(test)]
30mod tests;
31
32pub(crate) mod internal_methods;
33
34pub mod builtins;
35mod datatypes;
36mod jsobject;
37mod operations;
38mod property_map;
39
40pub mod shape;
41
42pub(crate) use builtins::*;
43
44pub use datatypes::JsData;
45pub use jsobject::*;
46
47pub const CONSTRUCTOR: JsString = js_string!("constructor");
49
50pub const PROTOTYPE: JsString = js_string!("prototype");
52
53pub type JsPrototype = Option<JsObject>;
57
58pub(crate) type ObjectStorage = Vec<JsValue>;
62
63pub trait NativeObject: Any + Trace + JsData {
67 fn as_any(&self) -> &dyn Any;
69
70 fn as_mut_any(&mut self) -> &mut dyn Any;
72
73 fn type_name_of_value(&self) -> &'static str;
75}
76
77impl<T: Any + Trace + JsData> NativeObject for T {
79 fn as_any(&self) -> &dyn Any {
80 self
81 }
82
83 fn as_mut_any(&mut self) -> &mut dyn Any {
84 self
85 }
86
87 fn type_name_of_value(&self) -> &'static str {
88 fn name_of_val<T: ?Sized>(_val: &T) -> &'static str {
89 std::any::type_name::<T>()
90 }
91
92 name_of_val(self)
93 }
94}
95
96impl dyn NativeObject {
98 #[inline]
100 pub fn is<T: NativeObject>(&self) -> bool {
101 let t = TypeId::of::<T>();
103
104 let concrete = self.type_id();
106
107 t == concrete
109 }
110
111 #[inline]
114 pub fn downcast_ref<T: NativeObject>(&self) -> Option<&T> {
115 if self.is::<T>() {
116 unsafe { Some(self.downcast_ref_unchecked()) }
120 } else {
121 None
122 }
123 }
124
125 #[inline]
128 pub fn downcast_mut<T: NativeObject>(&mut self) -> Option<&mut T> {
129 if self.is::<T>() {
130 unsafe { Some(self.downcast_mut_unchecked()) }
132 } else {
133 None
134 }
135 }
136
137 #[inline]
144 pub unsafe fn downcast_ref_unchecked<T: NativeObject>(&self) -> &T {
145 debug_assert!(self.is::<T>());
146 let ptr: *const dyn NativeObject = self;
147 unsafe { &*ptr.cast::<T>() }
149 }
150
151 #[inline]
158 pub unsafe fn downcast_mut_unchecked<T: NativeObject>(&mut self) -> &mut T {
159 debug_assert!(self.is::<T>());
160 let ptr: *mut dyn NativeObject = self;
162 unsafe { &mut *ptr.cast::<T>() }
163 }
164}
165
166#[derive(Debug, Finalize, Trace)]
168#[boa_gc(unsafe_no_drop)]
170#[repr(C)]
173pub struct Object<T: ?Sized> {
174 pub(crate) properties: PropertyMap,
176 pub(crate) extensible: bool,
178 private_elements: ThinVec<(PrivateName, PrivateElement)>,
180 data: ObjectData<T>,
182}
183
184impl<T: Default> Default for Object<T> {
185 fn default() -> Self {
186 Self {
187 properties: PropertyMap::default(),
188 extensible: true,
189 private_elements: ThinVec::new(),
190 data: ObjectData::default(),
191 }
192 }
193}
194
195#[derive(Clone, Debug, PartialEq, Eq, Trace, Finalize)]
197pub struct PrivateName {
198 description: JsString,
200
201 id: usize,
203}
204
205impl PrivateName {
206 pub(crate) const fn new(description: JsString, id: usize) -> Self {
208 Self { description, id }
209 }
210}
211
212#[derive(Clone, Debug, Trace, Finalize)]
214pub enum PrivateElement {
215 Field(JsValue),
217
218 Method(JsObject),
220
221 Accessor {
223 getter: Option<JsObject>,
225
226 setter: Option<JsObject>,
228 },
229}
230
231impl<T: ?Sized> Object<T> {
232 #[must_use]
234 pub const fn shape(&self) -> &Shape {
235 &self.properties.shape
236 }
237
238 #[inline]
240 #[must_use]
241 pub fn data(&self) -> &T {
242 self.data.as_ref()
243 }
244
245 #[inline]
247 #[must_use]
248 pub fn data_mut(&mut self) -> &mut T {
249 self.data.as_mut()
250 }
251
252 #[inline]
254 #[must_use]
255 pub fn prototype(&self) -> JsPrototype {
256 self.properties.shape.prototype()
257 }
258
259 #[track_caller]
265 pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {
266 let prototype = prototype.into();
267 if self.extensible {
268 self.properties.shape = self.properties.shape.change_prototype_transition(prototype);
269 true
270 } else {
271 self.prototype() == prototype
274 }
275 }
276
277 #[inline]
279 #[must_use]
280 pub const fn properties(&self) -> &PropertyMap {
281 &self.properties
282 }
283
284 #[inline]
285 pub(crate) fn properties_mut(&mut self) -> &mut PropertyMap {
286 &mut self.properties
287 }
288
289 pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> bool
294 where
295 K: Into<PropertyKey>,
296 P: Into<PropertyDescriptor>,
297 {
298 self.properties.insert(&key.into(), property.into())
299 }
300
301 #[inline]
305 pub(crate) fn remove(&mut self, key: &PropertyKey) -> bool {
306 self.properties.remove(key)
307 }
308
309 pub(crate) fn append_private_element(&mut self, name: PrivateName, element: PrivateElement) {
311 if let PrivateElement::Accessor { getter, setter } = &element {
312 for (key, value) in &mut self.private_elements {
313 if name == *key
314 && let PrivateElement::Accessor {
315 getter: existing_getter,
316 setter: existing_setter,
317 } = value
318 {
319 if existing_getter.is_none() {
320 existing_getter.clone_from(getter);
321 }
322 if existing_setter.is_none() {
323 existing_setter.clone_from(setter);
324 }
325 return;
326 }
327 }
328 }
329
330 self.private_elements.push((name, element));
331 }
332}
333
334#[derive(Debug, Clone)]
348pub struct FunctionBinding {
349 pub(crate) binding: PropertyKey,
350 pub(crate) name: JsString,
351}
352
353impl From<JsString> for FunctionBinding {
354 #[inline]
355 fn from(name: JsString) -> Self {
356 Self {
357 binding: name.clone().into(),
358 name,
359 }
360 }
361}
362
363impl From<JsSymbol> for FunctionBinding {
364 #[inline]
365 fn from(binding: JsSymbol) -> Self {
366 Self {
367 name: binding.fn_name(),
368 binding: binding.into(),
369 }
370 }
371}
372
373impl<B, N> From<(B, N)> for FunctionBinding
374where
375 B: Into<PropertyKey>,
376 N: Into<JsString>,
377{
378 fn from((binding, name): (B, N)) -> Self {
379 Self {
380 binding: binding.into(),
381 name: name.into(),
382 }
383 }
384}
385
386#[derive(Debug)]
388pub struct FunctionObjectBuilder<'realm> {
389 realm: &'realm Realm,
390 function: NativeFunction,
391 constructor: Option<ConstructorKind>,
392 name: JsString,
393 length: usize,
394}
395
396impl<'realm> FunctionObjectBuilder<'realm> {
397 #[inline]
399 #[must_use]
400 pub fn new(realm: &'realm Realm, function: NativeFunction) -> Self {
401 Self {
402 realm,
403 function,
404 constructor: None,
405 name: js_string!(),
406 length: 0,
407 }
408 }
409
410 #[must_use]
414 pub fn name<N>(mut self, name: N) -> Self
415 where
416 N: Into<JsString>,
417 {
418 self.name = name.into();
419 self
420 }
421
422 #[inline]
428 #[must_use]
429 pub const fn length(mut self, length: usize) -> Self {
430 self.length = length;
431 self
432 }
433
434 #[must_use]
438 pub fn constructor(mut self, yes: bool) -> Self {
439 self.constructor = yes.then_some(ConstructorKind::Base);
440 self
441 }
442
443 #[must_use]
445 pub fn build(self) -> JsFunction {
446 let object = self.realm.intrinsics().templates().function().create(
447 NativeFunctionObject {
448 f: self.function,
449 name: self.name.clone(),
450 constructor: self.constructor,
451 realm: Some(self.realm.clone()),
452 },
453 vec![self.length.into(), self.name.into()],
454 );
455
456 JsFunction::from_object_unchecked(object)
457 }
458}
459
460#[derive(Debug)]
494pub struct ObjectInitializer<'ctx> {
495 context: &'ctx mut Context,
496 object: JsObject,
497}
498
499impl<'ctx> ObjectInitializer<'ctx> {
500 #[inline]
502 pub fn new(context: &'ctx mut Context) -> Self {
503 let object = JsObject::with_object_proto(context.intrinsics());
504 Self { context, object }
505 }
506
507 pub fn with_native_data<T: NativeObject>(data: T, context: &'ctx mut Context) -> Self {
509 let object = JsObject::from_proto_and_data_with_shared_shape(
510 context.root_shape(),
511 context.intrinsics().constructors().object().prototype(),
512 data,
513 )
514 .upcast();
515 Self { context, object }
516 }
517
518 pub fn with_native_data_and_proto<T: NativeObject>(
520 data: T,
521 proto: JsObject,
522 context: &'ctx mut Context,
523 ) -> Self {
524 let object =
525 JsObject::from_proto_and_data_with_shared_shape(context.root_shape(), proto, data)
526 .upcast();
527 Self { context, object }
528 }
529
530 pub fn function<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
532 where
533 B: Into<FunctionBinding>,
534 {
535 let binding = binding.into();
536 let function = FunctionObjectBuilder::new(self.context.realm(), function)
537 .name(binding.name)
538 .length(length)
539 .constructor(false)
540 .build();
541
542 self.object.borrow_mut().insert(
543 binding.binding,
544 PropertyDescriptor::builder()
545 .value(function)
546 .writable(true)
547 .enumerable(false)
548 .configurable(true),
549 );
550 self
551 }
552
553 pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
555 where
556 K: Into<PropertyKey>,
557 V: Into<JsValue>,
558 {
559 let property = PropertyDescriptor::builder()
560 .value(value)
561 .writable(attribute.writable())
562 .enumerable(attribute.enumerable())
563 .configurable(attribute.configurable());
564 self.object.borrow_mut().insert(key, property);
565 self
566 }
567
568 pub fn accessor<K>(
574 &mut self,
575 key: K,
576 get: Option<JsFunction>,
577 set: Option<JsFunction>,
578 attribute: Attribute,
579 ) -> &mut Self
580 where
581 K: Into<PropertyKey>,
582 {
583 assert!(set.is_some() || get.is_some());
585
586 let property = PropertyDescriptor::builder()
587 .maybe_get(get)
588 .maybe_set(set)
589 .enumerable(attribute.enumerable())
590 .configurable(attribute.configurable());
591 self.object.borrow_mut().insert(key, property);
592 self
593 }
594
595 #[inline]
597 pub fn build(&mut self) -> JsObject {
598 self.object.clone()
599 }
600
601 #[inline]
603 pub fn context(&mut self) -> &mut Context {
604 self.context
605 }
606}
607
608#[derive(Debug)]
610pub struct ConstructorBuilder<'ctx> {
611 context: &'ctx mut Context,
612 function: NativeFunction,
613 constructor_object: Object<OrdinaryObject>,
614 has_prototype_property: bool,
615 prototype: Object<OrdinaryObject>,
616 name: JsString,
617 length: usize,
618 callable: bool,
619 kind: Option<ConstructorKind>,
620 inherit: Option<JsPrototype>,
621 custom_prototype: Option<JsPrototype>,
622}
623
624impl<'ctx> ConstructorBuilder<'ctx> {
625 #[inline]
627 pub fn new(context: &'ctx mut Context, function: NativeFunction) -> ConstructorBuilder<'ctx> {
628 Self {
629 context,
630 function,
631 constructor_object: Object {
632 data: ObjectData::new(OrdinaryObject),
633 properties: PropertyMap::default(),
634 extensible: true,
635 private_elements: ThinVec::new(),
636 },
637 prototype: Object {
638 data: ObjectData::new(OrdinaryObject),
639 properties: PropertyMap::default(),
640 extensible: true,
641 private_elements: ThinVec::new(),
642 },
643 length: 0,
644 name: js_string!(),
645 callable: true,
646 kind: Some(ConstructorKind::Base),
647 inherit: None,
648 custom_prototype: None,
649 has_prototype_property: true,
650 }
651 }
652
653 pub fn method<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
655 where
656 B: Into<FunctionBinding>,
657 {
658 let binding = binding.into();
659 let function = FunctionObjectBuilder::new(self.context.realm(), function)
660 .name(binding.name)
661 .length(length)
662 .constructor(false)
663 .build();
664
665 self.prototype.insert(
666 binding.binding,
667 PropertyDescriptor::builder()
668 .value(function)
669 .writable(true)
670 .enumerable(false)
671 .configurable(true),
672 );
673 self
674 }
675
676 pub fn static_method<B>(
678 &mut self,
679 function: NativeFunction,
680 binding: B,
681 length: usize,
682 ) -> &mut Self
683 where
684 B: Into<FunctionBinding>,
685 {
686 let binding = binding.into();
687 let function = FunctionObjectBuilder::new(self.context.realm(), function)
688 .name(binding.name)
689 .length(length)
690 .constructor(false)
691 .build();
692
693 self.constructor_object.insert(
694 binding.binding,
695 PropertyDescriptor::builder()
696 .value(function)
697 .writable(true)
698 .enumerable(false)
699 .configurable(true),
700 );
701 self
702 }
703
704 pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
706 where
707 K: Into<PropertyKey>,
708 V: Into<JsValue>,
709 {
710 let property = PropertyDescriptor::builder()
711 .value(value)
712 .writable(attribute.writable())
713 .enumerable(attribute.enumerable())
714 .configurable(attribute.configurable());
715 self.prototype.insert(key, property);
716 self
717 }
718
719 pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
721 where
722 K: Into<PropertyKey>,
723 V: Into<JsValue>,
724 {
725 let property = PropertyDescriptor::builder()
726 .value(value)
727 .writable(attribute.writable())
728 .enumerable(attribute.enumerable())
729 .configurable(attribute.configurable());
730 self.constructor_object.insert(key, property);
731 self
732 }
733
734 pub fn accessor<K>(
736 &mut self,
737 key: K,
738 get: Option<JsFunction>,
739 set: Option<JsFunction>,
740 attribute: Attribute,
741 ) -> &mut Self
742 where
743 K: Into<PropertyKey>,
744 {
745 let property = PropertyDescriptor::builder()
746 .maybe_get(get)
747 .maybe_set(set)
748 .enumerable(attribute.enumerable())
749 .configurable(attribute.configurable());
750 self.prototype.insert(key, property);
751 self
752 }
753
754 pub fn static_accessor<K>(
756 &mut self,
757 key: K,
758 get: Option<JsFunction>,
759 set: Option<JsFunction>,
760 attribute: Attribute,
761 ) -> &mut Self
762 where
763 K: Into<PropertyKey>,
764 {
765 let property = PropertyDescriptor::builder()
766 .maybe_get(get)
767 .maybe_set(set)
768 .enumerable(attribute.enumerable())
769 .configurable(attribute.configurable());
770 self.constructor_object.insert(key, property);
771 self
772 }
773
774 pub fn property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
776 where
777 K: Into<PropertyKey>,
778 P: Into<PropertyDescriptor>,
779 {
780 let property = property.into();
781 self.prototype.insert(key, property);
782 self
783 }
784
785 pub fn static_property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
787 where
788 K: Into<PropertyKey>,
789 P: Into<PropertyDescriptor>,
790 {
791 let property = property.into();
792 self.constructor_object.insert(key, property);
793 self
794 }
795
796 #[inline]
800 pub fn length(&mut self, length: usize) -> &mut Self {
801 self.length = length;
802 self
803 }
804
805 pub fn name<N>(&mut self, name: N) -> &mut Self
809 where
810 N: AsRef<str>,
811 {
812 self.name = name.as_ref().into();
813 self
814 }
815
816 #[inline]
820 pub fn callable(&mut self, callable: bool) -> &mut Self {
821 self.callable = callable;
822 self
823 }
824
825 #[inline]
829 pub fn constructor(&mut self, constructor: bool) -> &mut Self {
830 self.kind = constructor.then_some(ConstructorKind::Base);
831 self
832 }
833
834 pub fn inherit<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
839 self.inherit = Some(prototype.into());
840 self
841 }
842
843 pub fn custom_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
847 self.custom_prototype = Some(prototype.into());
848 self
849 }
850
851 #[inline]
855 pub fn has_prototype_property(&mut self, has_prototype_property: bool) -> &mut Self {
856 self.has_prototype_property = has_prototype_property;
857 self
858 }
859
860 #[inline]
862 pub fn context(&mut self) -> &mut Context {
863 self.context
864 }
865
866 #[must_use]
868 pub fn build(mut self) -> StandardConstructor {
869 let length = PropertyDescriptor::builder()
870 .value(self.length)
871 .writable(false)
872 .enumerable(false)
873 .configurable(true);
874 let name = PropertyDescriptor::builder()
875 .value(self.name.clone())
876 .writable(false)
877 .enumerable(false)
878 .configurable(true);
879
880 let prototype = {
881 if let Some(proto) = self.inherit.take() {
882 self.prototype.set_prototype(proto);
883 } else {
884 self.prototype.set_prototype(
885 self.context
886 .intrinsics()
887 .constructors()
888 .object()
889 .prototype(),
890 );
891 }
892
893 JsObject::from_object_and_vtable(self.prototype, &ORDINARY_INTERNAL_METHODS)
894 };
895
896 let constructor = {
897 let data = NativeFunctionObject {
898 f: self.function,
899 name: self.name.clone(),
900 constructor: self.kind,
901 realm: Some(self.context.realm().clone()),
902 };
903 let internal_methods = data.internal_methods();
904 let mut constructor = Object {
905 properties: self.constructor_object.properties,
906 extensible: self.constructor_object.extensible,
907 private_elements: self.constructor_object.private_elements,
908 data: ObjectData::new(data),
909 };
910
911 constructor.insert(StaticJsStrings::LENGTH, length);
912 constructor.insert(js_string!("name"), name);
913
914 if let Some(proto) = self.custom_prototype.take() {
915 constructor.set_prototype(proto);
916 } else {
917 constructor.set_prototype(
918 self.context
919 .intrinsics()
920 .constructors()
921 .function()
922 .prototype(),
923 );
924 }
925
926 if self.has_prototype_property {
927 constructor.insert(
928 PROTOTYPE,
929 PropertyDescriptor::builder()
930 .value(prototype.clone())
931 .writable(false)
932 .enumerable(false)
933 .configurable(false),
934 );
935 }
936
937 JsObject::from_object_and_vtable(constructor, internal_methods)
938 };
939
940 {
941 let mut prototype = prototype.borrow_mut();
942 prototype.insert(
943 CONSTRUCTOR,
944 PropertyDescriptor::builder()
945 .value(constructor.clone())
946 .writable(true)
947 .enumerable(false)
948 .configurable(true),
949 );
950 }
951
952 StandardConstructor::new(JsFunction::from_object_unchecked(constructor), prototype)
953 }
954}