Browse Source

Unify object creation with `empty` and `from_proto_and_data` methods (#1567)

pull/1633/head
jedel1043 3 years ago committed by GitHub
parent
commit
e1c573aaca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 25
      boa/src/builtins/array/array_iterator.rs
  2. 26
      boa/src/builtins/array/mod.rs
  3. 14
      boa/src/builtins/boolean/mod.rs
  4. 51
      boa/src/builtins/date/mod.rs
  5. 13
      boa/src/builtins/error/eval.rs
  6. 16
      boa/src/builtins/error/mod.rs
  7. 16
      boa/src/builtins/error/range.rs
  8. 16
      boa/src/builtins/error/reference.rs
  9. 16
      boa/src/builtins/error/syntax.rs
  10. 16
      boa/src/builtins/error/type.rs
  11. 16
      boa/src/builtins/error/uri.rs
  12. 374
      boa/src/builtins/function/mod.rs
  13. 21
      boa/src/builtins/iterable/mod.rs
  14. 25
      boa/src/builtins/map/map_iterator.rs
  15. 7
      boa/src/builtins/map/mod.rs
  16. 17
      boa/src/builtins/number/mod.rs
  17. 25
      boa/src/builtins/object/for_in_iterator.rs
  18. 20
      boa/src/builtins/object/mod.rs
  19. 6
      boa/src/builtins/regexp/mod.rs
  20. 30
      boa/src/builtins/regexp/regexp_string_iterator.rs
  21. 17
      boa/src/builtins/set/mod.rs
  22. 25
      boa/src/builtins/set/set_iterator.rs
  23. 4
      boa/src/builtins/string/mod.rs
  24. 27
      boa/src/builtins/string/string_iterator.rs
  25. 2
      boa/src/bytecompiler.rs
  26. 13
      boa/src/class.rs
  27. 50
      boa/src/context.rs
  28. 376
      boa/src/object/function.rs
  29. 55
      boa/src/object/jsobject.rs
  30. 134
      boa/src/object/mod.rs
  31. 8
      boa/src/realm.rs
  32. 2
      boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs
  33. 2
      boa/src/syntax/ast/node/declaration/function_decl/mod.rs
  34. 2
      boa/src/syntax/ast/node/declaration/function_expr/mod.rs
  35. 46
      boa/src/syntax/ast/node/object/mod.rs
  36. 48
      boa/src/value/conversions.rs
  37. 48
      boa/src/value/mod.rs
  38. 20
      boa/src/value/tests.rs
  39. 33
      boa/src/vm/code_block.rs
  40. 9
      boa/src/vm/mod.rs

25
boa/src/builtins/array/array_iterator.rs

@ -1,7 +1,7 @@
use crate::{
builtins::{iterable::create_iter_result_object, Array, JsValue},
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue},
gc::{Finalize, Trace},
object::{function::make_builtin_fn, JsObject, ObjectData},
object::{JsObject, ObjectData},
property::{PropertyDescriptor, PropertyNameKind},
symbol::WellKnownSymbols,
BoaProfiler, Context, JsResult,
@ -46,13 +46,11 @@ impl ArrayIterator {
kind: PropertyNameKind,
context: &Context,
) -> JsValue {
let array_iterator = JsValue::new_object(context);
array_iterator.set_data(ObjectData::array_iterator(Self::new(array, kind)));
array_iterator
.as_object()
.expect("array iterator object")
.set_prototype_instance(context.iterator_prototypes().array_iterator().into());
array_iterator
let array_iterator = JsObject::from_proto_and_data(
context.iterator_prototypes().array_iterator(),
ObjectData::array_iterator(Self::new(array, kind)),
);
array_iterator.into()
}
/// %ArrayIteratorPrototype%.next( )
@ -123,13 +121,16 @@ impl ArrayIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
pub(crate) fn create_prototype(
iterator_prototype: JsObject,
context: &mut Context,
) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let array_iterator = context.construct_object();
let array_iterator =
JsObject::from_proto_and_data(iterator_prototype, ObjectData::ordinary());
make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
array_iterator.set_prototype_instance(iterator_prototype);
let to_string_tag = WellKnownSymbols::to_string_tag();
let to_string_tag_property = PropertyDescriptor::builder()

26
boa/src/builtins/array/mod.rs

@ -223,12 +223,7 @@ impl Array {
Some(prototype) => prototype,
None => context.standard_objects().array_object().prototype(),
};
let array = context.construct_object();
array.set_prototype_instance(prototype.into());
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
array.borrow_mut().data = ObjectData::array();
let array = JsObject::from_proto_and_data(prototype, ObjectData::array());
// 6. Perform ! OrdinaryDefineOwnProperty(A, "length", PropertyDescriptor { [[Value]]: 𝔽(length), [[Writable]]: true, [[Enumerable]]: false, [[Configurable]]: false }).
crate::object::internal_methods::ordinary_define_own_property(
@ -275,22 +270,9 @@ impl Array {
/// Creates a new `Array` instance.
pub(crate) fn new_array(context: &mut Context) -> JsValue {
let array = JsValue::new_object(context);
array.set_data(ObjectData::array());
array
.as_object()
.expect("'array' should be an object")
.set_prototype_instance(context.standard_objects().array_object().prototype().into());
array.set_property(
"length",
PropertyDescriptor::builder()
.value(0)
.writable(true)
.enumerable(false)
.configurable(false)
.build(),
);
array
Self::array_create(0, None, context)
.expect("creating an empty array with the default prototype must not fail")
.into()
}
/// Utility function for concatenating array objects.

14
boa/src/builtins/boolean/mod.rs

@ -15,7 +15,9 @@ mod tests;
use crate::{
builtins::BuiltIn,
context::StandardObjects,
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
property::Attribute,
BoaProfiler, Context, JsResult, JsValue,
};
@ -69,15 +71,9 @@ impl Boolean {
}
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::boolean_object, context)?;
let boolean = JsValue::new_object(context);
let boolean = JsObject::from_proto_and_data(prototype, ObjectData::boolean(data));
boolean
.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
boolean.set_data(ObjectData::boolean(data));
Ok(boolean)
Ok(boolean.into())
}
/// An Utility function used to get the internal `[[BooleanData]]`.

51
boa/src/builtins/date/mod.rs

@ -5,7 +5,9 @@ use crate::{
builtins::BuiltIn,
context::StandardObjects,
gc::{empty_trace, Finalize, Trace},
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
property::Attribute,
symbol::WellKnownSymbols,
value::{JsValue, PreferredType},
@ -346,16 +348,14 @@ impl Date {
StandardObjects::object_object,
context,
)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = obj.into();
if args.is_empty() {
Ok(Self::make_date_now(&this))
Ok(if args.is_empty() {
Self::make_date_now(prototype)
} else if args.len() == 1 {
Self::make_date_single(&this, args, context)
Self::make_date_single(prototype, args, context)?
} else {
Self::make_date_multiple(&this, args, context)
Self::make_date_multiple(prototype, args, context)?
}
.into())
}
}
@ -383,10 +383,8 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
pub(crate) fn make_date_now(this: &JsValue) -> JsValue {
let date = Date::default();
this.set_data(ObjectData::date(date));
this.clone()
pub(crate) fn make_date_now(prototype: JsObject) -> JsObject {
JsObject::from_proto_and_data(prototype, ObjectData::date(Date::default()))
}
/// `Date(value)`
@ -400,10 +398,10 @@ impl Date {
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
pub(crate) fn make_date_single(
this: &JsValue,
prototype: JsObject,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
) -> JsResult<JsObject> {
let value = &args[0];
let tv = match this_time_value(value, context) {
Ok(dt) => dt.0,
@ -426,9 +424,10 @@ impl Date {
};
let tv = tv.filter(|time| Self::time_clip(time.timestamp_millis() as f64).is_some());
let date = Date(tv);
this.set_data(ObjectData::date(date));
Ok(this.clone())
Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::date(Date(tv)),
))
}
/// `Date(year, month [ , date [ , hours [ , minutes [ , seconds [ , ms ] ] ] ] ])`
@ -442,10 +441,10 @@ impl Date {
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
pub(crate) fn make_date_multiple(
this: &JsValue,
prototype: JsObject,
args: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
) -> JsResult<JsObject> {
let mut year = args[0].to_number(context)?;
let month = args[1].to_number(context)?;
let day = args
@ -466,9 +465,10 @@ impl Date {
// If any of the args are infinity or NaN, return an invalid date.
if !check_normal_opt!(year, month, day, hour, min, sec, milli) {
let date = Date(None);
this.set_data(ObjectData::date(date));
return Ok(this.clone());
return Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::date(Date(None)),
));
}
if (0.0..=99.0).contains(&year) {
@ -493,9 +493,10 @@ impl Date {
Some(milli),
);
this.set_data(ObjectData::date(date));
Ok(this.clone())
Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::date(date),
))
}
/// `Date.prototype[@@toPrimitive]`

13
boa/src/builtins/error/eval.rs

@ -13,6 +13,7 @@
use crate::context::StandardObjects;
use crate::object::internal_methods::get_prototype_from_constructor;
use crate::object::JsObject;
use crate::{
builtins::BuiltIn,
@ -66,18 +67,12 @@ impl EvalError {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, false, context)?;
obj.set("message", message.to_string(context)?, false, context)?;
}
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::error());
Ok(this)
Ok(obj.into())
}
}

16
boa/src/builtins/error/mod.rs

@ -13,7 +13,9 @@
use crate::{
builtins::BuiltIn,
context::StandardObjects,
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
profiler::BoaProfiler,
property::Attribute,
Context, JsResult, JsValue,
@ -81,19 +83,13 @@ impl Error {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, false, context)?;
obj.set("message", message.to_string(context)?, false, context)?;
}
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::error());
Ok(this)
Ok(obj.into())
}
/// `Error.prototype.toString()`

