pub use jsobject::{JsObject, RecursionLimiter, Ref, RefMut};
pub use operations::IntegrityLevel;
pub use property_map::*;
use self::internal_methods::{
arguments::ARGUMENTS_EXOTIC_INTERNAL_METHODS,
array::ARRAY_EXOTIC_INTERNAL_METHODS,
bound_function::{
BOUND_CONSTRUCTOR_EXOTIC_INTERNAL_METHODS, BOUND_FUNCTION_EXOTIC_INTERNAL_METHODS,
},
function::{CONSTRUCTOR_INTERNAL_METHODS, FUNCTION_INTERNAL_METHODS},
global::GLOBAL_INTERNAL_METHODS,
integer_indexed::INTEGER_INDEXED_EXOTIC_INTERNAL_METHODS,
proxy::{
PROXY_EXOTIC_INTERNAL_METHODS_ALL, PROXY_EXOTIC_INTERNAL_METHODS_BASIC,
PROXY_EXOTIC_INTERNAL_METHODS_WITH_CALL,
},
string::STRING_EXOTIC_INTERNAL_METHODS,
InternalObjectMethods, ORDINARY_INTERNAL_METHODS,
};
#[cfg(feature = "intl")]
use crate::builtins::intl::date_time_format::DateTimeFormat;
use crate::{
builtins::{
array::array_iterator::ArrayIterator,
array_buffer::ArrayBuffer,
async_generator::AsyncGenerator,
function::arguments::Arguments,
function::{
arguments::ParameterMap, BoundFunction, Captures, ConstructorKind, Function,
NativeFunctionSignature,
},
generator::Generator,
iterable::AsyncFromSyncIterator,
map::map_iterator::MapIterator,
map::ordered_map::OrderedMap,
object::for_in_iterator::ForInIterator,
proxy::Proxy,
regexp::regexp_string_iterator::RegExpStringIterator,
set::ordered_set::OrderedSet,
set::set_iterator::SetIterator,
string::string_iterator::StringIterator,
typed_array::integer_indexed_object::IntegerIndexed,
DataView, Date, Promise, RegExp,
},
context::intrinsics::StandardConstructor,
property::{Attribute, PropertyDescriptor, PropertyKey},
Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue,
};
use boa_gc::{custom_trace, Finalize, Trace};
use boa_interner::Sym;
use rustc_hash::FxHashMap;
use std::{
any::Any,
fmt::{self, Debug, Display},
ops::{Deref, DerefMut},
};
#[cfg(test)]
mod tests;
pub(crate) mod internal_methods;
mod jsarray;
mod jsarraybuffer;
mod jsfunction;
mod jsmap;
mod jsmap_iterator;
mod jsobject;
mod jsproxy;
mod jsset;
mod jsset_iterator;
mod jstypedarray;
mod operations;
mod property_map;
pub use jsarray::*;
pub use jsarraybuffer::*;
pub use jsfunction::*;
pub use jsmap::*;
pub use jsmap_iterator::*;
pub use jsproxy::*;
pub use jsset::*;
pub use jsset_iterator::*;
pub use jstypedarray::*;
pub(crate) trait JsObjectType:
Into<JsValue> + Into<JsObject> + Deref<Target = JsObject>
{
}
pub static PROTOTYPE: &str = "prototype";
pub type JsPrototype = Option<JsObject>;
pub trait NativeObject: Debug + Any + Trace {
fn as_any(&self) -> &dyn Any;
fn as_mut_any(&mut self) -> &mut dyn Any;
}
impl<T: Any + Debug + Trace> NativeObject for T {
#[inline]
fn as_any(&self) -> &dyn Any {
self as &dyn Any
}
#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any
}
}
#[derive(Debug, Finalize)]
pub struct Object {
pub data: ObjectData,
properties: PropertyMap,
prototype: JsPrototype,
extensible: bool,
private_elements: FxHashMap<Sym, PrivateElement>,
}
unsafe impl Trace for Object {
boa_gc::custom_trace!(this, {
mark(&this.data);
mark(&this.properties);
mark(&this.prototype);
for elem in this.private_elements.values() {
mark(elem);
}
});
}
#[derive(Clone, Debug, Trace, Finalize)]
pub enum PrivateElement {
Field(JsValue),
Method(JsObject),
Accessor {
getter: Option<JsObject>,
setter: Option<JsObject>,
},
}
#[derive(Trace, Finalize)]
pub struct ObjectData {
kind: ObjectKind,
internal_methods: &'static InternalObjectMethods,
}
#[derive(Debug, Finalize)]
pub enum ObjectKind {
AsyncFromSyncIterator(AsyncFromSyncIterator),
AsyncGenerator(AsyncGenerator),
AsyncGeneratorFunction(Function),
Array,
ArrayIterator(ArrayIterator),
ArrayBuffer(ArrayBuffer),
Map(OrderedMap<JsValue>),
MapIterator(MapIterator),
RegExp(Box<RegExp>),
RegExpStringIterator(RegExpStringIterator),
BigInt(JsBigInt),
Boolean(bool),
DataView(DataView),
ForInIterator(ForInIterator),
Function(Function),
BoundFunction(BoundFunction),
Generator(Generator),
GeneratorFunction(Function),
Set(OrderedSet<JsValue>),
SetIterator(SetIterator),
String(JsString),
StringIterator(StringIterator),
Number(f64),
Symbol(JsSymbol),
Error,
Ordinary,
Proxy(Proxy),
Date(Date),
Global,
Arguments(Arguments),
NativeObject(Box<dyn NativeObject>),
IntegerIndexed(IntegerIndexed),
#[cfg(feature = "intl")]
DateTimeFormat(Box<DateTimeFormat>),
Promise(Promise),
}
unsafe impl Trace for ObjectKind {
custom_trace! {this, {
match this {
Self::AsyncFromSyncIterator(a) => mark(a),
Self::ArrayIterator(i) => mark(i),
Self::ArrayBuffer(b) => mark(b),
Self::Map(m) => mark(m),
Self::MapIterator(i) => mark(i),
Self::RegExpStringIterator(i) => mark(i),
Self::DataView(v) => mark(v),
Self::ForInIterator(i) => mark(i),
Self::Function(f) | Self::GeneratorFunction(f) | Self::AsyncGeneratorFunction(f) => mark(f),
Self::BoundFunction(f) => mark(f),
Self::Generator(g) => mark(g),
Self::Set(s) => mark(s),
Self::SetIterator(i) => mark(i),
Self::StringIterator(i) => mark(i),
Self::Proxy(p) => mark(p),
Self::Arguments(a) => mark(a),
Self::NativeObject(o) => mark(o),
Self::IntegerIndexed(i) => mark(i),
#[cfg(feature = "intl")]
Self::DateTimeFormat(f) => mark(f),
Self::Promise(p) => mark(p),
Self::AsyncGenerator(g) => mark(g),
Self::RegExp(_)
| Self::BigInt(_)
| Self::Boolean(_)
| Self::String(_)
| Self::Date(_)
| Self::Array
| Self::Error
| Self::Ordinary
| Self::Global
| Self::Number(_)
| Self::Symbol(_) => {}
}
}}
}
impl ObjectData {
pub fn async_from_sync_iterator(async_from_sync_iterator: AsyncFromSyncIterator) -> Self {
Self {
kind: ObjectKind::AsyncFromSyncIterator(async_from_sync_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn async_generator(async_generator: AsyncGenerator) -> Self {
Self {
kind: ObjectKind::AsyncGenerator(async_generator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn async_generator_function(function: Function) -> Self {
Self {
internal_methods: if function.is_constructor() {
&CONSTRUCTOR_INTERNAL_METHODS
} else {
&FUNCTION_INTERNAL_METHODS
},
kind: ObjectKind::GeneratorFunction(function),
}
}
pub fn array() -> Self {
Self {
kind: ObjectKind::Array,
internal_methods: &ARRAY_EXOTIC_INTERNAL_METHODS,
}
}
pub fn array_iterator(array_iterator: ArrayIterator) -> Self {
Self {
kind: ObjectKind::ArrayIterator(array_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn array_buffer(array_buffer: ArrayBuffer) -> Self {
Self {
kind: ObjectKind::ArrayBuffer(array_buffer),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn map(map: OrderedMap<JsValue>) -> Self {
Self {
kind: ObjectKind::Map(map),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn map_iterator(map_iterator: MapIterator) -> Self {
Self {
kind: ObjectKind::MapIterator(map_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn reg_exp(reg_exp: Box<RegExp>) -> Self {
Self {
kind: ObjectKind::RegExp(reg_exp),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn reg_exp_string_iterator(reg_exp_string_iterator: RegExpStringIterator) -> Self {
Self {
kind: ObjectKind::RegExpStringIterator(reg_exp_string_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn big_int(big_int: JsBigInt) -> Self {
Self {
kind: ObjectKind::BigInt(big_int),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn boolean(boolean: bool) -> Self {
Self {
kind: ObjectKind::Boolean(boolean),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn data_view(data_view: DataView) -> Self {
Self {
kind: ObjectKind::DataView(data_view),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn promise(promise: Promise) -> Self {
Self {
kind: ObjectKind::Promise(promise),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn for_in_iterator(for_in_iterator: ForInIterator) -> Self {
Self {
kind: ObjectKind::ForInIterator(for_in_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn function(function: Function) -> Self {
Self {
internal_methods: if function.is_constructor() {
&CONSTRUCTOR_INTERNAL_METHODS
} else {
&FUNCTION_INTERNAL_METHODS
},
kind: ObjectKind::Function(function),
}
}
pub fn bound_function(bound_function: BoundFunction, constructor: bool) -> Self {
Self {
kind: ObjectKind::BoundFunction(bound_function),
internal_methods: if constructor {
&BOUND_CONSTRUCTOR_EXOTIC_INTERNAL_METHODS
} else {
&BOUND_FUNCTION_EXOTIC_INTERNAL_METHODS
},
}
}
pub fn generator(generator: Generator) -> Self {
Self {
kind: ObjectKind::Generator(generator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn generator_function(function: Function) -> Self {
Self {
internal_methods: if function.is_constructor() {
&CONSTRUCTOR_INTERNAL_METHODS
} else {
&FUNCTION_INTERNAL_METHODS
},
kind: ObjectKind::GeneratorFunction(function),
}
}
pub fn set(set: OrderedSet<JsValue>) -> Self {
Self {
kind: ObjectKind::Set(set),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn set_iterator(set_iterator: SetIterator) -> Self {
Self {
kind: ObjectKind::SetIterator(set_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn string(string: JsString) -> Self {
Self {
kind: ObjectKind::String(string),
internal_methods: &STRING_EXOTIC_INTERNAL_METHODS,
}
}
pub fn string_iterator(string_iterator: StringIterator) -> Self {
Self {
kind: ObjectKind::StringIterator(string_iterator),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn number(number: f64) -> Self {
Self {
kind: ObjectKind::Number(number),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn symbol(symbol: JsSymbol) -> Self {
Self {
kind: ObjectKind::Symbol(symbol),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn error() -> Self {
Self {
kind: ObjectKind::Error,
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn ordinary() -> Self {
Self {
kind: ObjectKind::Ordinary,
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn proxy(proxy: Proxy, call: bool, construct: bool) -> Self {
Self {
kind: ObjectKind::Proxy(proxy),
internal_methods: if call && construct {
&PROXY_EXOTIC_INTERNAL_METHODS_ALL
} else if call {
&PROXY_EXOTIC_INTERNAL_METHODS_WITH_CALL
} else {
&PROXY_EXOTIC_INTERNAL_METHODS_BASIC
},
}
}
pub fn date(date: Date) -> Self {
Self {
kind: ObjectKind::Date(date),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn global() -> Self {
Self {
kind: ObjectKind::Global,
internal_methods: &GLOBAL_INTERNAL_METHODS,
}
}
pub fn arguments(arguments: Arguments) -> Self {
Self {
internal_methods: if matches!(arguments, Arguments::Unmapped) {
&ORDINARY_INTERNAL_METHODS
} else {
&ARGUMENTS_EXOTIC_INTERNAL_METHODS
},
kind: ObjectKind::Arguments(arguments),
}
}
pub fn native_object(native_object: Box<dyn NativeObject>) -> Self {
Self {
kind: ObjectKind::NativeObject(native_object),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
pub fn integer_indexed(integer_indexed: IntegerIndexed) -> Self {
Self {
kind: ObjectKind::IntegerIndexed(integer_indexed),
internal_methods: &INTEGER_INDEXED_EXOTIC_INTERNAL_METHODS,
}
}
#[cfg(feature = "intl")]
pub fn date_time_format(date_time_fmt: Box<DateTimeFormat>) -> Self {
Self {
kind: ObjectKind::DateTimeFormat(date_time_fmt),
internal_methods: &ORDINARY_INTERNAL_METHODS,
}
}
}
impl Display for ObjectKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(match self {
Self::AsyncFromSyncIterator(_) => "AsyncFromSyncIterator",
Self::AsyncGenerator(_) => "AsyncGenerator",
Self::AsyncGeneratorFunction(_) => "AsyncGeneratorFunction",
Self::Array => "Array",
Self::ArrayIterator(_) => "ArrayIterator",
Self::ArrayBuffer(_) => "ArrayBuffer",
Self::ForInIterator(_) => "ForInIterator",
Self::Function(_) => "Function",
Self::BoundFunction(_) => "BoundFunction",
Self::Generator(_) => "Generator",
Self::GeneratorFunction(_) => "GeneratorFunction",
Self::RegExp(_) => "RegExp",
Self::RegExpStringIterator(_) => "RegExpStringIterator",
Self::Map(_) => "Map",
Self::MapIterator(_) => "MapIterator",
Self::Set(_) => "Set",
Self::SetIterator(_) => "SetIterator",
Self::String(_) => "String",
Self::StringIterator(_) => "StringIterator",
Self::Symbol(_) => "Symbol",
Self::Error => "Error",
Self::Ordinary => "Ordinary",
Self::Proxy(_) => "Proxy",
Self::Boolean(_) => "Boolean",
Self::Number(_) => "Number",
Self::BigInt(_) => "BigInt",
Self::Date(_) => "Date",
Self::Global => "Global",
Self::Arguments(_) => "Arguments",
Self::NativeObject(_) => "NativeObject",
Self::IntegerIndexed(_) => "TypedArray",
Self::DataView(_) => "DataView",
#[cfg(feature = "intl")]
Self::DateTimeFormat(_) => "DateTimeFormat",
Self::Promise(_) => "Promise",
})
}
}
impl Debug for ObjectData {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ObjectData")
.field("kind", &self.kind)
.field("internal_methods", &"internal_methods")
.finish()
}
}
impl Default for Object {
#[inline]
fn default() -> Self {
Self {
data: ObjectData::ordinary(),
properties: PropertyMap::default(),
prototype: None,
extensible: true,
private_elements: FxHashMap::default(),
}
}
}
impl Object {
#[inline]
pub fn kind(&self) -> &ObjectKind {
&self.data.kind
}
#[inline]
pub fn is_async_from_sync_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::AsyncFromSyncIterator(_),
..
}
)
}
#[inline]
pub fn as_async_from_sync_iterator(&self) -> Option<&AsyncFromSyncIterator> {
match self.data {
ObjectData {
kind: ObjectKind::AsyncFromSyncIterator(ref async_from_sync_iterator),
..
} => Some(async_from_sync_iterator),
_ => None,
}
}
#[inline]
pub fn is_async_generator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::AsyncGenerator(_),
..
}
)
}
#[inline]
pub fn as_async_generator(&self) -> Option<&AsyncGenerator> {
match self.data {
ObjectData {
kind: ObjectKind::AsyncGenerator(ref async_generator),
..
} => Some(async_generator),
_ => None,
}
}
#[inline]
pub fn as_async_generator_mut(&mut self) -> Option<&mut AsyncGenerator> {
match self.data {
ObjectData {
kind: ObjectKind::AsyncGenerator(ref mut async_generator),
..
} => Some(async_generator),
_ => None,
}
}
#[inline]
pub fn is_array(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Array,
..
}
)
}
#[inline]
pub fn is_array_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::ArrayIterator(_),
..
}
)
}
#[inline]
pub fn as_array_iterator(&self) -> Option<&ArrayIterator> {
match self.data {
ObjectData {
kind: ObjectKind::ArrayIterator(ref iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub(crate) fn has_viewed_array_buffer(&self) -> bool {
self.is_typed_array() || self.is_data_view()
}
#[inline]
pub fn is_data_view(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::DataView(_),
..
}
)
}
#[inline]
pub fn is_array_buffer(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::ArrayBuffer(_),
..
}
)
}
#[inline]
pub fn as_array_buffer(&self) -> Option<&ArrayBuffer> {
match &self.data {
ObjectData {
kind: ObjectKind::ArrayBuffer(buffer),
..
} => Some(buffer),
_ => None,
}
}
#[inline]
pub fn as_array_buffer_mut(&mut self) -> Option<&mut ArrayBuffer> {
match &mut self.data {
ObjectData {
kind: ObjectKind::ArrayBuffer(buffer),
..
} => Some(buffer),
_ => None,
}
}
#[inline]
pub fn as_array_iterator_mut(&mut self) -> Option<&mut ArrayIterator> {
match &mut self.data {
ObjectData {
kind: ObjectKind::ArrayIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn as_string_iterator_mut(&mut self) -> Option<&mut StringIterator> {
match &mut self.data {
ObjectData {
kind: ObjectKind::StringIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn as_regexp_string_iterator_mut(&mut self) -> Option<&mut RegExpStringIterator> {
match &mut self.data {
ObjectData {
kind: ObjectKind::RegExpStringIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn as_for_in_iterator(&self) -> Option<&ForInIterator> {
match &self.data {
ObjectData {
kind: ObjectKind::ForInIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn as_for_in_iterator_mut(&mut self) -> Option<&mut ForInIterator> {
match &mut self.data {
ObjectData {
kind: ObjectKind::ForInIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn is_map(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Map(_),
..
}
)
}
#[inline]
pub fn as_map_ref(&self) -> Option<&OrderedMap<JsValue>> {
match self.data {
ObjectData {
kind: ObjectKind::Map(ref map),
..
} => Some(map),
_ => None,
}
}
#[inline]
pub fn as_map_mut(&mut self) -> Option<&mut OrderedMap<JsValue>> {
match &mut self.data {
ObjectData {
kind: ObjectKind::Map(map),
..
} => Some(map),
_ => None,
}
}
#[inline]
pub fn is_map_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::MapIterator(_),
..
}
)
}
#[inline]
pub fn as_map_iterator_ref(&self) -> Option<&MapIterator> {
match &self.data {
ObjectData {
kind: ObjectKind::MapIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn as_map_iterator_mut(&mut self) -> Option<&mut MapIterator> {
match &mut self.data {
ObjectData {
kind: ObjectKind::MapIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn is_set(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Set(_),
..
}
)
}
#[inline]
pub fn is_set_iterator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::SetIterator(_),
..
}
)
}
#[inline]
pub fn as_set_ref(&self) -> Option<&OrderedSet<JsValue>> {
match self.data {
ObjectData {
kind: ObjectKind::Set(ref set),
..
} => Some(set),
_ => None,
}
}
#[inline]
pub fn as_set_mut(&mut self) -> Option<&mut OrderedSet<JsValue>> {
match &mut self.data {
ObjectData {
kind: ObjectKind::Set(set),
..
} => Some(set),
_ => None,
}
}
#[inline]
pub fn as_set_iterator_mut(&mut self) -> Option<&mut SetIterator> {
match &mut self.data {
ObjectData {
kind: ObjectKind::SetIterator(iter),
..
} => Some(iter),
_ => None,
}
}
#[inline]
pub fn is_string(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::String(_),
..
}
)
}
#[inline]
pub fn as_string(&self) -> Option<JsString> {
match self.data {
ObjectData {
kind: ObjectKind::String(ref string),
..
} => Some(string.clone()),
_ => None,
}
}
#[inline]
pub fn is_function(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Function(_),
..
}
)
}
#[inline]
pub fn as_function(&self) -> Option<&Function> {
match self.data {
ObjectData {
kind:
ObjectKind::Function(ref function) | ObjectKind::GeneratorFunction(ref function),
..
} => Some(function),
_ => None,
}
}
#[inline]
pub fn as_function_mut(&mut self) -> Option<&mut Function> {
match self.data {
ObjectData {
kind:
ObjectKind::Function(ref mut function)
| ObjectKind::GeneratorFunction(ref mut function),
..
} => Some(function),
_ => None,
}
}
#[inline]
pub fn as_bound_function(&self) -> Option<&BoundFunction> {
match self.data {
ObjectData {
kind: ObjectKind::BoundFunction(ref bound_function),
..
} => Some(bound_function),
_ => None,
}
}
#[inline]
pub fn is_generator(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Generator(_),
..
}
)
}
#[inline]
pub fn as_generator(&self) -> Option<&Generator> {
match self.data {
ObjectData {
kind: ObjectKind::Generator(ref generator),
..
} => Some(generator),
_ => None,
}
}
#[inline]
pub fn as_generator_mut(&mut self) -> Option<&mut Generator> {
match self.data {
ObjectData {
kind: ObjectKind::Generator(ref mut generator),
..
} => Some(generator),
_ => None,
}
}
#[inline]
pub fn is_symbol(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Symbol(_),
..
}
)
}
#[inline]
pub fn as_symbol(&self) -> Option<JsSymbol> {
match self.data {
ObjectData {
kind: ObjectKind::Symbol(ref symbol),
..
} => Some(symbol.clone()),
_ => None,
}
}
#[inline]
pub fn is_error(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Error,
..
}
)
}
#[inline]
pub fn is_boolean(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Boolean(_),
..
}
)
}
#[inline]
pub fn as_boolean(&self) -> Option<bool> {
match self.data {
ObjectData {
kind: ObjectKind::Boolean(boolean),
..
} => Some(boolean),
_ => None,
}
}
#[inline]
pub fn is_number(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Number(_),
..
}
)
}
#[inline]
pub fn as_number(&self) -> Option<f64> {
match self.data {
ObjectData {
kind: ObjectKind::Number(number),
..
} => Some(number),
_ => None,
}
}
#[inline]
pub fn is_bigint(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::BigInt(_),
..
}
)
}
#[inline]
pub fn as_bigint(&self) -> Option<&JsBigInt> {
match self.data {
ObjectData {
kind: ObjectKind::BigInt(ref bigint),
..
} => Some(bigint),
_ => None,
}
}
#[inline]
pub fn is_date(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Date(_),
..
}
)
}
#[inline]
pub fn as_date(&self) -> Option<&Date> {
match self.data {
ObjectData {
kind: ObjectKind::Date(ref date),
..
} => Some(date),
_ => None,
}
}
#[inline]
pub fn is_regexp(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::RegExp(_),
..
}
)
}
#[inline]
pub fn as_regexp(&self) -> Option<&RegExp> {
match self.data {
ObjectData {
kind: ObjectKind::RegExp(ref regexp),
..
} => Some(regexp),
_ => None,
}
}
#[inline]
pub fn is_typed_array(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::IntegerIndexed(_),
..
}
)
}
#[inline]
pub fn as_data_view(&self) -> Option<&DataView> {
match &self.data {
ObjectData {
kind: ObjectKind::DataView(data_view),
..
} => Some(data_view),
_ => None,
}
}
#[inline]
pub fn as_data_view_mut(&mut self) -> Option<&mut DataView> {
match &mut self.data {
ObjectData {
kind: ObjectKind::DataView(data_view),
..
} => Some(data_view),
_ => None,
}
}
#[inline]
pub fn is_arguments(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Arguments(_),
..
}
)
}
#[inline]
pub fn as_mapped_arguments(&self) -> Option<&ParameterMap> {
match self.data {
ObjectData {
kind: ObjectKind::Arguments(Arguments::Mapped(ref args)),
..
} => Some(args),
_ => None,
}
}
#[inline]
pub fn as_mapped_arguments_mut(&mut self) -> Option<&mut ParameterMap> {
match self.data {
ObjectData {
kind: ObjectKind::Arguments(Arguments::Mapped(ref mut args)),
..
} => Some(args),
_ => None,
}
}
#[inline]
pub fn as_typed_array(&self) -> Option<&IntegerIndexed> {
match self.data {
ObjectData {
kind: ObjectKind::IntegerIndexed(ref integer_indexed_object),
..
} => Some(integer_indexed_object),
_ => None,
}
}
#[inline]
pub fn as_typed_array_mut(&mut self) -> Option<&mut IntegerIndexed> {
match self.data {
ObjectData {
kind: ObjectKind::IntegerIndexed(ref mut integer_indexed_object),
..
} => Some(integer_indexed_object),
_ => None,
}
}
#[inline]
pub fn is_ordinary(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Ordinary,
..
}
)
}
#[inline]
pub fn is_proxy(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Proxy(_),
..
}
)
}
#[inline]
pub fn as_proxy(&self) -> Option<&Proxy> {
match self.data {
ObjectData {
kind: ObjectKind::Proxy(ref proxy),
..
} => Some(proxy),
_ => None,
}
}
#[inline]
pub fn as_proxy_mut(&mut self) -> Option<&mut Proxy> {
match self.data {
ObjectData {
kind: ObjectKind::Proxy(ref mut proxy),
..
} => Some(proxy),
_ => None,
}
}
#[inline]
pub fn prototype(&self) -> &JsPrototype {
&self.prototype
}
#[inline]
#[track_caller]
pub fn set_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> bool {
let prototype = prototype.into();
if self.extensible {
self.prototype = prototype;
true
} else {
self.prototype == prototype
}
}
#[inline]
pub fn is_native_object(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::NativeObject(_),
..
}
)
}
#[inline]
pub fn as_native_object(&self) -> Option<&dyn NativeObject> {
match self.data {
ObjectData {
kind: ObjectKind::NativeObject(ref object),
..
} => Some(object.as_ref()),
_ => None,
}
}
#[inline]
pub fn is_promise(&self) -> bool {
matches!(
self.data,
ObjectData {
kind: ObjectKind::Promise(_),
..
}
)
}
#[inline]
pub fn as_promise(&self) -> Option<&Promise> {
match self.data {
ObjectData {
kind: ObjectKind::Promise(ref promise),
..
} => Some(promise),
_ => None,
}
}
#[inline]
pub fn as_promise_mut(&mut self) -> Option<&mut Promise> {
match self.data {
ObjectData {
kind: ObjectKind::Promise(ref mut promise),
..
} => Some(promise),
_ => None,
}
}
#[inline]
pub fn is<T>(&self) -> bool
where
T: NativeObject,
{
match self.data {
ObjectData {
kind: ObjectKind::NativeObject(ref object),
..
} => object.deref().as_any().is::<T>(),
_ => false,
}
}
#[inline]
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: NativeObject,
{
match self.data {
ObjectData {
kind: ObjectKind::NativeObject(ref object),
..
} => object.deref().as_any().downcast_ref::<T>(),
_ => None,
}
}
#[inline]
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: NativeObject,
{
match self.data {
ObjectData {
kind: ObjectKind::NativeObject(ref mut object),
..
} => object.deref_mut().as_mut_any().downcast_mut::<T>(),
_ => None,
}
}
#[inline]
pub fn properties(&self) -> &PropertyMap {
&self.properties
}
#[inline]
pub(crate) fn properties_mut(&mut self) -> &mut PropertyMap {
&mut self.properties
}
#[inline]
pub(crate) fn insert<K, P>(&mut self, key: K, property: P) -> Option<PropertyDescriptor>
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
self.properties.insert(&key.into(), property.into())
}
#[inline]
pub(crate) fn remove(&mut self, key: &PropertyKey) -> Option<PropertyDescriptor> {
self.properties.remove(key)
}
#[inline]
pub(crate) fn get_private_element(&self, name: Sym) -> Option<&PrivateElement> {
self.private_elements.get(&name)
}
#[inline]
pub(crate) fn set_private_element(&mut self, name: Sym, value: PrivateElement) {
self.private_elements.insert(name, value);
}
#[inline]
pub(crate) fn set_private_element_setter(&mut self, name: Sym, setter: JsObject) {
match self.private_elements.get_mut(&name) {
Some(PrivateElement::Accessor {
getter: _,
setter: s,
}) => {
*s = Some(setter);
}
_ => {
self.private_elements.insert(
name,
PrivateElement::Accessor {
getter: None,
setter: Some(setter),
},
);
}
}
}
#[inline]
pub(crate) fn set_private_element_getter(&mut self, name: Sym, getter: JsObject) {
match self.private_elements.get_mut(&name) {
Some(PrivateElement::Accessor {
getter: g,
setter: _,
}) => {
*g = Some(getter);
}
_ => {
self.private_elements.insert(
name,
PrivateElement::Accessor {
getter: Some(getter),
setter: None,
},
);
}
}
}
}
#[derive(Debug, Clone)]
pub struct FunctionBinding {
binding: PropertyKey,
name: JsString,
}
impl From<&str> for FunctionBinding {
#[inline]
fn from(name: &str) -> Self {
let name: JsString = name.into();
Self {
binding: name.clone().into(),
name,
}
}
}
impl From<String> for FunctionBinding {
#[inline]
fn from(name: String) -> Self {
let name: JsString = name.into();
Self {
binding: name.clone().into(),
name,
}
}
}
impl From<JsString> for FunctionBinding {
#[inline]
fn from(name: JsString) -> Self {
Self {
binding: name.clone().into(),
name,
}
}
}
impl<B, N> From<(B, N)> for FunctionBinding
where
B: Into<PropertyKey>,
N: AsRef<str>,
{
#[inline]
fn from((binding, name): (B, N)) -> Self {
Self {
binding: binding.into(),
name: name.as_ref().into(),
}
}
}
#[derive(Debug)]
pub struct FunctionBuilder<'context> {
context: &'context mut Context,
function: Function,
name: JsString,
length: usize,
}
impl<'context> FunctionBuilder<'context> {
#[inline]
pub fn native(context: &'context mut Context, function: NativeFunctionSignature) -> Self {
Self {
context,
function: Function::Native {
function,
constructor: None,
},
name: JsString::default(),
length: 0,
}
}
#[inline]
pub fn closure<F>(context: &'context mut Context, function: F) -> Self
where
F: Fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue> + Copy + 'static,
{
Self {
context,
function: Function::Closure {
function: Box::new(move |this, args, _, context| function(this, args, context)),
constructor: None,
captures: Captures::new(()),
},
name: JsString::default(),
length: 0,
}
}
#[inline]
pub fn closure_with_captures<F, C>(
context: &'context mut Context,
function: F,
captures: C,
) -> Self
where
F: Fn(&JsValue, &[JsValue], &mut C, &mut Context) -> JsResult<JsValue> + Copy + 'static,
C: NativeObject,
{
Self {
context,
function: Function::Closure {
function: Box::new(move |this, args, captures: Captures, context| {
let mut captures = captures.as_mut_any();
let captures = captures.downcast_mut::<C>().ok_or_else(|| {
context.construct_type_error("cannot downcast `Captures` to given type")
})?;
function(this, args, captures, context)
}),
constructor: None,
captures: Captures::new(captures),
},
name: JsString::default(),
length: 0,
}
}
#[inline]
#[must_use]
pub fn name<N>(mut self, name: N) -> Self
where
N: AsRef<str>,
{
self.name = name.as_ref().into();
self
}
#[inline]
#[must_use]
pub fn length(mut self, length: usize) -> Self {
self.length = length;
self
}
#[inline]
#[must_use]
pub fn constructor(mut self, yes: bool) -> Self {
match self.function {
Function::Native {
ref mut constructor,
..
}
| Function::Closure {
ref mut constructor,
..
} => {
*constructor = yes.then(|| ConstructorKind::Base);
}
Function::Ordinary { .. }
| Function::Generator { .. }
| Function::AsyncGenerator { .. }
| Function::Async { .. } => {
unreachable!("function must be native or closure");
}
}
self
}
#[inline]
pub fn build(self) -> JsFunction {
let function = JsObject::from_proto_and_data(
self.context
.intrinsics()
.constructors()
.function()
.prototype(),
ObjectData::function(self.function),
);
let property = PropertyDescriptor::builder()
.writable(false)
.enumerable(false)
.configurable(true);
function.insert_property("length", property.clone().value(self.length));
function.insert_property("name", property.value(self.name));
JsFunction::from_object_unchecked(function)
}
pub(crate) fn build_function_prototype(self, object: &JsObject) {
let mut object = object.borrow_mut();
object.data = ObjectData::function(self.function);
object.set_prototype(
self.context
.intrinsics()
.constructors()
.object()
.prototype(),
);
let property = PropertyDescriptor::builder()
.writable(false)
.enumerable(false)
.configurable(true);
object.insert("name", property.clone().value(self.name.clone()));
object.insert("length", property.value(self.length));
}
}
#[derive(Debug)]
pub struct ObjectInitializer<'context> {
context: &'context mut Context,
object: JsObject,
}
impl<'context> ObjectInitializer<'context> {
#[inline]
pub fn new(context: &'context mut Context) -> Self {
let object = context.construct_object();
Self { context, object }
}
#[inline]
pub fn function<B>(
&mut self,
function: NativeFunctionSignature,
binding: B,
length: usize,
) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionBuilder::native(self.context, function)
.name(binding.name)
.length(length)
.constructor(false)
.build();
self.object.borrow_mut().insert(
binding.binding,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
self
}
#[inline]
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
let property = PropertyDescriptor::builder()
.value(value)
.writable(attribute.writable())
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.object.borrow_mut().insert(key, property);
self
}
#[inline]
pub fn build(&mut self) -> JsObject {
self.object.clone()
}
}
pub struct ConstructorBuilder<'context> {
context: &'context mut Context,
function: NativeFunctionSignature,
object: JsObject,
has_prototype_property: bool,
prototype: JsObject,
name: JsString,
length: usize,
callable: bool,
constructor: Option<ConstructorKind>,
inherit: Option<JsPrototype>,
custom_prototype: Option<JsPrototype>,
}
impl Debug for ConstructorBuilder<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ConstructorBuilder")
.field("name", &self.name)
.field("length", &self.length)
.field("constructor", &self.object)
.field("constructor_has_prototype", &self.has_prototype_property)
.field("prototype", &self.prototype)
.field("inherit", &self.inherit)
.field("callable", &self.callable)
.field("constructor", &self.constructor)
.finish()
}
}
impl<'context> ConstructorBuilder<'context> {
#[inline]
pub fn new(context: &'context mut Context, function: NativeFunctionSignature) -> Self {
Self {
context,
function,
object: JsObject::empty(),
prototype: JsObject::empty(),
length: 0,
name: JsString::default(),
callable: true,
constructor: Some(ConstructorKind::Base),
inherit: None,
custom_prototype: None,
has_prototype_property: true,
}
}
#[inline]
pub(crate) fn with_standard_constructor(
context: &'context mut Context,
function: NativeFunctionSignature,
standard_constructor: StandardConstructor,
) -> Self {
Self {
context,
function,
object: standard_constructor.constructor,
has_prototype_property: true,
prototype: standard_constructor.prototype,
length: 0,
name: JsString::default(),
callable: true,
constructor: Some(ConstructorKind::Base),
inherit: None,
custom_prototype: None,
}
}
#[inline]
pub fn method<B>(
&mut self,
function: NativeFunctionSignature,
binding: B,
length: usize,
) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionBuilder::native(self.context, function)
.name(binding.name)
.length(length)
.constructor(false)
.build();
self.prototype.borrow_mut().insert(
binding.binding,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
self
}
#[inline]
pub fn static_method<B>(
&mut self,
function: NativeFunctionSignature,
binding: B,
length: usize,
) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionBuilder::native(self.context, function)
.name(binding.name)
.length(length)
.constructor(false)
.build();
self.object.borrow_mut().insert(
binding.binding,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
self
}
#[inline]
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
let property = PropertyDescriptor::builder()
.value(value)
.writable(attribute.writable())
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.prototype.borrow_mut().insert(key, property);
self
}
#[inline]
pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<JsValue>,
{
let property = PropertyDescriptor::builder()
.value(value)
.writable(attribute.writable())
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.object.borrow_mut().insert(key, property);
self
}
#[inline]
pub fn accessor<K>(
&mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
attribute: Attribute,
) -> &mut Self
where
K: Into<PropertyKey>,
{
let property = PropertyDescriptor::builder()
.maybe_get(get)
.maybe_set(set)
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.prototype.borrow_mut().insert(key, property);
self
}
#[inline]
pub fn static_accessor<K>(
&mut self,
key: K,
get: Option<JsFunction>,
set: Option<JsFunction>,
attribute: Attribute,
) -> &mut Self
where
K: Into<PropertyKey>,
{
let property = PropertyDescriptor::builder()
.maybe_get(get)
.maybe_set(set)
.enumerable(attribute.enumerable())
.configurable(attribute.configurable());
self.object.borrow_mut().insert(key, property);
self
}
#[inline]
pub fn property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
let property = property.into();
self.prototype.borrow_mut().insert(key, property);
self
}
#[inline]
pub fn static_property_descriptor<K, P>(&mut self, key: K, property: P) -> &mut Self
where
K: Into<PropertyKey>,
P: Into<PropertyDescriptor>,
{
let property = property.into();
self.object.borrow_mut().insert(key, property);
self
}
#[inline]
pub fn length(&mut self, length: usize) -> &mut Self {
self.length = length;
self
}
#[inline]
pub fn name<N>(&mut self, name: N) -> &mut Self
where
N: AsRef<str>,
{
self.name = name.as_ref().into();
self
}
#[inline]
pub fn callable(&mut self, callable: bool) -> &mut Self {
self.callable = callable;
self
}
#[inline]
pub fn constructor(&mut self, constructor: bool) -> &mut Self {
self.constructor = constructor.then(|| ConstructorKind::Base);
self
}
#[inline]
pub fn inherit<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
self.inherit = Some(prototype.into());
self
}
#[inline]
pub fn custom_prototype<O: Into<JsPrototype>>(&mut self, prototype: O) -> &mut Self {
self.custom_prototype = Some(prototype.into());
self
}
#[inline]
pub fn has_prototype_property(&mut self, has_prototype_property: bool) -> &mut Self {
self.has_prototype_property = has_prototype_property;
self
}
#[inline]
pub fn context(&mut self) -> &'_ mut Context {
self.context
}
pub fn build(&mut self) -> JsFunction {
let function = Function::Native {
function: self.function,
constructor: self.constructor,
};
let length = PropertyDescriptor::builder()
.value(self.length)
.writable(false)
.enumerable(false)
.configurable(true);
let name = PropertyDescriptor::builder()
.value(self.name.clone())
.writable(false)
.enumerable(false)
.configurable(true);
{
let mut constructor = self.object.borrow_mut();
constructor.data = ObjectData::function(function);
constructor.insert("length", length);
constructor.insert("name", name);
if let Some(proto) = self.custom_prototype.take() {
constructor.set_prototype(proto);
} else {
constructor.set_prototype(
self.context
.intrinsics()
.constructors()
.function()
.prototype(),
);
}
if self.has_prototype_property {
constructor.insert(
PROTOTYPE,
PropertyDescriptor::builder()
.value(self.prototype.clone())
.writable(false)
.enumerable(false)
.configurable(false),
);
}
}
{
let mut prototype = self.prototype.borrow_mut();
prototype.insert(
"constructor",
PropertyDescriptor::builder()
.value(self.object.clone())
.writable(true)
.enumerable(false)
.configurable(true),
);
if let Some(proto) = self.inherit.take() {
prototype.set_prototype(proto);
} else {
prototype.set_prototype(
self.context
.intrinsics()
.constructors()
.object()
.prototype(),
);
}
}
JsFunction::from_object_unchecked(self.object.clone())
}
}