16
boa/src/builtins/error/range.rs

@ -12,7 +12,9 @@
use crate::{
builtins::BuiltIn,
context::StandardObjects,
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
profiler::BoaProfiler,
property::Attribute,
Context, JsResult, JsValue,
@ -62,18 +64,12 @@ impl RangeError {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, false, context)?;
obj.set("message", message.to_string(context)?, false, context)?;
}
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::error());
Ok(this)
Ok(obj.into())
}
}

16
boa/src/builtins/error/reference.rs

@ -12,7 +12,9 @@
use crate::{
builtins::BuiltIn,
context::StandardObjects,
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
profiler::BoaProfiler,
property::Attribute,
Context, JsResult, JsValue,
@ -61,18 +63,12 @@ impl ReferenceError {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, false, context)?;
obj.set("message", message.to_string(context)?, false, context)?;
}
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::error());
Ok(this)
Ok(obj.into())
}
}

16
boa/src/builtins/error/syntax.rs

@ -14,7 +14,9 @@
use crate::{
builtins::BuiltIn,
context::StandardObjects,
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
profiler::BoaProfiler,
property::Attribute,
Context, JsResult, JsValue,
@ -64,18 +66,12 @@ impl SyntaxError {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, false, context)?;
obj.set("message", message.to_string(context)?, false, context)?;
}
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::error());
Ok(this)
Ok(obj.into())
}
}

16
boa/src/builtins/error/type.rs

@ -18,7 +18,9 @@
use crate::{
builtins::BuiltIn,
context::StandardObjects,
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
property::Attribute,
BoaProfiler, Context, JsResult, JsValue,
};
@ -67,18 +69,12 @@ impl TypeError {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, false, context)?;
obj.set("message", message.to_string(context)?, false, context)?;
}
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::error());
Ok(this)
Ok(obj.into())
}
}

16
boa/src/builtins/error/uri.rs

@ -13,7 +13,9 @@
use crate::{
builtins::BuiltIn,
context::StandardObjects,
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, JsObject, ObjectData,
},
profiler::BoaProfiler,
property::Attribute,
Context, JsResult, JsValue,
@ -63,18 +65,12 @@ impl UriError {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::error_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let this = JsValue::new(obj);
let obj = JsObject::from_proto_and_data(prototype, ObjectData::error());
if let Some(message) = args.get(0) {
if !message.is_undefined() {
this.set_field("message", message.to_string(context)?, false, context)?;
obj.set("message", message.to_string(context)?, false, context)?;
}
}
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_data(ObjectData::error());
Ok(this)
Ok(obj.into())
}
}

374
boa/src/builtins/function/mod.rs

@ -11,14 +11,26 @@
//! [spec]: https://tc39.es/ecma262/#sec-function-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
use std::{
fmt,
ops::{Deref, DerefMut},
};
use dyn_clone::DynClone;
use crate::{
builtins::BuiltIn,
context::StandardObjects,
environment::lexical_environment::Environment,
gc::{Finalize, Trace},
object::JsObject,
object::{
function::Function, internal_methods::get_prototype_from_constructor, ConstructorBuilder,
FunctionBuilder, ObjectData,
internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder,
NativeObject, ObjectData,
},
property::Attribute,
property::PropertyDescriptor,
syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, JsResult, JsValue,
};
@ -27,6 +39,349 @@ use super::JsArgs;
#[cfg(test)]
mod tests;
/// Type representing a native built-in function a.k.a. function pointer.
///
/// Native functions need to have this signature in order to
/// be callable from Javascript.
pub type NativeFunctionSignature = fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>;
// Allows restricting closures to only `Copy` ones.
// Used the sealed pattern to disallow external implementations
// of `DynCopy`.
mod sealed {
pub trait Sealed {}
impl<T: Copy> Sealed for T {}
}
pub trait DynCopy: sealed::Sealed {}
impl<T: Copy> DynCopy for T {}
/// Trait representing a native built-in closure.
///
/// Closures need to have this signature in order to
/// be callable from Javascript, but most of the time the compiler
/// is smart enough to correctly infer the types.
pub trait ClosureFunctionSignature:
Fn(&JsValue, &[JsValue], Captures, &mut Context) -> JsResult<JsValue> + DynCopy + DynClone + 'static
{
}
// The `Copy` bound automatically infers `DynCopy` and `DynClone`
impl<T> ClosureFunctionSignature for T where
T: Fn(&JsValue, &[JsValue], Captures, &mut Context) -> JsResult<JsValue> + Copy + 'static
{
}
// Allows cloning Box<dyn ClosureFunctionSignature>
dyn_clone::clone_trait_object!(ClosureFunctionSignature);
#[derive(Debug, Trace, Finalize, PartialEq, Clone)]
pub enum ThisMode {
Lexical,
Strict,
Global,
}
impl ThisMode {
/// Returns `true` if the this mode is `Lexical`.
pub fn is_lexical(&self) -> bool {
matches!(self, Self::Lexical)
}
/// Returns `true` if the this mode is `Strict`.
pub fn is_strict(&self) -> bool {
matches!(self, Self::Strict)
}
/// Returns `true` if the this mode is `Global`.
pub fn is_global(&self) -> bool {
matches!(self, Self::Global)
}
}
#[derive(Debug, Trace, Finalize, PartialEq, Clone)]
pub enum ConstructorKind {
Base,
Derived,
}
impl ConstructorKind {
/// Returns `true` if the constructor kind is `Base`.
pub fn is_base(&self) -> bool {
matches!(self, Self::Base)
}
/// Returns `true` if the constructor kind is `Derived`.
pub fn is_derived(&self) -> bool {
matches!(self, Self::Derived)
}
}
// We don't use a standalone `NativeObject` for `Captures` because it doesn't
// guarantee that the internal type implements `Clone`.
// This private trait guarantees that the internal type passed to `Captures`
// implements `Clone`, and `DynClone` allows us to implement `Clone` for
// `Box<dyn CapturesObject>`.
trait CapturesObject: NativeObject + DynClone {}
impl<T: NativeObject + Clone> CapturesObject for T {}
dyn_clone::clone_trait_object!(CapturesObject);
/// Wrapper for `Box<dyn NativeObject + Clone>` that allows passing additional
/// captures through a `Copy` closure.
///
/// Any type implementing `Trace + Any + Debug + Clone`
/// can be used as a capture context, so you can pass e.g. a String,
/// a tuple or even a full struct.
///
/// You can downcast to any type and handle the fail case as you like
/// with `downcast_ref` and `downcast_mut`, or you can use `try_downcast_ref`
/// and `try_downcast_mut` to automatically throw a `TypeError` if the downcast
/// fails.
#[derive(Debug, Clone, Trace, Finalize)]
pub struct Captures(Box<dyn CapturesObject>);
impl Captures {
/// Creates a new capture context.
pub(crate) fn new<T>(captures: T) -> Self
where
T: NativeObject + Clone,
{
Self(Box::new(captures))
}
/// Downcasts `Captures` to the specified type, returning a reference to the
/// downcasted type if successful or `None` otherwise.
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: NativeObject + Clone,
{
self.0.deref().as_any().downcast_ref::<T>()
}
/// Mutably downcasts `Captures` to the specified type, returning a
/// mutable reference to the downcasted type if successful or `None` otherwise.
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: NativeObject + Clone,
{
self.0.deref_mut().as_mut_any().downcast_mut::<T>()
}
/// Downcasts `Captures` to the specified type, returning a reference to the
/// downcasted type if successful or a `TypeError` otherwise.
pub fn try_downcast_ref<T>(&self, context: &mut Context) -> JsResult<&T>
where
T: NativeObject + Clone,
{
self.0
.deref()
.as_any()
.downcast_ref::<T>()
.ok_or_else(|| context.construct_type_error("cannot downcast `Captures` to given type"))
}
/// Downcasts `Captures` to the specified type, returning a reference to the
/// downcasted type if successful or a `TypeError` otherwise.
pub fn try_downcast_mut<T>(&mut self, context: &mut Context) -> JsResult<&mut T>
where
T: NativeObject + Clone,
{
self.0
.deref_mut()
.as_mut_any()
.downcast_mut::<T>()
.ok_or_else(|| context.construct_type_error("cannot downcast `Captures` to given type"))
}
}
/// Boa representation of a Function Object.
///
/// FunctionBody is specific to this interpreter, it will either be Rust code or JavaScript code (AST Node)
///
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
#[derive(Clone, Trace, Finalize)]
pub enum Function {
Native {
#[unsafe_ignore_trace]
function: NativeFunctionSignature,
constructable: bool,
},
Closure {
#[unsafe_ignore_trace]
function: Box<dyn ClosureFunctionSignature>,
constructable: bool,
captures: Captures,
},
Ordinary {
constructable: bool,
this_mode: ThisMode,
body: RcStatementList,
params: Box<[FormalParameter]>,
environment: Environment,
},
#[cfg(feature = "vm")]
VmOrdinary {
code: gc::Gc<crate::vm::CodeBlock>,
environment: Environment,
},
}
impl fmt::Debug for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Function {{ ... }}")
}
}
impl Function {
// Adds the final rest parameters to the Environment as an array
#[cfg(not(feature = "vm"))]
pub(crate) fn add_rest_param(
param: &FormalParameter,
index: usize,
args_list: &[JsValue],
context: &mut Context,
local_env: &Environment,
) {
use crate::builtins::Array;
// Create array of values
let array = Array::new_array(context);
Array::add_to_array_object(&array, args_list.get(index..).unwrap_or_default(), context)
.unwrap();
// Create binding
local_env
// Function parameters can share names in JavaScript...
.create_mutable_binding(param.name(), false, true, context)
.expect("Failed to create binding for rest param");
// Set Binding to value
local_env
.initialize_binding(param.name(), array, context)
.expect("Failed to initialize rest param");
}
// Adds an argument to the environment
pub(crate) fn add_arguments_to_environment(
param: &FormalParameter,
value: JsValue,
local_env: &Environment,
context: &mut Context,
) {
// Create binding
local_env
.create_mutable_binding(param.name(), false, true, context)
.expect("Failed to create binding");
// Set Binding to value
local_env
.initialize_binding(param.name(), value, context)
.expect("Failed to intialize binding");
}
/// Returns true if the function object is constructable.
pub fn is_constructable(&self) -> bool {
match self {
Self::Native { constructable, .. } => *constructable,
Self::Closure { constructable, .. } => *constructable,
Self::Ordinary { constructable, .. } => *constructable,
#[cfg(feature = "vm")]
Self::VmOrdinary { code, .. } => code.constructable,
}
}
}
/// Arguments.
///
/// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject>
pub fn create_unmapped_arguments_object(
arguments_list: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let len = arguments_list.len();
let obj = JsObject::empty();
// Set length
let length = PropertyDescriptor::builder()
.value(len)
.writable(true)
.enumerable(false)
.configurable(true)
.build();
// Define length as a property
crate::object::internal_methods::ordinary_define_own_property(
&obj,
"length".into(),
length,
context,
)?;
let mut index: usize = 0;
while index < len {
let val = arguments_list.get(index).expect("Could not get argument");
let prop = PropertyDescriptor::builder()
.value(val.clone())
.writable(true)
.enumerable(true)
.configurable(true);
obj.insert(index, prop);
index += 1;
}
Ok(JsValue::new(obj))
}
/// Creates a new member function of a `Object` or `prototype`.
///
/// A function registered using this macro can then be called from Javascript using:
///
/// parent.name()
///
/// See the javascript 'Number.toString()' as an example.
///
/// # Arguments
/// function: The function to register as a built in function.
/// name: The name of the function (how it will be called but without the ()).
/// parent: The object to register the function on, if the global object is used then the function is instead called as name()
/// without requiring the parent, see parseInt() as an example.
/// length: As described at <https://tc39.es/ecma262/#sec-function-instances-length>, The value of the "length" property is an integer that
/// indicates the typical number of arguments expected by the function. However, the language permits the function to be invoked with
/// some other number of arguments.
///
/// If no length is provided, the length will be set to 0.
// TODO: deprecate/remove this.
pub(crate) fn make_builtin_fn<N>(
function: NativeFunctionSignature,
name: N,
parent: &JsObject,
length: usize,
interpreter: &Context,
) where
N: Into<String>,
{
let name = name.into();
let _timer = BoaProfiler::global().start_event(&format!("make_builtin_fn: {}", &name), "init");
let function = JsObject::from_proto_and_data(
interpreter.standard_objects().function_object().prototype(),
ObjectData::function(Function::Native {
function,
constructable: false,
}),
);
let attribute = PropertyDescriptor::builder()
.writable(false)
.enumerable(false)
.configurable(true);
function.insert_property("length", attribute.clone().value(length));
function.insert_property("name", attribute.value(name.as_str()));
parent.clone().insert_property(
name,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
}
#[derive(Debug, Clone, Copy)]
pub struct BuiltInFunctionObject;
@ -40,17 +395,16 @@ impl BuiltInFunctionObject {
) -> JsResult<JsValue> {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::function_object, context)?;
let this = JsValue::new_object(context);
this.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
this.set_data(ObjectData::function(Function::Native {
let this = JsObject::from_proto_and_data(
prototype,
ObjectData::function(Function::Native {
function: |_, _, _| Ok(JsValue::undefined()),
constructable: true,
}));
Ok(this)
}),
);
Ok(this.into())
}
fn prototype(_: &JsValue, _: &[JsValue], _: &mut Context) -> JsResult<JsValue> {

21
boa/src/builtins/iterable/mod.rs

@ -24,24 +24,15 @@ impl IteratorPrototypes {
pub(crate) fn init(context: &mut Context) -> Self {
let iterator_prototype = create_iterator_prototype(context);
Self {
array_iterator: ArrayIterator::create_prototype(
iterator_prototype.clone().into(),
context,
),
set_iterator: SetIterator::create_prototype(iterator_prototype.clone().into(), context),
string_iterator: StringIterator::create_prototype(
iterator_prototype.clone().into(),
context,
),
array_iterator: ArrayIterator::create_prototype(iterator_prototype.clone(), context),
set_iterator: SetIterator::create_prototype(iterator_prototype.clone(), context),
string_iterator: StringIterator::create_prototype(iterator_prototype.clone(), context),
regexp_string_iterator: RegExpStringIterator::create_prototype(
iterator_prototype.clone().into(),
context,
),
map_iterator: MapIterator::create_prototype(iterator_prototype.clone().into(), context),
for_in_iterator: ForInIterator::create_prototype(
iterator_prototype.clone().into(),
iterator_prototype.clone(),
context,
),
map_iterator: MapIterator::create_prototype(iterator_prototype.clone(), context),
for_in_iterator: ForInIterator::create_prototype(iterator_prototype.clone(), context),
iterator_prototype,
}
}

25
boa/src/builtins/map/map_iterator.rs

@ -1,6 +1,6 @@
use crate::{
builtins::{iterable::create_iter_result_object, Array, JsValue},
object::{function::make_builtin_fn, JsObject, ObjectData},
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, Array, JsValue},
object::{JsObject, ObjectData},
property::{PropertyDescriptor, PropertyNameKind},
symbol::WellKnownSymbols,
BoaProfiler, Context, JsResult,
@ -47,13 +47,11 @@ impl MapIterator {
map_iteration_kind: kind,
lock,
};
let map_iterator = JsValue::new_object(context);
map_iterator.set_data(ObjectData::map_iterator(iter));
map_iterator
.as_object()
.expect("map iterator object")
.set_prototype_instance(context.iterator_prototypes().map_iterator().into());
return Ok(map_iterator);
let map_iterator = JsObject::from_proto_and_data(
context.iterator_prototypes().map_iterator(),
ObjectData::map_iterator(iter),
);
return Ok(map_iterator.into());
}
}
context.throw_type_error("`this` is not a Map")
@ -123,13 +121,16 @@ impl MapIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%mapiteratorprototype%-object
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
pub(crate) fn create_prototype(
iterator_prototype: JsObject,
context: &mut Context,
) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let map_iterator = context.construct_object();
let map_iterator =
JsObject::from_proto_and_data(iterator_prototype, ObjectData::ordinary());
make_builtin_fn(Self::next, "next", &map_iterator, 0, context);
map_iterator.set_prototype_instance(iterator_prototype);
let to_string_tag = WellKnownSymbols::to_string_tag();
let to_string_tag_property = PropertyDescriptor::builder()

7
boa/src/builtins/map/mod.rs

@ -133,13 +133,10 @@ impl Map {
}
// 2. Let map be ? OrdinaryCreateFromConstructor(NewTarget, "%Map.prototype%", « [[MapData]] »).
// 3. Set map.[[MapData]] to a new empty List.
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::map_object, context)?;
let map = context.construct_object();
map.set_prototype_instance(prototype.into());
// 3. Set map.[[MapData]] to a new empty List.
map.borrow_mut().data = ObjectData::map(OrderedMap::new());
let map = JsObject::from_proto_and_data(prototype, ObjectData::map(OrderedMap::new()));
// 4. If iterable is either undefined or null, return map.
let iterable = match args.get_or_undefined(0) {

17
boa/src/builtins/number/mod.rs

@ -16,12 +16,10 @@
use super::string::is_trimmable_whitespace;
use super::JsArgs;
use crate::context::StandardObjects;
use crate::object::JsObject;
use crate::{
builtins::BuiltIn,
object::{
function::make_builtin_fn, internal_methods::get_prototype_from_constructor,
ConstructorBuilder, ObjectData,
},
builtins::{function::make_builtin_fn, BuiltIn},
object::{internal_methods::get_prototype_from_constructor, ConstructorBuilder, ObjectData},
property::Attribute,
value::{AbstractRelation, IntegerOrInfinity, JsValue},
BoaProfiler, Context, JsResult,
@ -172,13 +170,8 @@ impl Number {
}
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::number_object, context)?;
let this = JsValue::new_object(context);
this.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
this.set_data(ObjectData::number(data));
Ok(this)
let this = JsObject::from_proto_and_data(prototype, ObjectData::number(data));
Ok(this.into())
}
/// This function returns a `JsResult` of the number `Value`.

25
boa/src/builtins/object/for_in_iterator.rs

@ -1,7 +1,7 @@
use crate::{
builtins::iterable::create_iter_result_object,
builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
gc::{Finalize, Trace},
object::{function::make_builtin_fn, JsObject, ObjectData},
object::{JsObject, ObjectData},
property::PropertyDescriptor,
property::PropertyKey,
symbol::WellKnownSymbols,
@ -46,13 +46,11 @@ impl ForInIterator {
///
/// [spec]: https://tc39.es/ecma262/#sec-createforiniterator
pub(crate) fn create_for_in_iterator(object: JsValue, context: &Context) -> JsValue {
let for_in_iterator = JsValue::new_object(context);
for_in_iterator.set_data(ObjectData::for_in_iterator(Self::new(object)));
for_in_iterator
.as_object()
.expect("for in iterator object")
.set_prototype_instance(context.iterator_prototypes().for_in_iterator().into());
for_in_iterator
let for_in_iterator = JsObject::from_proto_and_data(
context.iterator_prototypes().for_in_iterator(),
ObjectData::for_in_iterator(Self::new(object)),
);
for_in_iterator.into()
}
/// %ForInIteratorPrototype%.next( )
@ -129,13 +127,16 @@ impl ForInIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%foriniteratorprototype%-object
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
pub(crate) fn create_prototype(
iterator_prototype: JsObject,
context: &mut Context,
) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let for_in_iterator = context.construct_object();
let for_in_iterator =
JsObject::from_proto_and_data(iterator_prototype, ObjectData::ordinary());
make_builtin_fn(Self::next, "next", &for_in_iterator, 0, context);
for_in_iterator.set_prototype_instance(iterator_prototype);
let to_string_tag = WellKnownSymbols::to_string_tag();
let to_string_tag_property = PropertyDescriptor::builder()

20
boa/src/builtins/object/mod.rs

@ -18,7 +18,7 @@ use crate::{
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, IntegrityLevel,
JsObject, Object as BuiltinObject, ObjectData, ObjectInitializer, ObjectKind,
JsObject, ObjectData, ObjectInitializer, ObjectKind,
},
property::{Attribute, DescriptorKind, PropertyDescriptor, PropertyKey, PropertyNameKind},
symbol::WellKnownSymbols,
@ -107,20 +107,15 @@ impl Object {
StandardObjects::object_object,
context,
)?;
let object = JsValue::new_object(context);
object
.as_object()
.expect("this should be an object")
.set_prototype_instance(prototype.into());
return Ok(object);
let object = JsObject::from_proto_and_data(prototype, ObjectData::ordinary());
return Ok(object.into());
}
if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() {
return Ok(arg.to_object(context)?.into());
}
}
Ok(JsValue::new_object(context))
Ok(context.construct_object().into())
}
/// `Object.create( proto, [propertiesObject] )`
@ -138,10 +133,9 @@ impl Object {
let properties = args.get_or_undefined(1);
let obj = match prototype {
JsValue::Object(_) | JsValue::Null => JsObject::new(BuiltinObject::with_prototype(
prototype.clone(),
ObjectData::ordinary(),
)),
JsValue::Object(_) | JsValue::Null => {
JsObject::from_proto_and_data(prototype.as_object(), ObjectData::ordinary())
}
_ => {
return context.throw_type_error(format!(
"Object prototype may only be an Object or null: {}",

6
boa/src/builtins/regexp/mod.rs

@ -17,7 +17,7 @@ use crate::{
gc::{empty_trace, Finalize, Trace},
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder,
JsObject, Object, ObjectData,
JsObject, ObjectData,
},
property::Attribute,
symbol::WellKnownSymbols,
@ -263,7 +263,7 @@ impl RegExp {
fn alloc(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
let proto = get_prototype_from_constructor(this, StandardObjects::regexp_object, context)?;
Ok(JsObject::new(Object::create(proto.into())).into())
Ok(JsObject::from_proto_and_data(proto, ObjectData::ordinary()).into())
}
/// `22.2.3.2.2 RegExpInitialize ( obj, pattern, flags )`
@ -983,7 +983,7 @@ impl RegExp {
let named_groups = match_value.named_groups();
let groups = if named_groups.clone().count() > 0 {
// a. Let groups be ! OrdinaryObjectCreate(null).
let groups = JsValue::from(JsObject::new(Object::create(JsValue::null())));
let groups = JsValue::from(JsObject::empty());
// Perform 27.f here
// f. If the ith capture of R was defined with a GroupName, then

30
boa/src/builtins/regexp/regexp_string_iterator.rs

@ -12,9 +12,9 @@
use regexp::{advance_string_index, RegExp};
use crate::{
builtins::{iterable::create_iter_result_object, regexp},
builtins::{function::make_builtin_fn, iterable::create_iter_result_object, regexp},
gc::{Finalize, Trace},
object::{function::make_builtin_fn, JsObject, ObjectData},
object::{JsObject, ObjectData},
property::PropertyDescriptor,
symbol::WellKnownSymbols,
BoaProfiler, Context, JsResult, JsString, JsValue,
@ -66,24 +66,18 @@ impl RegExpStringIterator {
// and fullUnicode and performs the following steps when called:
// 5. Return ! CreateIteratorFromClosure(closure, "%RegExpStringIteratorPrototype%", %RegExpStringIteratorPrototype%).
let regexp_string_iterator = JsValue::new_object(context);
regexp_string_iterator.set_data(ObjectData::reg_exp_string_iterator(Self::new(
let regexp_string_iterator = JsObject::from_proto_and_data(
context.iterator_prototypes().regexp_string_iterator(),
ObjectData::reg_exp_string_iterator(Self::new(
matcher.clone(),
string,
global,
unicode,
)));
regexp_string_iterator
.as_object()
.expect("regexp string iterator object")
.set_prototype_instance(
context
.iterator_prototypes()
.regexp_string_iterator()
.into(),
)),
);
Ok(regexp_string_iterator)
Ok(regexp_string_iterator.into())
}
pub fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
@ -161,13 +155,15 @@ impl RegExpStringIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
pub(crate) fn create_prototype(
iterator_prototype: JsObject,
context: &mut Context,
) -> JsObject {
let _timer = BoaProfiler::global().start_event("RegExp String Iterator", "init");
// Create prototype
let result = context.construct_object();
let result = JsObject::from_proto_and_data(iterator_prototype, ObjectData::ordinary());
make_builtin_fn(Self::next, "next", &result, 0, context);
result.set_prototype_instance(iterator_prototype);
let to_string_tag = WellKnownSymbols::to_string_tag();
let to_string_tag_property = PropertyDescriptor::builder()

17
boa/src/builtins/set/mod.rs

@ -15,7 +15,7 @@ use crate::{
context::StandardObjects,
object::{
internal_methods::get_prototype_from_constructor, ConstructorBuilder, FunctionBuilder,
ObjectData,
JsObject, ObjectData,
},
property::{Attribute, PropertyNameKind},
symbol::WellKnownSymbols,
@ -130,21 +130,16 @@ impl Set {
let prototype =
get_prototype_from_constructor(new_target, StandardObjects::set_object, context)?;
let obj = context.construct_object();
obj.set_prototype_instance(prototype.into());
let set = JsValue::new(obj);
// 3
set.set_data(ObjectData::set(OrderedSet::default()));
let obj = JsObject::from_proto_and_data(prototype, ObjectData::set(OrderedSet::default()));
let iterable = args.get_or_undefined(0);
// 4
if iterable.is_null_or_undefined() {
return Ok(set);
return Ok(obj.into());
}
// 5
let adder = set.get_field("add", context)?;
let adder = obj.get("add", context)?;
// 6
if !adder.is_function() {
@ -163,7 +158,7 @@ impl Set {
let next_value = next.value;
// d, e
if let Err(status) = context.call(&adder, &set, &[next_value]) {
if let Err(status) = context.call(&adder, &obj.clone().into(), &[next_value]) {
return iterator_record.close(Err(status), context);
}
@ -171,7 +166,7 @@ impl Set {
}
// 8.b
Ok(set)
Ok(obj.into())
}
/// `get Set [ @@species ]`

25
boa/src/builtins/set/set_iterator.rs

@ -1,8 +1,8 @@
use crate::{
builtins::iterable::create_iter_result_object,
builtins::Array,
builtins::JsValue,
object::{function::make_builtin_fn, JsObject, ObjectData},
builtins::{function::make_builtin_fn, iterable::create_iter_result_object},
object::{JsObject, ObjectData},
property::{PropertyDescriptor, PropertyNameKind},
symbol::WellKnownSymbols,
BoaProfiler, Context, JsResult,
@ -47,13 +47,11 @@ impl SetIterator {
kind: PropertyNameKind,
context: &Context,
) -> JsValue {
let set_iterator = JsValue::new_object(context);
set_iterator.set_data(ObjectData::set_iterator(Self::new(set, kind)));
set_iterator
.as_object()
.expect("set iterator object")
.set_prototype_instance(context.iterator_prototypes().set_iterator().into());
set_iterator
let set_iterator = JsObject::from_proto_and_data(
context.iterator_prototypes().set_iterator(),
ObjectData::set_iterator(Self::new(set, kind)),
);
set_iterator.into()
}
/// %SetIteratorPrototype%.next( )
@ -140,13 +138,16 @@ impl SetIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%setiteratorprototype%-object
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
pub(crate) fn create_prototype(
iterator_prototype: JsObject,
context: &mut Context,
) -> JsObject {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let set_iterator = context.construct_object();
let set_iterator =
JsObject::from_proto_and_data(iterator_prototype, ObjectData::ordinary());
make_builtin_fn(Self::next, "next", &set_iterator, 0, context);
set_iterator.set_prototype_instance(iterator_prototype);
let to_string_tag = WellKnownSymbols::to_string_tag();
let to_string_tag_property = PropertyDescriptor::builder()

4
boa/src/builtins/string/mod.rs

@ -205,9 +205,7 @@ impl String {
// 4. Set S.[[GetOwnProperty]] as specified in 10.4.3.1.
// 5. Set S.[[DefineOwnProperty]] as specified in 10.4.3.2.
// 6. Set S.[[OwnPropertyKeys]] as specified in 10.4.3.3.
let s = context.construct_object();
s.set_prototype_instance(prototype.into());
s.borrow_mut().data = ObjectData::string(value);
let s = JsObject::from_proto_and_data(prototype, ObjectData::string(value));
// 8. Perform ! DefinePropertyOrThrow(S, "length", PropertyDescriptor { [[Value]]: 𝔽(length),
// [[Writable]]: false, [[Enumerable]]: false, [[Configurable]]: false }).

27
boa/src/builtins/string/string_iterator.rs

@ -1,7 +1,9 @@
use crate::{
builtins::{iterable::create_iter_result_object, string::code_point_at},
builtins::{
function::make_builtin_fn, iterable::create_iter_result_object, string::code_point_at,
},
gc::{Finalize, Trace},
object::{function::make_builtin_fn, JsObject, ObjectData},
object::{JsObject, ObjectData},
property::PropertyDescriptor,
symbol::WellKnownSymbols,
BoaProfiler, Context, JsResult, JsValue,
@ -22,13 +24,11 @@ impl StringIterator {
}
pub fn create_string_iterator(string: JsValue, context: &mut Context) -> JsResult<JsValue> {
let string_iterator = JsValue::new_object(context);
string_iterator.set_data(ObjectData::string_iterator(Self::new(string)));
string_iterator
.as_object()
.expect("array iterator object")
.set_prototype_instance(context.iterator_prototypes().string_iterator().into());
Ok(string_iterator)
let string_iterator = JsObject::from_proto_and_data(
context.iterator_prototypes().string_iterator(),
ObjectData::string_iterator(Self::new(string)),
);
Ok(string_iterator.into())
}
pub fn next(this: &JsValue, _: &[JsValue], context: &mut Context) -> JsResult<JsValue> {
@ -76,13 +76,16 @@ impl StringIterator {
/// - [ECMA reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-%arrayiteratorprototype%-object
pub(crate) fn create_prototype(iterator_prototype: JsValue, context: &mut Context) -> JsObject {
pub(crate) fn create_prototype(
iterator_prototype: JsObject,
context: &mut Context,
) -> JsObject {
let _timer = BoaProfiler::global().start_event("String Iterator", "init");
// Create prototype
let array_iterator = context.construct_object();
let array_iterator =
JsObject::from_proto_and_data(iterator_prototype, ObjectData::ordinary());
make_builtin_fn(Self::next, "next", &array_iterator, 0, context);
array_iterator.set_prototype_instance(iterator_prototype);
let to_string_tag = WellKnownSymbols::to_string_tag();
let to_string_tag_property = PropertyDescriptor::builder()

2
boa/src/bytecompiler.rs

@ -1,7 +1,7 @@
use gc::Gc;
use crate::{
object::function::ThisMode,
builtins::function::ThisMode,
syntax::ast::{
node::{Declaration, GetConstField, GetField, StatementList},
op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp},

13
boa/src/class.rs

@ -62,10 +62,8 @@
//! [class-trait]: ./trait.Class.html
use crate::{
object::{
function::NativeFunctionSignature, ConstructorBuilder, JsObject, NativeObject, ObjectData,
PROTOTYPE,
},
builtins::function::NativeFunctionSignature,
object::{ConstructorBuilder, JsObject, NativeObject, ObjectData, PROTOTYPE},
property::{Attribute, PropertyDescriptor, PropertyKey},
Context, JsResult, JsValue,
};
@ -142,9 +140,10 @@ impl<T: Class> ClassConstructor for T {
.unwrap_or(class_prototype);
let native_instance = Self::constructor(this, args, context)?;
let object_instance = context.construct_object();
object_instance.set_prototype_instance(prototype.into());
object_instance.borrow_mut().data = ObjectData::native_object(Box::new(native_instance));
let object_instance = JsObject::from_proto_and_data(
prototype,
ObjectData::native_object(Box::new(native_instance)),
);
Ok(object_instance.into())
}
}

50
boa/src/context.rs

@ -1,13 +1,16 @@
//! Javascript context.
use crate::{
builtins::{self, iterable::IteratorPrototypes, typed_array::TypedArray},
class::{Class, ClassBuilder},
exec::Interpreter,
object::{
builtins::{
self,
function::{Function, NativeFunctionSignature, ThisMode},
FunctionBuilder, JsObject, Object, PROTOTYPE,
iterable::IteratorPrototypes,
typed_array::TypedArray,
},
class::{Class, ClassBuilder},
exec::Interpreter,
object::PROTOTYPE,
object::{FunctionBuilder, JsObject, ObjectData},
property::{Attribute, PropertyDescriptor, PropertyKey},
realm::Realm,
syntax::{
@ -39,18 +42,18 @@ pub struct StandardConstructor {
impl Default for StandardConstructor {
fn default() -> Self {
Self {
constructor: JsObject::new(Object::default()),
prototype: JsObject::new(Object::default()),
constructor: JsObject::empty(),
prototype: JsObject::empty(),
}
}
}
impl StandardConstructor {
/// Build a constructor with a defined prototype.
fn with_prototype(prototype: Object) -> Self {
fn with_prototype(prototype: JsObject) -> Self {
Self {
constructor: JsObject::new(Object::default()),
prototype: JsObject::new(prototype),
constructor: JsObject::empty(),
prototype,
}
}
@ -114,9 +117,18 @@ impl Default for StandardObjects {
function: StandardConstructor::default(),
array: StandardConstructor::default(),
bigint: StandardConstructor::default(),
number: StandardConstructor::with_prototype(Object::number(0.0)),
boolean: StandardConstructor::with_prototype(Object::boolean(false)),
string: StandardConstructor::with_prototype(Object::string("")),
number: StandardConstructor::with_prototype(JsObject::from_proto_and_data(
None,
ObjectData::number(0.0),
)),
boolean: StandardConstructor::with_prototype(JsObject::from_proto_and_data(
None,
ObjectData::boolean(false),
)),
string: StandardConstructor::with_prototype(JsObject::from_proto_and_data(
None,
ObjectData::string("".into()),
)),
regexp: StandardConstructor::default(),
symbol: StandardConstructor::default(),
error: StandardConstructor::default(),
@ -484,8 +496,10 @@ impl Context {
/// Constructs an object with the `%Object.prototype%` prototype.
#[inline]
pub fn construct_object(&self) -> JsObject {
let object_prototype: JsValue = self.standard_objects().object_object().prototype().into();
JsObject::new(Object::create(object_prototype))
JsObject::from_proto_and_data(
self.standard_objects().object_object().prototype(),
ObjectData::ordinary(),
)
}
/// <https://tc39.es/ecma262/#sec-call>
@ -682,8 +696,7 @@ impl Context {
P: Into<Box<[FormalParameter]>>,
{
let name = name.into();
let function_prototype: JsValue =
self.standard_objects().function_object().prototype().into();
let function_prototype = self.standard_objects().function_object().prototype();
// Every new function has a prototype property pre-made
let prototype = self.construct_object();
@ -703,7 +716,8 @@ impl Context {
environment: self.get_current_environment().clone(),
};
let function = JsObject::new(Object::function(func, function_prototype));
let function =
JsObject::from_proto_and_data(function_prototype, ObjectData::function(func));
// Set constructor field to the newly created Value (function object)
let constructor = PropertyDescriptor::builder()

376
boa/src/object/function.rs

@ -1,376 +0,0 @@
//! This module implements the global `Function` object as well as creates Native Functions.
//!
//! Objects wrap `Function`s and expose them via call/construct slots.
//!
//! `The `Function` object is used for matching text with a pattern.
//!
//! More information:
//! - [ECMAScript reference][spec]
//! - [MDN documentation][mdn]
//!
//! [spec]: https://tc39.es/ecma262/#sec-function-objects
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
use crate::{
environment::lexical_environment::Environment,
gc::{Finalize, Trace},
object::{JsObject, Object},
property::PropertyDescriptor,
syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, JsResult, JsValue,
};
use dyn_clone::DynClone;
use std::{
fmt,
ops::{Deref, DerefMut},
};
use super::NativeObject;
/// Type representing a native built-in function a.k.a. function pointer.
///
/// Native functions need to have this signature in order to
/// be callable from Javascript.
pub type NativeFunctionSignature = fn(&JsValue, &[JsValue], &mut Context) -> JsResult<JsValue>;
// Allows restricting closures to only `Copy` ones.
// Used the sealed pattern to disallow external implementations
// of `DynCopy`.
mod sealed {
pub trait Sealed {}
impl<T: Copy> Sealed for T {}
}
pub trait DynCopy: sealed::Sealed {}
impl<T: Copy> DynCopy for T {}
/// Trait representing a native built-in closure.
///
/// Closures need to have this signature in order to
/// be callable from Javascript, but most of the time the compiler
/// is smart enough to correctly infer the types.
pub trait ClosureFunctionSignature:
Fn(&JsValue, &[JsValue], Captures, &mut Context) -> JsResult<JsValue> + DynCopy + DynClone + 'static
{
}
// The `Copy` bound automatically infers `DynCopy` and `DynClone`
impl<T> ClosureFunctionSignature for T where
T: Fn(&JsValue, &[JsValue], Captures, &mut Context) -> JsResult<JsValue> + Copy + 'static
{
}
// Allows cloning Box<dyn ClosureFunctionSignature>
dyn_clone::clone_trait_object!(ClosureFunctionSignature);
#[derive(Debug, Trace, Finalize, PartialEq, Clone)]
pub enum ThisMode {
Lexical,
Strict,
Global,
}
impl ThisMode {
/// Returns `true` if the this mode is `Lexical`.
pub fn is_lexical(&self) -> bool {
matches!(self, Self::Lexical)
}
/// Returns `true` if the this mode is `Strict`.
pub fn is_strict(&self) -> bool {
matches!(self, Self::Strict)
}
/// Returns `true` if the this mode is `Global`.
pub fn is_global(&self) -> bool {
matches!(self, Self::Global)
}
}
#[derive(Debug, Trace, Finalize, PartialEq, Clone)]
pub enum ConstructorKind {
Base,
Derived,
}
impl ConstructorKind {
/// Returns `true` if the constructor kind is `Base`.
pub fn is_base(&self) -> bool {
matches!(self, Self::Base)
}
/// Returns `true` if the constructor kind is `Derived`.
pub fn is_derived(&self) -> bool {
matches!(self, Self::Derived)
}
}
// We don't use a standalone `NativeObject` for `Captures` because it doesn't
// guarantee that the internal type implements `Clone`.
// This private trait guarantees that the internal type passed to `Captures`
// implements `Clone`, and `DynClone` allows us to implement `Clone` for
// `Box<dyn CapturesObject>`.
trait CapturesObject: NativeObject + DynClone {}
impl<T: NativeObject + Clone> CapturesObject for T {}
dyn_clone::clone_trait_object!(CapturesObject);
/// Wrapper for `Box<dyn NativeObject + Clone>` that allows passing additional
/// captures through a `Copy` closure.
///
/// Any type implementing `Trace + Any + Debug + Clone`
/// can be used as a capture context, so you can pass e.g. a String,
/// a tuple or even a full struct.
///
/// You can downcast to any type and handle the fail case as you like
/// with `downcast_ref` and `downcast_mut`, or you can use `try_downcast_ref`
/// and `try_downcast_mut` to automatically throw a `TypeError` if the downcast
/// fails.
#[derive(Debug, Clone, Trace, Finalize)]
pub struct Captures(Box<dyn CapturesObject>);
impl Captures {
/// Creates a new capture context.
pub(crate) fn new<T>(captures: T) -> Self
where
T: NativeObject + Clone,
{
Self(Box::new(captures))
}
/// Downcasts `Captures` to the specified type, returning a reference to the
/// downcasted type if successful or `None` otherwise.
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: NativeObject + Clone,
{
self.0.deref().as_any().downcast_ref::<T>()
}
/// Mutably downcasts `Captures` to the specified type, returning a
/// mutable reference to the downcasted type if successful or `None` otherwise.
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: NativeObject + Clone,
{
self.0.deref_mut().as_mut_any().downcast_mut::<T>()
}
/// Downcasts `Captures` to the specified type, returning a reference to the
/// downcasted type if successful or a `TypeError` otherwise.
pub fn try_downcast_ref<T>(&self, context: &mut Context) -> JsResult<&T>
where
T: NativeObject + Clone,
{
self.0
.deref()
.as_any()
.downcast_ref::<T>()
.ok_or_else(|| context.construct_type_error("cannot downcast `Captures` to given type"))
}
/// Downcasts `Captures` to the specified type, returning a reference to the
/// downcasted type if successful or a `TypeError` otherwise.
pub fn try_downcast_mut<T>(&mut self, context: &mut Context) -> JsResult<&mut T>
where
T: NativeObject + Clone,
{
self.0
.deref_mut()
.as_mut_any()
.downcast_mut::<T>()
.ok_or_else(|| context.construct_type_error("cannot downcast `Captures` to given type"))
}
}
/// Boa representation of a Function Object.
///
/// FunctionBody is specific to this interpreter, it will either be Rust code or JavaScript code (AST Node)
///
/// <https://tc39.es/ecma262/#sec-ecmascript-function-objects>
#[derive(Clone, Trace, Finalize)]
pub enum Function {
Native {
#[unsafe_ignore_trace]
function: NativeFunctionSignature,
constructable: bool,
},
Closure {
#[unsafe_ignore_trace]
function: Box<dyn ClosureFunctionSignature>,
constructable: bool,
captures: Captures,
},
Ordinary {
constructable: bool,
this_mode: ThisMode,
body: RcStatementList,
params: Box<[FormalParameter]>,
environment: Environment,
},
#[cfg(feature = "vm")]
VmOrdinary {
code: gc::Gc<crate::vm::CodeBlock>,
environment: Environment,
},
}
impl fmt::Debug for Function {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "Function {{ ... }}")
}
}
impl Function {
// Adds the final rest parameters to the Environment as an array
#[cfg(not(feature = "vm"))]
pub(crate) fn add_rest_param(
param: &FormalParameter,
index: usize,
args_list: &[JsValue],
context: &mut Context,
local_env: &Environment,
) {
use crate::builtins::Array;
// Create array of values
let array = Array::new_array(context);
Array::add_to_array_object(&array, args_list.get(index..).unwrap_or_default(), context)
.unwrap();
// Create binding
local_env
// Function parameters can share names in JavaScript...
.create_mutable_binding(param.name(), false, true, context)
.expect("Failed to create binding for rest param");
// Set Binding to value
local_env
.initialize_binding(param.name(), array, context)
.expect("Failed to initialize rest param");
}
// Adds an argument to the environment
pub(crate) fn add_arguments_to_environment(
param: &FormalParameter,
value: JsValue,
local_env: &Environment,
context: &mut Context,
) {
// Create binding
local_env
.create_mutable_binding(param.name(), false, true, context)
.expect("Failed to create binding");
// Set Binding to value
local_env
.initialize_binding(param.name(), value, context)
.expect("Failed to intialize binding");
}
/// Returns true if the function object is constructable.
pub fn is_constructable(&self) -> bool {
match self {
Self::Native { constructable, .. } => *constructable,
Self::Closure { constructable, .. } => *constructable,
Self::Ordinary { constructable, .. } => *constructable,
#[cfg(feature = "vm")]
Self::VmOrdinary { code, .. } => code.constructable,
}
}
}
/// Arguments.
///
/// <https://tc39.es/ecma262/#sec-createunmappedargumentsobject>
pub fn create_unmapped_arguments_object(
arguments_list: &[JsValue],
context: &mut Context,
) -> JsResult<JsValue> {
let len = arguments_list.len();
let obj = JsObject::new(Object::default());
// Set length
let length = PropertyDescriptor::builder()
.value(len)
.writable(true)
.enumerable(false)
.configurable(true)
.build();
// Define length as a property
crate::object::internal_methods::ordinary_define_own_property(
&obj,
"length".into(),
length,
context,
)?;
let mut index: usize = 0;
while index < len {
let val = arguments_list.get(index).expect("Could not get argument");
let prop = PropertyDescriptor::builder()
.value(val.clone())
.writable(true)
.enumerable(true)
.configurable(true);
obj.insert(index, prop);
index += 1;
}
Ok(JsValue::new(obj))
}
/// Creates a new member function of a `Object` or `prototype`.
///
/// A function registered using this macro can then be called from Javascript using:
///
/// parent.name()
///
/// See the javascript 'Number.toString()' as an example.
///
/// # Arguments
/// function: The function to register as a built in function.
/// name: The name of the function (how it will be called but without the ()).
/// parent: The object to register the function on, if the global object is used then the function is instead called as name()
/// without requiring the parent, see parseInt() as an example.
/// length: As described at <https://tc39.es/ecma262/#sec-function-instances-length>, The value of the "length" property is an integer that
/// indicates the typical number of arguments expected by the function. However, the language permits the function to be invoked with
/// some other number of arguments.
///
/// If no length is provided, the length will be set to 0.
// TODO: deprecate/remove this.
pub(crate) fn make_builtin_fn<N>(
function: NativeFunctionSignature,
name: N,
parent: &JsObject,
length: usize,
interpreter: &Context,
) where
N: Into<String>,
{
let name = name.into();
let _timer = BoaProfiler::global().start_event(&format!("make_builtin_fn: {}", &name), "init");
let mut function = Object::function(
Function::Native {
function,
constructable: false,
},
interpreter
.standard_objects()
.function_object()
.prototype()
.into(),
);
let attribute = PropertyDescriptor::builder()
.writable(false)
.enumerable(false)
.configurable(true);
function.insert_property("length", attribute.clone().value(length));
function.insert_property("name", attribute.value(name.as_str()));
parent.clone().insert_property(
name,
PropertyDescriptor::builder()
.value(function)
.writable(true)
.enumerable(false)
.configurable(true),
);
}

55
boa/src/object/jsobject.rs

@ -20,19 +20,16 @@ use std::{
#[cfg(not(feature = "vm"))]
use crate::{
builtins::function::{
create_unmapped_arguments_object, Captures, ClosureFunctionSignature, Function,
NativeFunctionSignature,
},
environment::{
environment_record_trait::EnvironmentRecordTrait,
function_environment_record::{BindingStatus, FunctionEnvironmentRecord},
lexical_environment::Environment,
},
exec::InterpreterState,
object::{
function::{
create_unmapped_arguments_object, Captures, ClosureFunctionSignature, Function,
NativeFunctionSignature,
},
PROTOTYPE,
},
syntax::ast::node::RcStatementList,
Executable,
};
@ -63,12 +60,32 @@ enum FunctionBody {
}
impl JsObject {
/// Create a new `GcObject` from a `Object`.
/// Create a new `JsObject` from an internal `Object`.
#[inline]
pub fn new(object: Object) -> Self {
fn from_object(object: Object) -> Self {
Self(Gc::new(GcCell::new(object)))
}
/// Create a new empty `JsObject`, with `prototype` set to `JsValue::Null`
/// and `data` set to `ObjectData::ordinary`
pub fn empty() -> Self {
Self::from_object(Object::default())
}
/// The more general form of `OrdinaryObjectCreate` and `MakeBasicObject`.
///
/// Create a `JsObject` and automatically set its internal methods and
/// internal slots from the `data` provided.
#[inline]
pub fn from_proto_and_data<O: Into<Option<JsObject>>>(prototype: O, data: ObjectData) -> Self {
Self::from_object(Object {
data,
prototype: prototype.into().map_or(JsValue::Null, JsValue::new),
extensible: true,
properties: Default::default(),
})
}
/// Immutably borrows the `Object`.
///
/// The borrow lasts until the returned `Ref` exits scope.
@ -143,6 +160,10 @@ impl JsObject {
context: &mut Context,
construct: bool,
) -> JsResult<JsValue> {
use crate::context::StandardObjects;
use super::internal_methods::get_prototype_from_constructor;
let this_function_object = self.clone();
let mut has_parameter_expressions = false;
@ -180,21 +201,13 @@ impl JsObject {
// prototype as prototype for the new object
// see <https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor>
// see <https://tc39.es/ecma262/#sec-getprototypefromconstructor>
let proto = this_target.as_object().unwrap().__get__(
&PROTOTYPE.into(),
this_target.clone(),
let proto = get_prototype_from_constructor(
this_target,
StandardObjects::object_object,
context,
)?;
let proto = if proto.is_object() {
proto
} else {
context
.standard_objects()
.object_object()
.prototype()
JsObject::from_proto_and_data(Some(proto), ObjectData::ordinary())
.into()
};
JsValue::new(Object::create(proto))
} else {
this_target.clone()
};

134
boa/src/object/mod.rs

@ -2,17 +2,22 @@
use crate::{
builtins::{
array::array_iterator::ArrayIterator, array_buffer::ArrayBuffer,
map::map_iterator::MapIterator, map::ordered_map::OrderedMap,
regexp::regexp_string_iterator::RegExpStringIterator, set::ordered_set::OrderedSet,
set::set_iterator::SetIterator, string::string_iterator::StringIterator,
typed_array::integer_indexed_object::IntegerIndexed, Date, RegExp,
array::array_iterator::ArrayIterator,
array_buffer::ArrayBuffer,
function::{Captures, Function, NativeFunctionSignature},
map::map_iterator::MapIterator,
map::ordered_map::OrderedMap,
regexp::regexp_string_iterator::RegExpStringIterator,
set::ordered_set::OrderedSet,
set::set_iterator::SetIterator,
string::string_iterator::StringIterator,
typed_array::integer_indexed_object::IntegerIndexed,
Date, RegExp,
},
context::StandardConstructor,
gc::{Finalize, Trace},
object::function::{Captures, Function, NativeFunctionSignature},
property::{Attribute, PropertyDescriptor, PropertyKey},
BoaProfiler, Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue,
Context, JsBigInt, JsResult, JsString, JsSymbol, JsValue,
};
use std::{
any::Any,
@ -23,7 +28,6 @@ use std::{
#[cfg(test)]
mod tests;
pub mod function;
pub(crate) mod internal_methods;
mod jsobject;
mod operations;
@ -352,98 +356,6 @@ impl Default for Object {
}
impl Object {
#[inline]
pub fn new() -> Self {
Default::default()
}
/// Return a new ObjectData struct, with `kind` set to Ordinary
#[inline]
pub fn function(function: Function, prototype: JsValue) -> Self {
let _timer = BoaProfiler::global().start_event("Object::Function", "object");
Self {
data: ObjectData::function(function),
properties: PropertyMap::default(),
prototype,
extensible: true,
}
}
/// `OrdinaryObjectCreate` is used to specify the runtime creation of new ordinary objects.
///
/// More information:
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinaryobjectcreate
#[inline]
pub fn create(proto: JsValue) -> Self {
let mut obj = Self::new();
obj.prototype = proto;
obj
}
/// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
#[inline]
pub fn boolean(value: bool) -> Self {
Self {
data: ObjectData::boolean(value),
properties: PropertyMap::default(),
prototype: JsValue::null(),
extensible: true,
}
}
/// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
#[inline]
pub fn number(value: f64) -> Self {
Self {
data: ObjectData::number(value),
properties: PropertyMap::default(),
prototype: JsValue::null(),
extensible: true,
}
}
/// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
#[inline]
pub fn string<S>(value: S) -> Self
where
S: Into<JsString>,
{
Self {
data: ObjectData::string(value.into()),
properties: PropertyMap::default(),
prototype: JsValue::null(),
extensible: true,
}
}
/// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
#[inline]
pub fn bigint(value: JsBigInt) -> Self {
Self {
data: ObjectData::big_int(value),
properties: PropertyMap::default(),
prototype: JsValue::null(),
extensible: true,
}
}
/// Create a new native object of type `T`.
#[inline]
pub fn native_object<T>(value: T) -> Self
where
T: NativeObject,
{
Self {
data: ObjectData::native_object(Box::new(value)),
properties: PropertyMap::default(),
prototype: JsValue::null(),
extensible: true,
}
}
#[inline]
pub fn kind(&self) -> &ObjectKind {
&self.data.kind
@ -1007,15 +919,6 @@ impl Object {
}
}
/// Similar to `Value::new_object`, but you can pass a prototype to create from, plus a kind
#[inline]
pub fn with_prototype(proto: JsValue, data: ObjectData) -> Object {
let mut object = Object::new();
object.data = data;
object.set_prototype_instance(proto);
object
}
/// Returns `true` if it holds an Rust type that implements `NativeObject`.
#[inline]
pub fn is_native_object(&self) -> bool {
@ -1300,13 +1203,12 @@ impl<'context> FunctionBuilder<'context> {
/// Build the function object.
#[inline]
pub fn build(&mut self) -> JsObject {
let mut function = Object::function(
self.function.take().unwrap(),
let function = JsObject::from_proto_and_data(
self.context
.standard_objects()
.function_object()
.prototype()
.into(),
.prototype(),
ObjectData::function(self.function.take().unwrap()),
);
let property = PropertyDescriptor::builder()
.writable(false)
@ -1315,7 +1217,7 @@ impl<'context> FunctionBuilder<'context> {
function.insert_property("name", property.clone().value(self.name.clone()));
function.insert_property("length", property.value(self.length));
JsObject::new(function)
function
}
/// Initializes the `Function.prototype` function object.
@ -1470,8 +1372,8 @@ impl<'context> ConstructorBuilder<'context> {
Self {
context,
constructor_function: constructor,
constructor_object: JsObject::new(Object::default()),
prototype: JsObject::new(Object::default()),
constructor_object: JsObject::empty(),
prototype: JsObject::empty(),
length: 0,
name: JsString::default(),
callable: true,

8
boa/src/realm.rs

@ -8,7 +8,7 @@ use crate::{
environment::{
global_environment_record::GlobalEnvironmentRecord, lexical_environment::LexicalEnvironment,
},
object::{JsObject, Object, ObjectData},
object::{JsObject, ObjectData},
BoaProfiler,
};
use gc::Gc;
@ -29,12 +29,8 @@ impl Realm {
let _timer = BoaProfiler::global().start_event("Realm::create", "realm");
// Create brand new global object
// Global has no prototype to pass None to new_obj
let mut global = Object::default();
// Allow identification of the global object easily
global.data = ObjectData::global();
let gc_global = JsObject::new(global);
let gc_global = JsObject::from_proto_and_data(None, ObjectData::global());
// We need to clone the global here because its referenced from separate places (only pointer is cloned)
let global_env = GlobalEnvironmentRecord::new(gc_global.clone(), gc_global.clone());

2
boa/src/syntax/ast/node/declaration/arrow_function_decl/mod.rs

@ -1,7 +1,7 @@
use crate::{
builtins::function::ThisMode,
exec::Executable,
gc::{Finalize, Trace},
object::function::ThisMode,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, JsResult, JsValue,
};

2
boa/src/syntax/ast/node/declaration/function_decl/mod.rs

@ -1,8 +1,8 @@
use crate::{
builtins::function::ThisMode,
environment::lexical_environment::VariableScope,
exec::Executable,
gc::{Finalize, Trace},
object::function::ThisMode,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
BoaProfiler, Context, JsResult, JsValue,
};

2
boa/src/syntax/ast/node/declaration/function_expr/mod.rs

@ -1,7 +1,7 @@
use crate::{
builtins::function::ThisMode,
exec::Executable,
gc::{Finalize, Trace},
object::function::ThisMode,
syntax::ast::node::{join_nodes, FormalParameter, Node, StatementList},
Context, JsResult, JsValue,
};

46
boa/src/syntax/ast/node/object/mod.rs

@ -89,7 +89,7 @@ impl Object {
impl Executable for Object {
fn run(&self, context: &mut Context) -> JsResult<JsValue> {
let _timer = BoaProfiler::global().start_event("object", "exec");
let obj = JsValue::new_object(context);
let mut obj = context.construct_object();
// TODO: Implement the rest of the property types.
for property in self.properties().iter() {
@ -101,14 +101,16 @@ impl Executable for Object {
node.run(context)?.to_property_key(context)?
}
};
obj.set_property(
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.value(value.run(context)?)
.writable(true)
.enumerable(true)
.configurable(true),
);
.configurable(true)
.build(),
context,
)?;
}
PropertyDefinition::MethodDefinition(kind, name, func) => {
let name = match name {
@ -119,44 +121,50 @@ impl Executable for Object {
};
match kind {
MethodDefinitionKind::Ordinary => {
obj.set_property(
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.value(func.run(context)?)
.writable(true)
.enumerable(true)
.configurable(true),
);
.configurable(true)
.build(),
context,
)?;
}
MethodDefinitionKind::Get => {
let set = obj
.get_property(name.clone())
.__get_own_property__(&name, context)?
.as_ref()
.and_then(|a| a.set())
.cloned();
obj.set_property(
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.maybe_get(func.run(context)?.as_object())
.maybe_set(set)
.enumerable(true)
.configurable(true),
)
.configurable(true)
.build(),
context,
)?;
}
MethodDefinitionKind::Set => {
let get = obj
.get_property(name.clone())
.__get_own_property__(&name, context)?
.as_ref()
.and_then(|a| a.get())
.cloned();
obj.set_property(
obj.__define_own_property__(
name,
PropertyDescriptor::builder()
.maybe_get(get)
.maybe_set(func.run(context)?.as_object())
.enumerable(true)
.configurable(true),
)
.configurable(true)
.build(),
context,
)?;
}
}
}
@ -168,17 +176,13 @@ impl Executable for Object {
continue;
}
obj.as_object().unwrap().copy_data_properties::<String>(
&val,
vec![],
context,
)?;
obj.copy_data_properties::<String>(&val, vec![], context)?;
}
_ => {} // unimplemented!("{:?} type of property", i),
}
}
Ok(obj)
Ok(obj.into())
}
}

48
boa/src/value/conversions.rs

@ -120,54 +120,6 @@ impl From<bool> for JsValue {
}
}
impl<T> From<&[T]> for JsValue
where
T: Clone + Into<JsValue>,
{
fn from(value: &[T]) -> Self {
let mut array = Object::default();
for (i, item) in value.iter().enumerate() {
array.insert(
i,
PropertyDescriptor::builder()
.value(item.clone())
.writable(true)
.enumerable(true)
.configurable(true),
);
}
Self::from(array)
}
}
impl<T> From<Vec<T>> for JsValue
where
T: Into<JsValue>,
{
fn from(value: Vec<T>) -> Self {
let mut array = Object::default();
for (i, item) in value.into_iter().enumerate() {
array.insert(
i,
PropertyDescriptor::builder()
.value(item)
.writable(true)
.enumerable(true)
.configurable(true),
);
}
JsValue::new(array)
}
}
impl From<Object> for JsValue {
#[inline]
fn from(object: Object) -> Self {
let _timer = BoaProfiler::global().start_event("From<Object>", "value");
JsValue::Object(JsObject::new(object))
}
}
impl From<JsObject> for JsValue {
#[inline]
fn from(object: JsObject) -> Self {

48
boa/src/value/mod.rs

@ -10,7 +10,7 @@ use crate::{
number::{f64_to_int32, f64_to_uint32},
Number,
},
object::{JsObject, Object, ObjectData},
object::{JsObject, ObjectData},
property::{PropertyDescriptor, PropertyKey},
symbol::{JsSymbol, WellKnownSymbols},
BoaProfiler, Context, JsBigInt, JsResult, JsString,
@ -113,22 +113,16 @@ impl JsValue {
/// Creates a new number with `Infinity` value.
#[inline]
pub fn positive_inifnity() -> Self {
pub fn positive_infinity() -> Self {
Self::Rational(f64::INFINITY)
}
/// Creates a new number with `-Infinity` value.
#[inline]
pub fn negative_inifnity() -> Self {
pub fn negative_infinity() -> Self {
Self::Rational(f64::NEG_INFINITY)
}
/// Returns a new empty object with the `%Object.prototype%` prototype.
pub(crate) fn new_object(context: &Context) -> Self {
let _timer = BoaProfiler::global().start_event("new_object", "value");
context.construct_object().into()
}
/// Returns true if the value is an object
#[inline]
pub fn is_object(&self) -> bool {
@ -526,32 +520,30 @@ impl JsValue {
}
JsValue::Boolean(boolean) => {
let prototype = context.standard_objects().boolean_object().prototype();
Ok(JsObject::new(Object::with_prototype(
prototype.into(),
Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::boolean(*boolean),
)))
))
}
JsValue::Integer(integer) => {
let prototype = context.standard_objects().number_object().prototype();
Ok(JsObject::new(Object::with_prototype(
prototype.into(),
Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::number(f64::from(*integer)),
)))
))
}
JsValue::Rational(rational) => {
let prototype = context.standard_objects().number_object().prototype();
Ok(JsObject::new(Object::with_prototype(
prototype.into(),
Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::number(*rational),
)))
))
}
JsValue::String(ref string) => {
let prototype = context.standard_objects().string_object().prototype();
let object = JsObject::new(Object::with_prototype(
prototype.into(),
ObjectData::string(string.clone()),
));
let object =
JsObject::from_proto_and_data(prototype, ObjectData::string(string.clone()));
// Make sure the correct length is set on our new string object
object.insert_property(
"length",
@ -565,17 +557,17 @@ impl JsValue {
}
JsValue::Symbol(ref symbol) => {
let prototype = context.standard_objects().symbol_object().prototype();
Ok(JsObject::new(Object::with_prototype(
prototype.into(),
Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::symbol(symbol.clone()),
)))
))
}
JsValue::BigInt(ref bigint) => {
let prototype = context.standard_objects().bigint_object().prototype();
Ok(JsObject::new(Object::with_prototype(
prototype.into(),
Ok(JsObject::from_proto_and_data(
prototype,
ObjectData::big_int(bigint.clone()),
)))
))
}
JsValue::Object(jsobject) => Ok(jsobject.clone()),
}

20
boa/src/value/tests.rs

@ -6,13 +6,6 @@ use crate::{check_output, forward, forward_val, Context, TestAction};
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
#[test]
fn is_object() {
let context = Context::new();
let val = JsValue::new_object(&context);
assert!(val.is_object());
}
#[test]
fn string_to_value() {
let s = String::from("Hello");
@ -31,15 +24,12 @@ fn undefined() {
#[test]
fn get_set_field() {
let mut context = Context::new();
let obj = JsValue::new_object(&context);
let obj = &context.construct_object();
// Create string and convert it to a Value
let s = JsValue::new("bar");
obj.set_field("foo", s, false, &mut context).unwrap();
obj.set("foo", s, false, &mut context).unwrap();
assert_eq!(
obj.get_field("foo", &mut context)
.unwrap()
.display()
.to_string(),
obj.get("foo", &mut context).unwrap().display().to_string(),
"\"bar\""
);
}
@ -142,11 +132,11 @@ fn hash_rational() {
#[test]
#[allow(clippy::eq_op)]
fn hash_object() {
let object1 = JsValue::new(Object::default());
let object1 = JsValue::new(JsObject::empty());
assert_eq!(object1, object1);
assert_eq!(object1, object1.clone());
let object2 = JsValue::new(Object::default());
let object2 = JsValue::new(JsObject::empty());
assert_ne!(object1, object2);
assert_eq!(hash_value(&object1), hash_value(&object1.clone()));

33
boa/src/vm/code_block.rs

@ -1,15 +1,14 @@
use crate::{
builtins::function::{
Captures, ClosureFunctionSignature, Function, NativeFunctionSignature, ThisMode,
},
context::StandardObjects,
environment::{
function_environment_record::{BindingStatus, FunctionEnvironmentRecord},
lexical_environment::Environment,
},
gc::{Finalize, Trace},
object::{
function::{
Captures, ClosureFunctionSignature, Function, NativeFunctionSignature, ThisMode,
},
JsObject, Object, PROTOTYPE,
},
object::{internal_methods::get_prototype_from_constructor, JsObject, ObjectData},
property::PropertyDescriptor,
syntax::ast::node::FormalParameter,
vm::Opcode,
@ -311,7 +310,8 @@ impl JsVmFunction {
let function = Function::VmOrdinary { code, environment };
let constructor = JsObject::new(Object::function(function, function_prototype.into()));
let constructor =
JsObject::from_proto_and_data(function_prototype, ObjectData::function(function));
let constructor_property = PropertyDescriptor::builder()
.value(constructor.clone())
@ -515,26 +515,17 @@ impl JsObject {
(function)(this_target, args, captures, context)
}
FunctionBody::Ordinary { code, environment } => {
let this = {
let this: JsValue = {
// If the prototype of the constructor is not an object, then use the default object
// prototype as prototype for the new object
// see <https://tc39.es/ecma262/#sec-ordinarycreatefromconstructor>
// see <https://tc39.es/ecma262/#sec-getprototypefromconstructor>
let proto = this_target.as_object().unwrap().__get__(
&PROTOTYPE.into(),
this_target.clone(),
let prototype = get_prototype_from_constructor(
this_target,
StandardObjects::object_object,
context,
)?;
let proto = if proto.is_object() {
proto
} else {
context
.standard_objects()
.object_object()
.prototype()
.into()
};
JsValue::from(Object::create(proto))
JsObject::from_proto_and_data(prototype, ObjectData::ordinary()).into()
};
let lexical_this_mode = code.this_mode == ThisMode::Lexical;

9
boa/src/vm/mod.rs

@ -155,22 +155,21 @@ impl Context {
self.vm.push(value);
}
Opcode::PushNaN => self.vm.push(JsValue::nan()),
Opcode::PushPositiveInfinity => self.vm.push(JsValue::positive_inifnity()),
Opcode::PushNegativeInfinity => self.vm.push(JsValue::negative_inifnity()),
Opcode::PushPositiveInfinity => self.vm.push(JsValue::positive_infinity()),
Opcode::PushNegativeInfinity => self.vm.push(JsValue::negative_infinity()),
Opcode::PushLiteral => {
let index = self.vm.read::<u32>() as usize;
let value = self.vm.frame().code.literals[index].clone();
self.vm.push(value)
}
Opcode::PushEmptyObject => self.vm.push(JsValue::new_object(self)),
Opcode::PushEmptyObject => self.vm.push(self.construct_object()),
Opcode::PushNewArray => {
let count = self.vm.read::<u32>();
let mut elements = Vec::with_capacity(count as usize);
for _ in 0..count {
elements.push(self.vm.pop());
}
let array = Array::new_array(self);
Array::add_to_array_object(&array, &elements, self)?;
let array = Array::create_array_from_list(elements, self);
self.vm.push(array);
}
Opcode::Add => bin_op!(add),

Loading…
Cancel
Save