Browse Source

`ConstructorBuilder`, `ObjectInitializer`, cache standard objects and fix global object attributes (#722)

- Add BuiltIn trait
- Add ConstructorBuilder
- Add ObjectInitializer
- Cache core standard objects
- More efficient ConstructorBuilder::build()
- Allow to specify which prototype to inherit
- Refactor object property insertion
- Made ClassBuilder use ConstructorBuilder
- Make ConstructorBuilder::build() return GcObject
- Implement Debug for ClassBuilder and ConstructorBuilder
- Make ClassBuilder methods return &mut Self
- Make ObjectBuilder::build() return a GcObject
- Fixed global objects/properies attributes
- Fixed function prototype and attributes
- doc cached standard objects
- Set error object types to inherit from `Error.prototype`
- Added FunctionBuilder
- Add #[inline]
pull/787/head
Halid Odat 4 years ago committed by GitHub
parent
commit
eb1fd280d5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 2
      boa/src/builtins/array/array_iterator.rs
  2. 272
      boa/src/builtins/array/mod.rs
  3. 100
      boa/src/builtins/bigint/mod.rs
  4. 87
      boa/src/builtins/boolean/mod.rs
  5. 70
      boa/src/builtins/console/mod.rs
  6. 419
      boa/src/builtins/date/mod.rs
  7. 76
      boa/src/builtins/error/mod.rs
  8. 81
      boa/src/builtins/error/range.rs
  9. 80
      boa/src/builtins/error/reference.rs
  10. 81
      boa/src/builtins/error/syntax.rs
  11. 81
      boa/src/builtins/error/type.rs
  12. 79
      boa/src/builtins/function/mod.rs
  13. 22
      boa/src/builtins/global_this/mod.rs
  14. 17
      boa/src/builtins/infinity/mod.rs
  15. 31
      boa/src/builtins/iterable/mod.rs
  16. 40
      boa/src/builtins/json/mod.rs
  17. 171
      boa/src/builtins/map/mod.rs
  18. 134
      boa/src/builtins/math/mod.rs
  19. 61
      boa/src/builtins/mod.rs
  20. 17
      boa/src/builtins/nan/mod.rs
  21. 194
      boa/src/builtins/number/mod.rs
  22. 109
      boa/src/builtins/object/mod.rs
  23. 84
      boa/src/builtins/regexp/mod.rs
  24. 188
      boa/src/builtins/string/mod.rs
  25. 198
      boa/src/builtins/symbol/mod.rs
  26. 19
      boa/src/builtins/undefined/mod.rs
  27. 144
      boa/src/class.rs
  28. 145
      boa/src/context.rs
  29. 3
      boa/src/lib.rs
  30. 25
      boa/src/object/internal_methods.rs
  31. 535
      boa/src/object/mod.rs
  32. 4
      boa/src/value/conversions.rs
  33. 90
      boa/src/value/mod.rs
  34. 11
      boa/src/value/rcstring.rs

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

@ -96,7 +96,7 @@ impl ArrayIterator {
} }
ArrayIterationKind::KeyAndValue => { ArrayIterationKind::KeyAndValue => {
let element_value = array_iterator.array.get_field(index); let element_value = array_iterator.array.get_field(index);
let result = Array::make_array( let result = Array::constructor(
&Value::new_object(Some(ctx.global_object())), &Value::new_object(Some(ctx.global_object())),
&[index.into(), element_value], &[index.into(), element_value],
ctx, ctx,

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

@ -13,10 +13,10 @@ pub mod array_iterator;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator}, builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator},
object::{ObjectData, PROTOTYPE}, builtins::BuiltIn,
object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE},
property::{Attribute, Property}, property::{Attribute, Property},
value::{same_value_zero, Value}, value::{same_value_zero, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
@ -27,17 +27,122 @@ use std::cmp::{max, min};
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct Array; pub(crate) struct Array;
impl BuiltIn for Array {
const NAME: &'static str = "Array";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let symbol_iterator = context.well_known_symbols().iterator_symbol();
let values_function = FunctionBuilder::new(context, Self::values)
.name("values")
.length(0)
.callable(true)
.constructable(false)
.build();
let array = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().array_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.property("length", 0, Attribute::all())
.property(
"values",
values_function.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.property(
symbol_iterator,
values_function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
)
.method(Self::concat, "concat", 1)
.method(Self::push, "push", 1)
.method(Self::index_of, "indexOf", 1)
.method(Self::last_index_of, "lastIndexOf", 1)
.method(Self::includes_value, "includes", 1)
.method(Self::map, "map", 1)
.method(Self::fill, "fill", 1)
.method(Self::for_each, "forEach", 1)
.method(Self::filter, "filter", 1)
.method(Self::pop, "pop", 0)
.method(Self::join, "join", 1)
.method(Self::to_string, "toString", 0)
.method(Self::reverse, "reverse", 0)
.method(Self::shift, "shift", 0)
.method(Self::unshift, "unshift", 1)
.method(Self::every, "every", 1)
.method(Self::find, "find", 1)
.method(Self::find_index, "findIndex", 1)
.method(Self::slice, "slice", 2)
.method(Self::some, "some", 2)
.method(Self::reduce, "reduce", 2)
.method(Self::reduce_right, "reduceRight", 2)
.method(Self::keys, "keys", 0)
.method(Self::entries, "entries", 0)
// Static Methods
.static_method(Self::is_array, "isArray", 1)
.build();
(Self::NAME, array.into(), Self::attribute())
}
}
impl Array { impl Array {
/// The name of the object. const LENGTH: usize = 1;
pub(crate) const NAME: &'static str = "Array";
/// The amount of arguments this function object takes. fn constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
pub(crate) const LENGTH: usize = 1; // Set Prototype
let prototype = context.standard_objects().array_object().prototype();
this.as_object_mut()
.expect("this should be an array object")
.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)
this.set_data(ObjectData::Array);
// add our arguments in
let mut length = args.len() as i32;
match args.len() {
1 if args[0].is_integer() => {
length = args[0].as_number().unwrap() as i32;
// TODO: It should not create an array of undefineds, but an empty array ("holy" array in V8) with length `n`.
for n in 0..length {
this.set_field(n, Value::undefined());
}
}
1 if args[0].is_double() => {
return context.throw_range_error("invalid array length");
}
_ => {
for (n, value) in args.iter().enumerate() {
this.set_field(n, value.clone());
}
}
}
// finally create length property
let length = Property::data_descriptor(
length.into(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
this.set_property("length".to_string(), length);
Ok(this.clone())
}
/// Creates a new `Array` instance. /// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Context) -> Result<Value> { pub(crate) fn new_array(context: &Context) -> Result<Value> {
let array = Value::new_object(Some( let array = Value::new_object(Some(
&interpreter &context
.realm() .realm()
.environment .environment
.get_global_object() .get_global_object()
@ -47,14 +152,7 @@ impl Array {
array array
.as_object_mut() .as_object_mut()
.expect("array object") .expect("array object")
.set_prototype_instance( .set_prototype_instance(context.standard_objects().array_object().prototype().into());
interpreter
.realm()
.environment
.get_binding_value("Array")
.expect("Array was not initialized")
.get_field(PROTOTYPE),
);
array.set_field("length", Value::from(0)); array.set_field("length", Value::from(0));
Ok(array) Ok(array)
} }
@ -103,52 +201,6 @@ impl Array {
Ok(array_ptr.clone()) Ok(array_ptr.clone())
} }
/// Create a new array
pub(crate) fn make_array(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype
// Set Prototype
let prototype = ctx.global_object().get_field("Array").get_field(PROTOTYPE);
this.as_object_mut()
.expect("this should be an array object")
.set_prototype_instance(prototype);
// 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::Array);
// add our arguments in
let mut length = args.len() as i32;
match args.len() {
1 if args[0].is_integer() => {
length = args[0].as_number().unwrap() as i32;
// TODO: It should not create an array of undefineds, but an empty array ("holy" array in V8) with length `n`.
for n in 0..length {
this.set_field(n, Value::undefined());
}
}
1 if args[0].is_double() => {
return ctx.throw_range_error("invalid array length");
}
_ => {
for (n, value) in args.iter().enumerate() {
this.set_field(n, value.clone());
}
}
}
// finally create length property
let length = Property::data_descriptor(
length.into(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
this.set_property("length".to_string(), length);
Ok(this.clone())
}
/// `Array.isArray( arg )` /// `Array.isArray( arg )`
/// ///
/// The isArray function takes one argument arg, and returns the Boolean value true /// The isArray function takes one argument arg, and returns the Boolean value true
@ -1103,12 +1155,8 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn values( pub(crate) fn values(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value, ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::Value)
_args: &[Value],
interpreter: &mut Context,
) -> Result<Value> {
ArrayIterator::create_array_iterator(interpreter, this.clone(), ArrayIterationKind::Value)
} }
/// `Array.prototype.keys( )` /// `Array.prototype.keys( )`
@ -1121,8 +1169,8 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn keys(this: &Value, _args: &[Value], interpreter: &mut Context) -> Result<Value> { pub(crate) fn keys(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(interpreter, this.clone(), ArrayIterationKind::Key) ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::Key)
} }
/// `Array.prototype.entries( )` /// `Array.prototype.entries( )`
@ -1135,87 +1183,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn entries( pub(crate) fn entries(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
this: &Value, ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::KeyAndValue)
_args: &[Value],
interpreter: &mut Context,
) -> Result<Value> {
ArrayIterator::create_array_iterator(
interpreter,
this.clone(),
ArrayIterationKind::KeyAndValue,
)
}
/// Initialise the `Array` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let prototype = Value::new_object(Some(global));
let length = Property::default().value(Value::from(0));
prototype.set_property("length", length);
make_builtin_fn(Self::concat, "concat", &prototype, 1, interpreter);
make_builtin_fn(Self::push, "push", &prototype, 1, interpreter);
make_builtin_fn(Self::index_of, "indexOf", &prototype, 1, interpreter);
make_builtin_fn(
Self::last_index_of,
"lastIndexOf",
&prototype,
1,
interpreter,
);
make_builtin_fn(Self::includes_value, "includes", &prototype, 1, interpreter);
make_builtin_fn(Self::map, "map", &prototype, 1, interpreter);
make_builtin_fn(Self::fill, "fill", &prototype, 1, interpreter);
make_builtin_fn(Self::for_each, "forEach", &prototype, 1, interpreter);
make_builtin_fn(Self::filter, "filter", &prototype, 1, interpreter);
make_builtin_fn(Self::pop, "pop", &prototype, 0, interpreter);
make_builtin_fn(Self::join, "join", &prototype, 1, interpreter);
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
make_builtin_fn(Self::reverse, "reverse", &prototype, 0, interpreter);
make_builtin_fn(Self::shift, "shift", &prototype, 0, interpreter);
make_builtin_fn(Self::unshift, "unshift", &prototype, 1, interpreter);
make_builtin_fn(Self::every, "every", &prototype, 1, interpreter);
make_builtin_fn(Self::find, "find", &prototype, 1, interpreter);
make_builtin_fn(Self::find_index, "findIndex", &prototype, 1, interpreter);
make_builtin_fn(Self::slice, "slice", &prototype, 2, interpreter);
make_builtin_fn(Self::some, "some", &prototype, 2, interpreter);
make_builtin_fn(Self::reduce, "reduce", &prototype, 2, interpreter);
make_builtin_fn(
Self::reduce_right,
"reduceRight",
&prototype,
2,
interpreter,
);
make_builtin_fn(Self::values, "values", &prototype, 0, interpreter);
make_builtin_fn(Self::keys, "keys", &prototype, 0, interpreter);
make_builtin_fn(Self::entries, "entries", &prototype, 0, interpreter);
let symbol_iterator = interpreter.well_known_symbols().iterator_symbol();
prototype.set_property(
symbol_iterator,
Property::default().value(prototype.get_field("values")),
);
let array = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_array,
global,
prototype,
true,
true,
);
// Static Methods
make_builtin_fn(Self::is_array, "isArray", &array, 1, interpreter);
(Self::NAME, array)
} }
} }

100
boa/src/builtins/bigint/mod.rs

@ -13,8 +13,9 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt
use crate::{ use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn}, builtins::BuiltIn,
object::ObjectData, object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{RcBigInt, Value}, value::{RcBigInt, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
@ -40,13 +41,57 @@ mod tests;
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] #[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct BigInt(num_bigint::BigInt); pub struct BigInt(num_bigint::BigInt);
impl BigInt { impl BuiltIn for BigInt {
/// The name of the object. const NAME: &'static str = "BigInt";
pub(crate) const NAME: &'static str = "BigInt";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let bigint_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().bigint_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.method(Self::to_string, "toString", 1)
.method(Self::value_of, "valueOf", 0)
.static_method(Self::as_int_n, "asIntN", 2)
.static_method(Self::as_uint_n, "asUintN", 2)
.callable(true)
.constructable(false)
.build();
(Self::NAME, bigint_object.into(), Self::attribute())
}
}
impl BigInt {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// `BigInt()`
///
/// The `BigInt()` constructor is used to create BigInt objects.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-bigint-objects
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_bigint(context)?,
None => RcBigInt::from(Self::from(0)),
};
Ok(Value::from(data))
}
/// The abstract operation thisBigIntValue takes argument value. /// The abstract operation thisBigIntValue takes argument value.
/// ///
/// The phrase “this BigInt value” within the specification of a method refers to the /// The phrase “this BigInt value” within the specification of a method refers to the
@ -78,24 +123,6 @@ impl BigInt {
Err(ctx.construct_type_error("'this' is not a BigInt")) Err(ctx.construct_type_error("'this' is not a BigInt"))
} }
/// `BigInt()`
///
/// The `BigInt()` constructor is used to create BigInt objects.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-bigint-objects
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_bigint(ctx)?,
None => RcBigInt::from(Self::from(0)),
};
Ok(Value::from(data))
}
/// `BigInt.prototype.toString( [radix] )` /// `BigInt.prototype.toString( [radix] )`
/// ///
/// The `toString()` method returns a string representing the specified BigInt object. /// The `toString()` method returns a string representing the specified BigInt object.
@ -194,33 +221,6 @@ impl BigInt {
bits, bits,
)) ))
} }
/// Initialise the `BigInt` object on the global object.
#[inline]
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
make_builtin_fn(Self::to_string, "toString", &prototype, 1, interpreter);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0, interpreter);
let bigint_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_bigint,
global,
prototype,
false,
true,
);
make_builtin_fn(Self::as_int_n, "asIntN", &bigint_object, 2, interpreter);
make_builtin_fn(Self::as_uint_n, "asUintN", &bigint_object, 2, interpreter);
(Self::NAME, bigint_object)
}
} }
impl Finalize for BigInt {} impl Finalize for BigInt {}

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

@ -12,20 +12,58 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{
use crate::{object::ObjectData, BoaProfiler, Context, Result, Value}; builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
property::Attribute,
BoaProfiler, Context, Result, Value,
};
/// Boolean implementation. /// Boolean implementation.
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct Boolean; pub(crate) struct Boolean;
impl Boolean { impl BuiltIn for Boolean {
/// The name of the object. /// The name of the object.
pub(crate) const NAME: &'static str = "Boolean"; const NAME: &'static str = "Boolean";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let boolean_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().boolean_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.method(Self::to_string, "toString", 0)
.method(Self::value_of, "valueOf", 0)
.build();
(Self::NAME, boolean_object.into(), Self::attribute())
}
}
impl Boolean {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// `[[Construct]]` Create a new boolean object
///
/// `[[Call]]` Creates a new boolean primitive
pub(crate) fn constructor(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
// Get the argument, if any
let data = args.get(0).map(|x| x.to_boolean()).unwrap_or(false);
this.set_data(ObjectData::Boolean(data));
Ok(Value::from(data))
}
/// An Utility function used to get the internal [[BooleanData]]. /// An Utility function used to get the internal [[BooleanData]].
/// ///
/// More information: /// More information:
@ -47,21 +85,6 @@ impl Boolean {
Err(ctx.construct_type_error("'this' is not a boolean")) Err(ctx.construct_type_error("'this' is not a boolean"))
} }
/// `[[Construct]]` Create a new boolean object
///
/// `[[Call]]` Creates a new boolean primitive
pub(crate) fn construct_boolean(
this: &Value,
args: &[Value],
_: &mut Context,
) -> Result<Value> {
// Get the argument, if any
let data = args.get(0).map(|x| x.to_boolean()).unwrap_or(false);
this.set_data(ObjectData::Boolean(data));
Ok(Value::from(data))
}
/// The `toString()` method returns a string representing the specified `Boolean` object. /// The `toString()` method returns a string representing the specified `Boolean` object.
/// ///
/// More information: /// More information:
@ -88,30 +111,4 @@ impl Boolean {
pub(crate) fn value_of(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn value_of(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
Ok(Value::from(Self::this_boolean_value(this, ctx)?)) Ok(Value::from(Self::this_boolean_value(this, ctx)?))
} }
/// Initialise the `Boolean` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create Prototype
// https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object
let prototype = Value::new_object(Some(global));
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0, interpreter);
let boolean_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::construct_boolean,
global,
prototype,
true,
true,
);
(Self::NAME, boolean_object)
}
} }

70
boa/src/builtins/console/mod.rs

@ -17,7 +17,9 @@
mod tests; mod tests;
use crate::{ use crate::{
builtins::function::make_builtin_fn, builtins::BuiltIn,
object::ObjectInitializer,
property::Attribute,
value::{display_obj, RcString, Value}, value::{display_obj, RcString, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
@ -136,6 +138,41 @@ pub(crate) struct Console {
groups: Vec<String>, groups: Vec<String>,
} }
impl BuiltIn for Console {
const NAME: &'static str = "console";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let console = ObjectInitializer::new(context)
.function(Self::assert, "assert", 0)
.function(Self::clear, "clear", 0)
.function(Self::debug, "debug", 0)
.function(Self::error, "error", 0)
.function(Self::info, "info", 0)
.function(Self::log, "log", 0)
.function(Self::trace, "trace", 0)
.function(Self::warn, "warn", 0)
.function(Self::error, "exception", 0)
.function(Self::count, "count", 0)
.function(Self::count_reset, "countReset", 0)
.function(Self::group, "group", 0)
.function(Self::group, "groupCollapsed", 0)
.function(Self::group_end, "groupEnd", 0)
.function(Self::time, "time", 0)
.function(Self::time_log, "timeLog", 0)
.function(Self::time_end, "timeEnd", 0)
.function(Self::dir, "dir", 0)
.function(Self::dir, "dirxml", 0)
.build();
(Self::NAME, console.into(), Self::attribute())
}
}
impl Console { impl Console {
/// The name of the object. /// The name of the object.
pub(crate) const NAME: &'static str = "console"; pub(crate) const NAME: &'static str = "console";
@ -495,35 +532,4 @@ impl Console {
Ok(Value::undefined()) Ok(Value::undefined())
} }
/// Initialise the `console` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let console = Value::new_object(Some(global));
make_builtin_fn(Self::assert, "assert", &console, 0, interpreter);
make_builtin_fn(Self::clear, "clear", &console, 0, interpreter);
make_builtin_fn(Self::debug, "debug", &console, 0, interpreter);
make_builtin_fn(Self::error, "error", &console, 0, interpreter);
make_builtin_fn(Self::info, "info", &console, 0, interpreter);
make_builtin_fn(Self::log, "log", &console, 0, interpreter);
make_builtin_fn(Self::trace, "trace", &console, 0, interpreter);
make_builtin_fn(Self::warn, "warn", &console, 0, interpreter);
make_builtin_fn(Self::error, "exception", &console, 0, interpreter);
make_builtin_fn(Self::count, "count", &console, 0, interpreter);
make_builtin_fn(Self::count_reset, "countReset", &console, 0, interpreter);
make_builtin_fn(Self::group, "group", &console, 0, interpreter);
make_builtin_fn(Self::group, "groupCollapsed", &console, 0, interpreter);
make_builtin_fn(Self::group_end, "groupEnd", &console, 0, interpreter);
make_builtin_fn(Self::time, "time", &console, 0, interpreter);
make_builtin_fn(Self::time_log, "timeLog", &console, 0, interpreter);
make_builtin_fn(Self::time_end, "timeEnd", &console, 0, interpreter);
make_builtin_fn(Self::dir, "dir", &console, 0, interpreter);
make_builtin_fn(Self::dir, "dirxml", &console, 0, interpreter);
(Self::NAME, console)
}
} }

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

@ -2,8 +2,9 @@
mod tests; mod tests;
use crate::{ use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn}, builtins::BuiltIn,
object::ObjectData, object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{PreferredType, Value}, value::{PreferredType, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
@ -104,10 +105,89 @@ impl Default for Date {
} }
} }
impl Date { impl BuiltIn for Date {
/// The name of the object. const NAME: &'static str = "Date";
pub(crate) const NAME: &'static str = "Date";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let date_object = ConstructorBuilder::new(context, Self::constructor)
.name(Self::NAME)
.length(Self::LENGTH)
.method(getter_method!(get_date), "getDate", 0)
.method(getter_method!(get_day), "getDay", 0)
.method(getter_method!(get_full_year), "getFullYear", 0)
.method(getter_method!(get_hours), "getHours", 0)
.method(getter_method!(get_milliseconds), "getMilliseconds", 0)
.method(getter_method!(get_minutes), "getMinutes", 0)
.method(getter_method!(get_month), "getMonth", 0)
.method(getter_method!(get_seconds), "getSeconds", 0)
.method(getter_method!(get_time), "getTime", 0)
.method(getter_method!(get_year), "getYear", 0)
.method(
getter_method!(Self::get_timezone_offset),
"getTimezoneOffset",
0,
)
.method(getter_method!(get_utc_date), "getUTCDate", 0)
.method(getter_method!(get_utc_day), "getUTCDay", 0)
.method(getter_method!(get_utc_full_year), "getUTCFullYear", 0)
.method(getter_method!(get_utc_hours), "getUTCHours", 0)
.method(
getter_method!(get_utc_milliseconds),
"getUTCMilliseconds",
0,
)
.method(getter_method!(get_utc_minutes), "getUTCMinutes", 0)
.method(getter_method!(get_utc_month), "getUTCMonth", 0)
.method(getter_method!(get_utc_seconds), "getUTCSeconds", 0)
.method(setter_method!(set_date(0)), "setDate", 1)
.method(setter_method!(set_full_year(0, 1, 2)), "setFullYear", 1)
.method(setter_method!(set_hours(0, 1, 2, 3)), "setHours", 1)
.method(setter_method!(set_milliseconds(0)), "setMilliseconds", 1)
.method(setter_method!(set_minutes(0, 1, 2)), "setMinutes", 1)
.method(setter_method!(set_month(0, 1)), "setMonth", 1)
.method(setter_method!(set_seconds(0, 1)), "setSeconds", 1)
.method(setter_method!(set_year(0, 1, 2)), "setYear", 1)
.method(setter_method!(set_time(0)), "setTime", 1)
.method(setter_method!(set_utc_date(0)), "setUTCDate", 1)
.method(
setter_method!(set_utc_full_year(0, 1, 2)),
"setUTCFullYear",
1,
)
.method(setter_method!(set_utc_hours(0, 1, 2, 3)), "setUTCHours", 1)
.method(
setter_method!(set_utc_milliseconds(0)),
"setUTCMilliseconds",
1,
)
.method(setter_method!(set_utc_minutes(0, 1, 2)), "setUTCMinutes", 1)
.method(setter_method!(set_utc_month(0, 1)), "setUTCMonth", 1)
.method(setter_method!(set_utc_seconds(0, 1)), "setUTCSeconds", 1)
.method(getter_method!(to_date_string), "toDateString", 0)
.method(getter_method!(to_gmt_string), "toGMTString", 0)
.method(getter_method!(to_iso_string), "toISOString", 0)
.method(getter_method!(to_json), "toJSON", 0)
// Locale strings
.method(getter_method!(to_string), "toString", 0)
.method(getter_method!(to_time_string), "toTimeString", 0)
.method(getter_method!(to_utc_string), "toUTCString", 0)
.method(getter_method!(value_of), "valueOf", 0)
.static_method(Self::now, "now", 0)
.static_method(Self::parse, "parse", 1)
.static_method(Self::utc, "UTC", 7)
.build();
(Self::NAME, date_object.into(), Self::attribute())
}
}
impl Date {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 7; pub(crate) const LENGTH: usize = 7;
@ -243,7 +323,7 @@ impl Date {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor /// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
pub(crate) fn make_date(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if this.is_global() { if this.is_global() {
Self::make_date_string() Self::make_date_string()
} else if args.is_empty() { } else if args.is_empty() {
@ -1236,333 +1316,6 @@ impl Date {
Ok(Value::number(f.timestamp_millis() as f64)) Ok(Value::number(f.timestamp_millis() as f64))
}) })
} }
/// Initialise the `Date` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
make_builtin_fn(
getter_method!(get_date),
"getDate",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_day),
"getDay",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_full_year),
"getFullYear",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_hours),
"getHours",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_milliseconds),
"getMilliseconds",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_minutes),
"getMinutes",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_month),
"getMonth",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_seconds),
"getSeconds",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_time),
"getTime",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_year),
"getYear",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(Self::get_timezone_offset),
"getTimezoneOffset",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_date),
"getUTCDate",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_day),
"getUTCDay",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_full_year),
"getUTCFullYear",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_hours),
"getUTCHours",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_milliseconds),
"getUTCMilliseconds",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_minutes),
"getUTCMinutes",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_month),
"getUTCMonth",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(get_utc_seconds),
"getUTCSeconds",
&prototype,
0,
interpreter,
);
make_builtin_fn(
setter_method!(set_date(0)),
"setDate",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_full_year(0, 1, 2)),
"setFullYear",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_hours(0, 1, 2, 3)),
"setHours",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_milliseconds(0)),
"setMilliseconds",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_minutes(0, 1, 2)),
"setMinutes",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_month(0, 1)),
"setMonth",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_seconds(0, 1)),
"setSeconds",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_year(0, 1, 2)),
"setYear",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_time(0)),
"setTime",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_utc_date(0)),
"setUTCDate",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_utc_full_year(0, 1, 2)),
"setUTCFullYear",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_utc_hours(0, 1, 2, 3)),
"setUTCHours",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_utc_milliseconds(0)),
"setUTCMilliseconds",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_utc_minutes(0, 1, 2)),
"setUTCMinutes",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_utc_month(0, 1)),
"setUTCMonth",
&prototype,
1,
interpreter,
);
make_builtin_fn(
setter_method!(set_utc_seconds(0, 1)),
"setUTCSeconds",
&prototype,
1,
interpreter,
);
make_builtin_fn(
getter_method!(to_date_string),
"toDateString",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(to_gmt_string),
"toGMTString",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(to_iso_string),
"toISOString",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(to_json),
"toJSON",
&prototype,
0,
interpreter,
);
// Locale strings
make_builtin_fn(
getter_method!(to_string),
"toString",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(to_time_string),
"toTimeString",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(to_utc_string),
"toUTCString",
&prototype,
0,
interpreter,
);
make_builtin_fn(
getter_method!(value_of),
"valueOf",
&prototype,
0,
interpreter,
);
let date_time_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_date,
global,
prototype,
true,
true,
);
make_builtin_fn(Self::now, "now", &date_time_object, 0, interpreter);
make_builtin_fn(Self::parse, "parse", &date_time_object, 1, interpreter);
make_builtin_fn(Self::utc, "UTC", &date_time_object, 7, interpreter);
(Self::NAME, date_time_object)
}
} }
/// The abstract operation `thisTimeValue` takes argument value. /// The abstract operation `thisTimeValue` takes argument value.

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

@ -11,9 +11,10 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error
use crate::{ use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn}, builtins::BuiltIn,
object::ObjectData, object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler, profiler::BoaProfiler,
property::Attribute,
Context, Result, Value, Context, Result, Value,
}; };
@ -35,15 +36,41 @@ pub(crate) use self::syntax::SyntaxError;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct Error; pub(crate) struct Error;
impl Error { impl BuiltIn for Error {
/// The name of the object. const NAME: &'static str = "Error";
pub(crate) const NAME: &'static str = "Error";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
let error_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().error_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.method(Self::to_string, "toString", 0)
.build();
(Self::NAME, error_object.into(), Self::attribute())
}
}
impl Error {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// `Error( message )`
///
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) { if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?); this.set_field("message", message.to_string(ctx)?);
} }
@ -65,38 +92,9 @@ impl Error {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> { pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let name = this.get_field("name"); let name = this.get_field("name").to_string(context)?;
let message = this.get_field("message"); let message = this.get_field("message").to_string(context)?;
Ok(Value::from(format!( Ok(format!("{}: {}", name, message).into())
"{}: {}",
name.display(),
message.display()
)))
}
/// Initialise the global object with the `Error` object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
prototype.set_field("name", Self::NAME);
prototype.set_field("message", "");
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
let error_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_error,
global,
prototype,
true,
true,
);
(Self::NAME, error_object)
} }
} }

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

@ -10,9 +10,10 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError
use crate::{ use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn}, builtins::BuiltIn,
object::ObjectData, object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler, profiler::BoaProfiler,
property::Attribute,
Context, Result, Value, Context, Result, Value,
}; };
@ -20,15 +21,40 @@ use crate::{
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct RangeError; pub(crate) struct RangeError;
impl RangeError { impl BuiltIn for RangeError {
/// The name of the object. const NAME: &'static str = "RangeError";
pub(crate) const NAME: &'static str = "RangeError";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let error_prototype = context.standard_objects().error_object().prototype();
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
let range_error_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().range_error_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.inherit(error_prototype.into())
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.build();
(Self::NAME, range_error_object.into(), Self::attribute())
}
}
impl RangeError {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) { if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?); this.set_field("message", message.to_string(ctx)?);
} }
@ -38,47 +64,4 @@ impl RangeError {
this.set_data(ObjectData::Error); this.set_data(ObjectData::Error);
Err(this.clone()) Err(this.clone())
} }
/// `Error.prototype.toString()`
///
/// The toString() method returns a string representing the specified Error object.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let name = this.get_field("name").to_string(ctx)?;
let message = this.get_field("message").to_string(ctx)?;
Ok(Value::from(format!("{}: {}", name, message)))
}
/// Initialise the global object with the `RangeError` object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
prototype.set_field("name", Self::NAME);
prototype.set_field("message", "");
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
let range_error_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_error,
global,
prototype,
true,
true,
);
(Self::NAME, range_error_object)
}
} }

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

@ -10,24 +10,50 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError
use crate::{ use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn}, builtins::BuiltIn,
object::ObjectData, object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler, profiler::BoaProfiler,
property::Attribute,
Context, Result, Value, Context, Result, Value,
}; };
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct ReferenceError; pub(crate) struct ReferenceError;
impl ReferenceError { impl BuiltIn for ReferenceError {
/// The name of the object. const NAME: &'static str = "ReferenceError";
pub(crate) const NAME: &'static str = "ReferenceError";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let error_prototype = context.standard_objects().error_object().prototype();
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
let reference_error_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().reference_error_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.inherit(error_prototype.into())
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.build();
(Self::NAME, reference_error_object.into(), Self::attribute())
}
}
impl ReferenceError {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) { if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?); this.set_field("message", message.to_string(ctx)?);
} }
@ -37,46 +63,4 @@ impl ReferenceError {
this.set_data(ObjectData::Error); this.set_data(ObjectData::Error);
Err(this.clone()) Err(this.clone())
} }
/// `Error.prototype.toString()`
///
/// The toString() method returns a string representing the specified Error object.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let name = this.get_field("name").to_string(ctx)?;
let message = this.get_field("message").to_string(ctx)?;
Ok(Value::from(format!("{}: {}", name, message)))
}
/// Initialise the global object with the `ReferenceError` object.
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
prototype.set_field("name", Self::NAME);
prototype.set_field("message", "");
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
let reference_error_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_error,
global,
prototype,
true,
true,
);
(Self::NAME, reference_error_object)
}
} }

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

@ -12,9 +12,10 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError
use crate::{ use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn}, builtins::BuiltIn,
object::ObjectData, object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler, profiler::BoaProfiler,
property::Attribute,
Context, Result, Value, Context, Result, Value,
}; };
@ -22,15 +23,40 @@ use crate::{
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct SyntaxError; pub(crate) struct SyntaxError;
impl SyntaxError { impl BuiltIn for SyntaxError {
/// The name of the object. const NAME: &'static str = "SyntaxError";
pub(crate) const NAME: &'static str = "SyntaxError";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let error_prototype = context.standard_objects().error_object().prototype();
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
let syntax_error_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().syntax_error_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.inherit(error_prototype.into())
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.build();
(Self::NAME, syntax_error_object.into(), Self::attribute())
}
}
impl SyntaxError {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) { if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?); this.set_field("message", message.to_string(ctx)?);
} }
@ -40,47 +66,4 @@ impl SyntaxError {
this.set_data(ObjectData::Error); this.set_data(ObjectData::Error);
Err(this.clone()) Err(this.clone())
} }
/// `Error.prototype.toString()`
///
/// The toString() method returns a string representing the specified Error object.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
let name = this.get_field("name");
let message = this.get_field("message");
// FIXME: This should not use `.display()`
Ok(format!("{}: {}", name.display(), message.display()).into())
}
/// Initialise the global object with the `SyntaxError` object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
prototype.set_field("name", Self::NAME);
prototype.set_field("message", "");
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
let syntax_error_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_error,
global,
prototype,
true,
true,
);
(Self::NAME, syntax_error_object)
}
} }

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

@ -16,8 +16,9 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError
use crate::{ use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn}, builtins::BuiltIn,
object::ObjectData, object::{ConstructorBuilder, ObjectData},
property::Attribute,
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
@ -25,15 +26,40 @@ use crate::{
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct TypeError; pub(crate) struct TypeError;
impl TypeError { impl BuiltIn for TypeError {
/// The name of the object. const NAME: &'static str = "TypeError";
pub(crate) const NAME: &'static str = "TypeError";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let error_prototype = context.standard_objects().error_object().prototype();
let attribute = Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE;
let type_error_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().type_error_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.inherit(error_prototype.into())
.property("name", Self::NAME, attribute)
.property("message", "", attribute)
.build();
(Self::NAME, type_error_object.into(), Self::attribute())
}
}
impl TypeError {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
if let Some(message) = args.get(0) { if let Some(message) = args.get(0) {
this.set_field("message", message.to_string(ctx)?); this.set_field("message", message.to_string(ctx)?);
} }
@ -43,47 +69,4 @@ impl TypeError {
this.set_data(ObjectData::Error); this.set_data(ObjectData::Error);
Err(this.clone()) Err(this.clone())
} }
/// `Error.prototype.toString()`
///
/// The toString() method returns a string representing the specified Error object.
///
/// More information:
/// - [MDN documentation][mdn]
/// - [ECMAScript reference][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
let name = this.get_field("name").to_string(ctx)?;
let message = this.get_field("message").to_string(ctx)?;
Ok(Value::from(format!("{}: {}", name, message)))
}
/// Initialise the global object with the `RangeError` object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
prototype.set_field("name", Self::NAME);
prototype.set_field("message", "");
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
let type_error_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_error,
global,
prototype,
true,
true,
);
(Self::NAME, type_error_object)
}
} }

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

@ -12,9 +12,9 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function
use crate::{ use crate::{
builtins::Array, builtins::{Array, BuiltIn},
environment::lexical_environment::Environment, environment::lexical_environment::Environment,
object::{Object, ObjectData, PROTOTYPE}, object::{ConstructorBuilder, Object, ObjectData, PROTOTYPE},
property::{Attribute, Property}, property::{Attribute, Property},
syntax::ast::node::{FormalParameter, RcStatementList}, syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
@ -188,24 +188,13 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
); );
obj.insert_property(index, prop); obj.insert(index, prop);
index += 1; index += 1;
} }
Value::from(obj) Value::from(obj)
} }
/// Create new function `[[Construct]]`
///
// This gets called when a new Function() is created.
pub fn make_function(this: &Value, _: &[Value], _: &mut Context) -> Result<Value> {
this.set_data(ObjectData::Function(Function::BuiltIn(
BuiltInFunction(|_, _, _| Ok(Value::undefined())),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
)));
Ok(this.clone())
}
/// Creates a new constructor function /// Creates a new constructor function
/// ///
/// This utility function handling linking the new Constructor to the prototype. /// This utility function handling linking the new Constructor to the prototype.
@ -237,25 +226,26 @@ pub fn make_constructor_fn(
length.into(), length.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
); );
constructor.insert_property("length", length); constructor.insert("length", length);
let name = Property::data_descriptor( let name = Property::data_descriptor(
name.into(), name.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
); );
constructor.insert_property("name", name); constructor.insert("name", name);
let constructor = Value::from(constructor); let constructor = Value::from(constructor);
prototype prototype.as_object_mut().unwrap().insert_property(
.as_object_mut() "constructor",
.unwrap() constructor.clone(),
.insert_field("constructor", constructor.clone()); Attribute::all(),
);
constructor constructor
.as_object_mut() .as_object_mut()
.expect("constructor object") .expect("constructor object")
.insert_field(PROTOTYPE, prototype); .insert_property(PROTOTYPE, prototype, Attribute::all());
constructor constructor
} }
@ -297,23 +287,48 @@ pub fn make_builtin_fn<N>(
.get_field("Function") .get_field("Function")
.get_field("prototype"), .get_field("prototype"),
); );
function.insert_field("length", Value::from(length)); function.insert_property("length", length, Attribute::all());
parent parent
.as_object_mut() .as_object_mut()
.unwrap() .unwrap()
.insert_field(name, Value::from(function)); .insert_property(name, function, Attribute::all());
} }
/// Initialise the `Function` object on the global object. #[derive(Debug, Clone, Copy)]
#[inline] pub struct BuiltInFunctionObject;
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("function", "init");
let prototype = Value::new_object(Some(global));
let function_object = impl BuiltInFunctionObject {
make_constructor_fn("Function", 1, make_function, global, prototype, true, true); pub const LENGTH: usize = 1;
("Function", function_object) fn constructor(this: &Value, _args: &[Value], _context: &mut Context) -> Result<Value> {
this.set_data(ObjectData::Function(Function::BuiltIn(
BuiltInFunction(|_, _, _| Ok(Value::undefined())),
FunctionFlags::CALLABLE | FunctionFlags::CONSTRUCTABLE,
)));
Ok(this.clone())
}
}
impl BuiltIn for BuiltInFunctionObject {
const NAME: &'static str = "Function";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event("function", "init");
let function_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().function_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.build();
(Self::NAME, function_object.into(), Self::attribute())
}
} }

22
boa/src/builtins/global_this/mod.rs

@ -10,7 +10,7 @@
//! [spec]: https://tc39.es/ecma262/#sec-globalthis //! [spec]: https://tc39.es/ecma262/#sec-globalthis
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/globalThis
use crate::{BoaProfiler, Context, Value}; use crate::{builtins::BuiltIn, property::Attribute, BoaProfiler, Context, Value};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -18,16 +18,20 @@ mod tests;
/// The JavaScript `globalThis`. /// The JavaScript `globalThis`.
pub(crate) struct GlobalThis; pub(crate) struct GlobalThis;
impl GlobalThis { impl BuiltIn for GlobalThis {
/// The binding name of the property. const NAME: &'static str = "globalThis";
pub(crate) const NAME: &'static str = "globalThis";
/// Initialize the `globalThis` property on the global object. fn attribute() -> Attribute {
#[inline] Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) { }
let global = interpreter.global_object();
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, global.clone()) (
Self::NAME,
context.global_object().clone(),
Self::attribute(),
)
} }
} }

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

@ -12,21 +12,22 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use crate::{BoaProfiler, Context, Value}; use crate::{builtins::BuiltIn, property::Attribute, BoaProfiler, Context, Value};
/// JavaScript global `Infinity` property. /// JavaScript global `Infinity` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Infinity; pub(crate) struct Infinity;
impl Infinity { impl BuiltIn for Infinity {
/// The binding name of the property. const NAME: &'static str = "Infinity";
pub(crate) const NAME: &'static str = "Infinity";
/// Initialize the `Infinity` property on the global object. fn attribute() -> Attribute {
#[inline] Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) { }
fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::from(f64::INFINITY)) (Self::NAME, f64::INFINITY.into(), Self::attribute())
} }
} }

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

@ -1,11 +1,7 @@
use crate::{ use crate::{
builtins::string::string_iterator::StringIterator, builtins::string::string_iterator::StringIterator,
builtins::{ builtins::ArrayIterator,
function::{BuiltInFunction, Function, FunctionFlags}, object::{GcObject, ObjectInitializer},
ArrayIterator,
},
object::GcObject,
object::{Object, PROTOTYPE},
property::Property, property::Property,
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
@ -79,23 +75,18 @@ pub fn get_iterator(ctx: &mut Context, iterable: Value) -> Result<IteratorRecord
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-%iteratorprototype%-object /// [spec]: https://tc39.es/ecma262/#sec-%iteratorprototype%-object
fn create_iterator_prototype(ctx: &mut Context) -> Value { fn create_iterator_prototype(ctx: &mut Context) -> Value {
let global = ctx.global_object();
let _timer = BoaProfiler::global().start_event("Iterator Prototype", "init"); let _timer = BoaProfiler::global().start_event("Iterator Prototype", "init");
let iterator_prototype = Value::new_object(Some(global));
let mut function = Object::function(
Function::BuiltIn(
BuiltInFunction(|v, _, _| Ok(v.clone())),
FunctionFlags::CALLABLE,
),
global.get_field("Function").get_field(PROTOTYPE),
);
function.insert_field("length", Value::from(0));
function.insert_field("name", Value::string("[Symbol.iterator]"));
let symbol_iterator = ctx.well_known_symbols().iterator_symbol(); let symbol_iterator = ctx.well_known_symbols().iterator_symbol();
iterator_prototype.set_field(symbol_iterator, Value::from(function)); let iterator_prototype = ObjectInitializer::new(ctx)
iterator_prototype .function(
|v, _, _| Ok(v.clone()),
(symbol_iterator, "[Symbol.iterator]"),
0,
)
.build();
// TODO: return GcObject
iterator_prototype.into()
} }
#[derive(Debug)] #[derive(Debug)]

40
boa/src/builtins/json/mod.rs

@ -14,8 +14,9 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
use crate::{ use crate::{
builtins::function::make_builtin_fn, builtins::BuiltIn,
property::{Property, PropertyKey}, object::ObjectInitializer,
property::{Attribute, Property, PropertyKey},
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
use serde_json::{self, Value as JSONValue}; use serde_json::{self, Value as JSONValue};
@ -27,10 +28,26 @@ mod tests;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Json; pub(crate) struct Json;
impl Json { impl BuiltIn for Json {
/// The name of the object. const NAME: &'static str = "JSON";
pub(crate) const NAME: &'static str = "JSON";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let json_object = ObjectInitializer::new(context)
.function(Self::parse, "parse", 2)
.function(Self::stringify, "stringify", 3)
.build();
(Self::NAME, json_object.into(), Self::attribute())
}
}
impl Json {
/// `JSON.parse( text[, reviver] )` /// `JSON.parse( text[, reviver] )`
/// ///
/// This `JSON` method parses a JSON string, constructing the JavaScript value or object described by the string. /// This `JSON` method parses a JSON string, constructing the JavaScript value or object described by the string.
@ -175,17 +192,4 @@ impl Json {
Ok(Value::from(object.to_json(ctx)?.to_string())) Ok(Value::from(object.to_json(ctx)?.to_string()))
} }
} }
/// Initialise the `JSON` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let json = Value::new_object(Some(global));
make_builtin_fn(Self::parse, "parse", &json, 2, interpreter);
make_builtin_fn(Self::stringify, "stringify", &json, 3, interpreter);
(Self::NAME, json)
}
} }

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

@ -1,8 +1,8 @@
#![allow(clippy::mutable_key_type)] #![allow(clippy::mutable_key_type)]
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
object::{ObjectData, PROTOTYPE}, builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::{Attribute, Property}, property::{Attribute, Property},
BoaProfiler, Context, Result, Value, BoaProfiler, Context, Result, Value,
}; };
@ -15,11 +15,89 @@ mod tests;
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub(crate) struct Map(OrderedMap<Value, Value>); pub(crate) struct Map(OrderedMap<Value, Value>);
impl Map { impl BuiltIn for Map {
pub(crate) const NAME: &'static str = "Map"; const NAME: &'static str = "Map";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let map_object = ConstructorBuilder::new(context, Self::constructor)
.name(Self::NAME)
.length(Self::LENGTH)
.method(Self::set, "set", 2)
.method(Self::delete, "delete", 1)
.method(Self::get, "get", 1)
.method(Self::clear, "clear", 0)
.method(Self::has, "has", 1)
.method(Self::for_each, "forEach", 1)
.callable(false)
.build();
(Self::NAME, map_object.into(), Self::attribute())
}
}
impl Map {
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new map
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// Set Prototype
let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE);
this.as_object_mut()
.expect("this is map object")
.set_prototype_instance(prototype);
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
// add our arguments in
let data = match args.len() {
0 => OrderedMap::new(),
_ => match &args[0] {
Value::Object(object) => {
let object = object.borrow();
if let Some(map) = object.as_map_ref().cloned() {
map
} else if object.is_array() {
let mut map = OrderedMap::new();
let len = args[0].get_field("length").to_integer(ctx)? as i32;
for i in 0..len {
let val = &args[0].get_field(i.to_string());
let (key, value) = Self::get_key_value(val).ok_or_else(|| {
ctx.construct_type_error(
"iterable for Map should have array-like objects",
)
})?;
map.insert(key, value);
}
map
} else {
return Err(ctx.construct_type_error(
"iterable for Map should have array-like objects",
));
}
}
_ => {
return Err(
ctx.construct_type_error("iterable for Map should have array-like objects")
)
}
},
};
// finally create length property
Self::set_size(this, data.len());
this.set_data(ObjectData::Map(data));
Ok(this.clone())
}
/// Helper function to set the size property. /// Helper function to set the size property.
fn set_size(this: &Value, size: usize) { fn set_size(this: &Value, size: usize) {
let size = Property::data_descriptor( let size = Property::data_descriptor(
@ -221,89 +299,4 @@ impl Map {
} }
None None
} }
/// Create a new map
pub(crate) fn make_map(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype
// Set Prototype
let prototype = ctx.global_object().get_field("Map").get_field(PROTOTYPE);
this.as_object_mut()
.expect("this is array object")
.set_prototype_instance(prototype);
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
// add our arguments in
let data = match args.len() {
0 => OrderedMap::new(),
_ => match &args[0] {
Value::Object(object) => {
let object = object.borrow();
if let Some(map) = object.as_map_ref().cloned() {
map
} else if object.is_array() {
let mut map = OrderedMap::new();
let len = args[0].get_field("length").to_integer(ctx)? as i32;
for i in 0..len {
let val = &args[0].get_field(i.to_string());
let (key, value) = Self::get_key_value(val).ok_or_else(|| {
ctx.construct_type_error(
"iterable for Map should have array-like objects",
)
})?;
map.insert(key, value);
}
map
} else {
return Err(ctx.construct_type_error(
"iterable for Map should have array-like objects",
));
}
}
_ => {
return Err(
ctx.construct_type_error("iterable for Map should have array-like objects")
)
}
},
};
// finally create length property
Self::set_size(this, data.len());
this.set_data(ObjectData::Map(data));
Ok(this.clone())
}
/// Initialise the `Map` object on the global object.
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype
let prototype = Value::new_object(Some(global));
make_builtin_fn(Self::set, "set", &prototype, 2, interpreter);
make_builtin_fn(Self::delete, "delete", &prototype, 1, interpreter);
make_builtin_fn(Self::get, "get", &prototype, 1, interpreter);
make_builtin_fn(Self::clear, "clear", &prototype, 0, interpreter);
make_builtin_fn(Self::has, "has", &prototype, 1, interpreter);
make_builtin_fn(Self::for_each, "forEach", &prototype, 1, interpreter);
let map_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_map,
global,
prototype,
true,
false,
);
(Self::NAME, map_object)
}
} }

134
boa/src/builtins/math/mod.rs

@ -11,7 +11,10 @@
//! [spec]: https://tc39.es/ecma262/#sec-math-object //! [spec]: https://tc39.es/ecma262/#sec-math-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
use crate::{builtins::function::make_builtin_fn, BoaProfiler, Context, Result, Value}; use crate::{
builtins::BuiltIn, object::ObjectInitializer, property::Attribute, BoaProfiler, Context,
Result, Value,
};
use std::f64; use std::f64;
#[cfg(test)] #[cfg(test)]
@ -21,10 +24,68 @@ mod tests;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Math; pub(crate) struct Math;
impl Math { impl BuiltIn for Math {
/// The name of the object. const NAME: &'static str = "Math";
pub(crate) const NAME: &'static str = "Math";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let object = ObjectInitializer::new(context)
.property("E", f64::consts::E, attribute)
.property("LN2", f64::consts::LN_2, attribute)
.property("LN10", f64::consts::LN_10, attribute)
.property("LOG2E", f64::consts::LOG2_E, attribute)
.property("LOG10E", f64::consts::LOG10_E, attribute)
.property("SQRT1_2", 0.5_f64.sqrt(), attribute)
.property("SQRT2", f64::consts::SQRT_2, attribute)
.property("PI", f64::consts::PI, attribute)
.function(Self::abs, "abs", 1)
.function(Self::acos, "acos", 1)
.function(Self::acosh, "acosh", 1)
.function(Self::asin, "asin", 1)
.function(Self::asinh, "asinh", 1)
.function(Self::atan, "atan", 1)
.function(Self::atanh, "atanh", 1)
.function(Self::atan2, "atan2", 2)
.function(Self::cbrt, "cbrt", 1)
.function(Self::ceil, "ceil", 1)
.function(Self::clz32, "clz32", 1)
.function(Self::cos, "cos", 1)
.function(Self::cosh, "cosh", 1)
.function(Self::exp, "exp", 1)
.function(Self::expm1, "expm1", 1)
.function(Self::floor, "floor", 1)
.function(Self::fround, "fround", 1)
.function(Self::hypot, "hypot", 1)
.function(Self::imul, "imul", 1)
.function(Self::log, "log", 1)
.function(Self::log1p, "log1p", 1)
.function(Self::log10, "log10", 1)
.function(Self::log2, "log2", 1)
.function(Self::max, "max", 2)
.function(Self::min, "min", 2)
.function(Self::pow, "pow", 2)
.function(Self::random, "random", 0)
.function(Self::round, "round", 1)
.function(Self::sign, "sign", 1)
.function(Self::sin, "sin", 1)
.function(Self::sinh, "sinh", 1)
.function(Self::sqrt, "sqrt", 1)
.function(Self::tan, "tan", 1)
.function(Self::tanh, "tanh", 1)
.function(Self::trunc, "trunc", 1)
.build();
(Self::NAME, object.into(), Self::attribute())
}
}
impl Math {
/// Get the absolute value of a number. /// Get the absolute value of a number.
/// ///
/// More information: /// More information:
@ -632,69 +693,4 @@ impl Math {
.map_or(f64::NAN, f64::trunc) .map_or(f64::NAN, f64::trunc)
.into()) .into())
} }
/// Create a new `Math` object
pub(crate) fn create(interpreter: &mut Context) -> Value {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("math:create", "init");
let math = Value::new_object(Some(global));
{
let mut properties = math.as_object_mut().unwrap();
properties.insert_field("E", Value::from(f64::consts::E));
properties.insert_field("LN2", Value::from(f64::consts::LN_2));
properties.insert_field("LN10", Value::from(f64::consts::LN_10));
properties.insert_field("LOG2E", Value::from(f64::consts::LOG2_E));
properties.insert_field("LOG10E", Value::from(f64::consts::LOG10_E));
properties.insert_field("SQRT1_2", Value::from(0.5_f64.sqrt()));
properties.insert_field("SQRT2", Value::from(f64::consts::SQRT_2));
properties.insert_field("PI", Value::from(f64::consts::PI));
}
make_builtin_fn(Self::abs, "abs", &math, 1, interpreter);
make_builtin_fn(Self::acos, "acos", &math, 1, interpreter);
make_builtin_fn(Self::acosh, "acosh", &math, 1, interpreter);
make_builtin_fn(Self::asin, "asin", &math, 1, interpreter);
make_builtin_fn(Self::asinh, "asinh", &math, 1, interpreter);
make_builtin_fn(Self::atan, "atan", &math, 1, interpreter);
make_builtin_fn(Self::atanh, "atanh", &math, 1, interpreter);
make_builtin_fn(Self::atan2, "atan2", &math, 2, interpreter);
make_builtin_fn(Self::cbrt, "cbrt", &math, 1, interpreter);
make_builtin_fn(Self::ceil, "ceil", &math, 1, interpreter);
make_builtin_fn(Self::clz32, "clz32", &math, 1, interpreter);
make_builtin_fn(Self::cos, "cos", &math, 1, interpreter);
make_builtin_fn(Self::cosh, "cosh", &math, 1, interpreter);
make_builtin_fn(Self::exp, "exp", &math, 1, interpreter);
make_builtin_fn(Self::expm1, "expm1", &math, 1, interpreter);
make_builtin_fn(Self::floor, "floor", &math, 1, interpreter);
make_builtin_fn(Self::fround, "fround", &math, 1, interpreter);
make_builtin_fn(Self::hypot, "hypot", &math, 1, interpreter);
make_builtin_fn(Self::imul, "imul", &math, 1, interpreter);
make_builtin_fn(Self::log, "log", &math, 1, interpreter);
make_builtin_fn(Self::log1p, "log1p", &math, 1, interpreter);
make_builtin_fn(Self::log10, "log10", &math, 1, interpreter);
make_builtin_fn(Self::log2, "log2", &math, 1, interpreter);
make_builtin_fn(Self::max, "max", &math, 2, interpreter);
make_builtin_fn(Self::min, "min", &math, 2, interpreter);
make_builtin_fn(Self::pow, "pow", &math, 2, interpreter);
make_builtin_fn(Self::random, "random", &math, 0, interpreter);
make_builtin_fn(Self::round, "round", &math, 1, interpreter);
make_builtin_fn(Self::sign, "sign", &math, 1, interpreter);
make_builtin_fn(Self::sin, "sin", &math, 1, interpreter);
make_builtin_fn(Self::sinh, "sinh", &math, 1, interpreter);
make_builtin_fn(Self::sqrt, "sqrt", &math, 1, interpreter);
make_builtin_fn(Self::tan, "tan", &math, 1, interpreter);
make_builtin_fn(Self::tanh, "tanh", &math, 1, interpreter);
make_builtin_fn(Self::trunc, "trunc", &math, 1, interpreter);
math
}
/// Initialise the `Math` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Self::create(interpreter))
}
} }

61
boa/src/builtins/mod.rs

@ -28,6 +28,7 @@ pub(crate) use self::{
console::Console, console::Console,
date::Date, date::Date,
error::{Error, RangeError, ReferenceError, SyntaxError, TypeError}, error::{Error, RangeError, ReferenceError, SyntaxError, TypeError},
function::BuiltInFunctionObject,
global_this::GlobalThis, global_this::GlobalThis,
infinity::Infinity, infinity::Infinity,
json::Json, json::Json,
@ -35,54 +36,64 @@ pub(crate) use self::{
math::Math, math::Math,
nan::NaN, nan::NaN,
number::Number, number::Number,
object::Object, object::Object as BuiltInObjectObject,
regexp::RegExp, regexp::RegExp,
string::String, string::String,
symbol::Symbol, symbol::Symbol,
undefined::Undefined, undefined::Undefined,
}; };
use crate::{Context, Value}; use crate::{
property::{Attribute, Property},
Context, Value,
};
pub(crate) trait BuiltIn {
/// The binding name of the property.
const NAME: &'static str;
fn attribute() -> Attribute;
fn init(context: &mut Context) -> (&'static str, Value, Attribute);
}
/// Initializes builtin objects and functions /// Initializes builtin objects and functions
#[inline] #[inline]
pub fn init(interpreter: &mut Context) { pub fn init(context: &mut Context) {
let globals = [ let globals = [
// The `Function` global must be initialized before other types. // Global properties.
function::init, Undefined::init,
Object::init, Infinity::init,
Symbol::init, NaN::init,
GlobalThis::init,
BuiltInFunctionObject::init,
BuiltInObjectObject::init,
Math::init,
Json::init,
Console::init,
Array::init, Array::init,
BigInt::init, BigInt::init,
Boolean::init, Boolean::init,
Date::init, Date::init,
Json::init,
Map::init, Map::init,
Math::init,
Number::init, Number::init,
RegExp::init,
String::init, String::init,
Console::init, RegExp::init,
// Global error types. Symbol::init,
Error::init, Error::init,
RangeError::init, RangeError::init,
ReferenceError::init, ReferenceError::init,
TypeError::init, TypeError::init,
SyntaxError::init, SyntaxError::init,
// Global properties.
NaN::init,
Infinity::init,
GlobalThis::init,
Undefined::init,
]; ];
let global_object = if let Value::Object(global) = context.global_object() {
global.clone()
} else {
unreachable!("global object should always be an object")
};
for init in &globals { for init in &globals {
let (name, value) = init(interpreter); let (name, value, attribute) = init(context);
let global = interpreter.global_object(); let property = Property::data_descriptor(value, attribute);
match global { global_object.borrow_mut().insert(name, property);
Value::Object(ref global_object) => {
global_object.borrow_mut().insert_field(name, value);
}
_ => unreachable!("expect global object"),
}
} }
} }

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

@ -13,21 +13,22 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use crate::{BoaProfiler, Context, Value}; use crate::{builtins::BuiltIn, property::Attribute, BoaProfiler, Context, Value};
/// JavaScript global `NaN` property. /// JavaScript global `NaN` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct NaN; pub(crate) struct NaN;
impl NaN { impl BuiltIn for NaN {
/// The binding name of the property. const NAME: &'static str = "NaN";
pub(crate) const NAME: &'static str = "NaN";
/// Initialize the `NaN` property on the global object. fn attribute() -> Attribute {
#[inline] Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) { }
fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::from(f64::NAN)) (Self::NAME, f64::NAN.into(), Self::attribute())
} }
} }

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

@ -13,9 +13,11 @@
//! [spec]: https://tc39.es/ecma262/#sec-number-object //! [spec]: https://tc39.es/ecma262/#sec-number-object
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::make_builtin_fn;
use crate::{ use crate::{
object::ObjectData, builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{AbstractRelation, Value}, value::{AbstractRelation, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
@ -40,10 +42,67 @@ const PARSE_INT_MAX_ARG_COUNT: usize = 2;
/// Maximum number of arguments expected to the builtin parseFloat() function. /// Maximum number of arguments expected to the builtin parseFloat() function.
const PARSE_FLOAT_MAX_ARG_COUNT: usize = 1; const PARSE_FLOAT_MAX_ARG_COUNT: usize = 1;
impl Number { impl BuiltIn for Number {
/// The name of the object. const NAME: &'static str = "Number";
pub(crate) const NAME: &'static str = "Number";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let number_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().number_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.static_property("EPSILON", f64::EPSILON, attribute)
.static_property("MAX_SAFE_INTEGER", Self::MAX_SAFE_INTEGER, attribute)
.static_property("MIN_SAFE_INTEGER", Self::MIN_SAFE_INTEGER, attribute)
.static_property("MAX_VALUE", Self::MAX_VALUE, attribute)
.static_property("MIN_VALUE", Self::MIN_VALUE, attribute)
.static_property("NEGATIVE_INFINITY", f64::NEG_INFINITY, attribute)
.static_property("POSITIVE_INFINITY", f64::INFINITY, attribute)
.static_property("NaN", f64::NAN, attribute)
.method(Self::to_exponential, "toExponential", 1)
.method(Self::to_fixed, "toFixed", 1)
.method(Self::to_locale_string, "toLocaleString", 0)
.method(Self::to_precision, "toPrecision", 1)
.method(Self::to_string, "toString", 1)
.method(Self::value_of, "valueOf", 0)
.static_method(Self::number_is_finite, "isFinite", 1)
.static_method(Self::number_is_nan, "isNaN", 1)
.static_method(Self::is_safe_integer, "isSafeInteger", 1)
.static_method(Self::number_is_integer, "isInteger", 1)
.build();
let global = context.global_object().clone();
make_builtin_fn(
Self::parse_int,
"parseInt",
&global,
PARSE_INT_MAX_ARG_COUNT,
context,
);
make_builtin_fn(
Self::parse_float,
"parseFloat",
&global,
PARSE_FLOAT_MAX_ARG_COUNT,
context,
);
make_builtin_fn(Self::global_is_finite, "isFinite", &global, 1, context);
make_builtin_fn(Self::global_is_nan, "isNaN", &global, 1, context);
(Self::NAME, number_object.into(), Self::attribute())
}
}
impl Number {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
@ -93,6 +152,17 @@ impl Number {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_VALUE /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/MIN_VALUE
pub(crate) const MIN_VALUE: f64 = f64::MIN; pub(crate) const MIN_VALUE: f64 = f64::MIN;
/// `Number( value )`
pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_numeric_number(ctx)?,
None => 0.0,
};
this.set_data(ObjectData::Number(data));
Ok(Value::from(data))
}
/// This function returns a `Result` of the number `Value`. /// This function returns a `Result` of the number `Value`.
/// ///
/// If the `Value` is a `Number` primitive of `Number` object the number is returned. /// If the `Value` is a `Number` primitive of `Number` object the number is returned.
@ -126,19 +196,6 @@ impl Number {
} }
} }
/// `[[Construct]]` - Creates a Number instance
///
/// `[[Call]]` - Creates a number primitive
pub(crate) fn make_number(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let data = match args.get(0) {
Some(ref value) => value.to_numeric_number(ctx)?,
None => 0.0,
};
this.set_data(ObjectData::Number(data));
Ok(Value::from(data))
}
/// `Number.prototype.toExponential( [fractionDigits] )` /// `Number.prototype.toExponential( [fractionDigits] )`
/// ///
/// The `toExponential()` method returns a string representing the Number object in exponential notation. /// The `toExponential()` method returns a string representing the Number object in exponential notation.
@ -718,107 +775,6 @@ impl Number {
number.is_finite() && number.abs().floor() == number.abs() number.is_finite() && number.abs().floor() == number.abs()
} }
/// Initialise the `Number` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let prototype = Value::new_object(Some(global));
make_builtin_fn(
Self::to_exponential,
"toExponential",
&prototype,
1,
interpreter,
);
make_builtin_fn(Self::to_fixed, "toFixed", &prototype, 1, interpreter);
make_builtin_fn(
Self::to_locale_string,
"toLocaleString",
&prototype,
0,
interpreter,
);
make_builtin_fn(
Self::to_precision,
"toPrecision",
&prototype,
1,
interpreter,
);
make_builtin_fn(Self::to_string, "toString", &prototype, 1, interpreter);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0, interpreter);
make_builtin_fn(
Self::parse_int,
"parseInt",
global,
PARSE_INT_MAX_ARG_COUNT,
interpreter,
);
make_builtin_fn(
Self::parse_float,
"parseFloat",
global,
PARSE_FLOAT_MAX_ARG_COUNT,
interpreter,
);
make_builtin_fn(Self::global_is_finite, "isFinite", global, 1, interpreter);
make_builtin_fn(Self::global_is_nan, "isNaN", global, 1, interpreter);
let number_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_number,
global,
prototype,
true,
true,
);
make_builtin_fn(
Self::number_is_finite,
"isFinite",
&number_object,
1,
interpreter,
);
make_builtin_fn(Self::number_is_nan, "isNaN", &number_object, 1, interpreter);
make_builtin_fn(
Self::is_safe_integer,
"isSafeInteger",
&number_object,
1,
interpreter,
);
make_builtin_fn(
Self::number_is_integer,
"isInteger",
&number_object,
1,
interpreter,
);
// Constants from:
// https://tc39.es/ecma262/#sec-properties-of-the-number-constructor
{
let mut properties = number_object.as_object_mut().expect("'Number' object");
properties.insert_field("EPSILON", Value::from(f64::EPSILON));
properties.insert_field("MAX_SAFE_INTEGER", Value::from(Self::MAX_SAFE_INTEGER));
properties.insert_field("MIN_SAFE_INTEGER", Value::from(Self::MIN_SAFE_INTEGER));
properties.insert_field("MAX_VALUE", Value::from(Self::MAX_VALUE));
properties.insert_field("MIN_VALUE", Value::from(Self::MIN_VALUE));
properties.insert_field("NEGATIVE_INFINITY", Value::from(f64::NEG_INFINITY));
properties.insert_field("POSITIVE_INFINITY", Value::from(f64::INFINITY));
properties.insert_field("NaN", Value::from(f64::NAN));
}
(Self::NAME, number_object)
}
/// The abstract operation Number::equal takes arguments /// The abstract operation Number::equal takes arguments
/// x (a Number) and y (a Number). It performs the following steps when called: /// x (a Number) and y (a Number). It performs the following steps when called:
/// ///

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

@ -14,9 +14,9 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object
use crate::{ use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn}, builtins::BuiltIn,
object::{Object as BuiltinObject, ObjectData}, object::{ConstructorBuilder, Object as BuiltinObject, ObjectData},
property::Property, property::{Attribute, Property},
value::{same_value, Value}, value::{same_value, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
@ -28,15 +28,48 @@ mod tests;
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub struct Object; pub struct Object;
impl BuiltIn for Object {
const NAME: &'static str = "Object";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().object_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.inherit(Value::null())
.method(Self::has_own_property, "hasOwnProperty", 0)
.method(Self::property_is_enumerable, "propertyIsEnumerable", 0)
.method(Self::to_string, "toString", 0)
.static_method(Self::create, "create", 2)
.static_method(Self::set_prototype_of, "setPrototypeOf", 2)
.static_method(Self::get_prototype_of, "getPrototypeOf", 1)
.static_method(Self::define_property, "defineProperty", 3)
.static_method(Self::is, "is", 2)
.build();
(Self::NAME, object.into(), Self::attribute())
}
}
impl Object { impl Object {
/// Create a new object. const LENGTH: usize = 1;
pub fn make_object(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if let Some(arg) = args.get(0) { if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() { if !arg.is_null_or_undefined() {
return Ok(arg.to_object(ctx)?.into()); return Ok(arg.to_object(context)?.into());
} }
} }
let global = ctx.global_object(); let global = context.global_object();
Ok(Value::new_object(Some(global))) Ok(Value::new_object(Some(global)))
} }
@ -192,66 +225,4 @@ impl Object {
Value::from(own_prop.enumerable_or(false)) Value::from(own_prop.enumerable_or(false))
})) }))
} }
/// Initialise the `Object` object on the global object.
#[inline]
pub fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_object();
let _timer = BoaProfiler::global().start_event("object", "init");
let prototype = Value::new_object(None);
make_builtin_fn(
Self::has_own_property,
"hasOwnProperty",
&prototype,
0,
interpreter,
);
make_builtin_fn(
Self::property_is_enumerable,
"propertyIsEnumerable",
&prototype,
0,
interpreter,
);
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
let object = make_constructor_fn(
"Object",
1,
Self::make_object,
global,
prototype,
true,
true,
);
// static methods of the builtin Object
make_builtin_fn(Self::create, "create", &object, 2, interpreter);
make_builtin_fn(
Self::set_prototype_of,
"setPrototypeOf",
&object,
2,
interpreter,
);
make_builtin_fn(
Self::get_prototype_of,
"getPrototypeOf",
&object,
1,
interpreter,
);
make_builtin_fn(
Self::define_property,
"defineProperty",
&object,
3,
interpreter,
);
make_builtin_fn(Self::is, "is", &object, 2, interpreter);
("Object", object)
}
} }

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

@ -9,16 +9,15 @@
//! [spec]: https://tc39.es/ecma262/#sec-regexp-constructor //! [spec]: https://tc39.es/ecma262/#sec-regexp-constructor
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
use regex::Regex;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
object::ObjectData, builtins::BuiltIn,
property::Property, gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, ObjectData},
property::{Attribute, Property},
value::{RcString, Value}, value::{RcString, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
use gc::{unsafe_empty_trace, Finalize, Trace}; use regex::Regex;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -58,7 +57,36 @@ pub struct RegExp {
} }
unsafe impl Trace for RegExp { unsafe impl Trace for RegExp {
unsafe_empty_trace!(); empty_trace!();
}
impl BuiltIn for RegExp {
const NAME: &'static str = "RegExp";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let regexp_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().regexp_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.property("lastIndex", 0, Attribute::all())
.method(Self::test, "test", 1)
.method(Self::exec, "exec", 1)
.method(Self::to_string, "toString", 0)
.build();
// TODO: add them RegExp accessor properties
(Self::NAME, regexp_object.into(), Self::attribute())
}
} }
impl RegExp { impl RegExp {
@ -69,7 +97,7 @@ impl RegExp {
pub(crate) const LENGTH: usize = 2; pub(crate) const LENGTH: usize = 2;
/// Create a new `RegExp` /// Create a new `RegExp`
pub(crate) fn make_regexp(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], _: &mut Context) -> Result<Value> {
let arg = args.get(0).ok_or_else(Value::undefined)?; let arg = args.get(0).ok_or_else(Value::undefined)?;
let mut regex_body = String::new(); let mut regex_body = String::new();
let mut regex_flags = String::new(); let mut regex_flags = String::new();
@ -474,44 +502,4 @@ impl RegExp {
Ok(result) Ok(result)
} }
/// Initialise the `RegExp` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let global = interpreter.global_object();
// Create prototype
let prototype = Value::new_object(Some(global));
prototype
.as_object_mut()
.unwrap()
.insert_field("lastIndex", Value::from(0));
make_builtin_fn(Self::test, "test", &prototype, 1, interpreter);
make_builtin_fn(Self::exec, "exec", &prototype, 1, interpreter);
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
// TODO: make them accessor properties, not methods.
// make_builtin_fn(Self::get_dot_all, "dotAll", &prototype, 0);
// make_builtin_fn(Self::get_flags, "flags", &prototype, 0);
// make_builtin_fn(Self::get_global, "global", &prototype, 0);
// make_builtin_fn(Self::get_ignore_case, "ignoreCase", &prototype, 0);
// make_builtin_fn(Self::get_multiline, "multiline", &prototype, 0);
// make_builtin_fn(Self::get_source, "source", &prototype, 0);
// make_builtin_fn(Self::get_sticky, "sticky", &prototype, 0);
// make_builtin_fn(Self::get_unicode, "unicode", &prototype, 0);
let regexp = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_regexp,
global,
prototype,
true,
true,
);
(Self::NAME, regexp)
}
} }

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

@ -13,14 +13,10 @@ pub mod string_iterator;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::builtins::function::{BuiltInFunction, Function, FunctionFlags};
use crate::builtins::string::string_iterator::StringIterator;
use crate::object::PROTOTYPE;
use crate::{ use crate::{
builtins::RegExp, builtins::{string::string_iterator::StringIterator, BuiltIn, RegExp},
object::{Object, ObjectData}, object::{ConstructorBuilder, Object, ObjectData},
property::Property, property::Attribute,
value::{RcString, Value}, value::{RcString, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
@ -65,40 +61,73 @@ fn is_trailing_surrogate(value: u16) -> bool {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(crate) struct String; pub(crate) struct String;
impl String { impl BuiltIn for String {
/// The name of the object. const NAME: &'static str = "String";
pub(crate) const NAME: &'static str = "String";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let symbol_iterator = context.well_known_symbols().iterator_symbol();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let string_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().string_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.property("length", 0, attribute)
.method(Self::char_at, "charAt", 1)
.method(Self::char_code_at, "charCodeAt", 1)
.method(Self::to_string, "toString", 0)
.method(Self::concat, "concat", 1)
.method(Self::repeat, "repeat", 1)
.method(Self::slice, "slice", 2)
.method(Self::starts_with, "startsWith", 1)
.method(Self::ends_with, "endsWith", 1)
.method(Self::includes, "includes", 1)
.method(Self::index_of, "indexOf", 1)
.method(Self::last_index_of, "lastIndexOf", 1)
.method(Self::r#match, "match", 1)
.method(Self::pad_end, "padEnd", 1)
.method(Self::pad_start, "padStart", 1)
.method(Self::trim, "trim", 0)
.method(Self::trim_start, "trimStart", 0)
.method(Self::trim_end, "trimEnd", 0)
.method(Self::to_lowercase, "toLowerCase", 0)
.method(Self::to_uppercase, "toUpperCase", 0)
.method(Self::substring, "substring", 2)
.method(Self::substr, "substr", 2)
.method(Self::value_of, "valueOf", 0)
.method(Self::match_all, "matchAll", 1)
.method(Self::replace, "replace", 2)
.method(Self::iterator, (symbol_iterator, "[Symbol.iterator]"), 0)
.build();
(Self::NAME, string_object.into(), Self::attribute())
}
}
impl String {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// JavaScript strings must be between `0` and less than positive `Infinity` and cannot be a negative number. /// JavaScript strings must be between `0` and less than positive `Infinity` and cannot be a negative number.
/// The range of allowed values can be described like this: `[0, +∞)`. /// The range of allowed values can be described like this: `[0, +∞)`.
/// ///
/// The resulting string can also not be larger than the maximum string size, /// The resulting string can also not be larger than the maximum string size,
/// which can differ in JavaScript engines. In Boa it is `2^32 - 1` /// which can differ in JavaScript engines. In Boa it is `2^32 - 1`
pub(crate) const MAX_STRING_LENGTH: f64 = u32::MAX as f64; pub(crate) const MAX_STRING_LENGTH: f64 = u32::MAX as f64;
fn this_string_value(this: &Value, ctx: &mut Context) -> Result<RcString> { /// `String( value )`
match this {
Value::String(ref string) => return Ok(string.clone()),
Value::Object(ref object) => {
let object = object.borrow();
if let Some(string) = object.as_string() {
return Ok(string);
}
}
_ => {}
}
Err(ctx.construct_type_error("'this' is not a string"))
}
/// [[Construct]] - Creates a new instance `this`
/// ///
/// [[Call]] - Returns a new native `string`
/// <https://tc39.es/ecma262/#sec-string-constructor-string-value> /// <https://tc39.es/ecma262/#sec-string-constructor-string-value>
pub(crate) fn make_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
// This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe // This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe
// to its Javascript Identifier (global constructor method name) // to its Javascript Identifier (global constructor method name)
let string = match args.get(0) { let string = match args.get(0) {
@ -115,6 +144,21 @@ impl String {
Ok(Value::from(string)) Ok(Value::from(string))
} }
fn this_string_value(this: &Value, ctx: &mut Context) -> Result<RcString> {
match this {
Value::String(ref string) => return Ok(string.clone()),
Value::Object(ref object) => {
let object = object.borrow();
if let Some(string) = object.as_string() {
return Ok(string);
}
}
_ => {}
}
Err(ctx.construct_type_error("'this' is not a string"))
}
/// Get the string value to a primitive string /// Get the string value to a primitive string
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[inline] #[inline]
@ -739,7 +783,7 @@ impl String {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
pub(crate) fn r#match(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn r#match(this: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let re = RegExp::make_regexp(&Value::from(Object::default()), &[args[0].clone()], ctx)?; let re = RegExp::constructor(&Value::from(Object::default()), &[args[0].clone()], ctx)?;
RegExp::r#match(&re, this.to_string(ctx)?, ctx) RegExp::r#match(&re, this.to_string(ctx)?, ctx)
} }
@ -1092,13 +1136,13 @@ impl String {
let re: Value = match args.get(0) { let re: Value = match args.get(0) {
Some(arg) => { Some(arg) => {
if arg.is_null() { if arg.is_null() {
RegExp::make_regexp( RegExp::constructor(
&Value::from(Object::default()), &Value::from(Object::default()),
&[Value::from(arg.to_string(ctx)?), Value::from("g")], &[Value::from(arg.to_string(ctx)?), Value::from("g")],
ctx, ctx,
) )
} else if arg.is_undefined() { } else if arg.is_undefined() {
RegExp::make_regexp( RegExp::constructor(
&Value::from(Object::default()), &Value::from(Object::default()),
&[Value::undefined(), Value::from("g")], &[Value::undefined(), Value::from("g")],
ctx, ctx,
@ -1107,7 +1151,7 @@ impl String {
Ok(arg.clone()) Ok(arg.clone())
} }
} }
None => RegExp::make_regexp( None => RegExp::constructor(
&Value::from(Object::default()), &Value::from(Object::default()),
&[Value::from(""), Value::from("g")], &[Value::from(""), Value::from("g")],
ctx, ctx,
@ -1120,82 +1164,4 @@ impl String {
pub(crate) fn iterator(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn iterator(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
StringIterator::create_string_iterator(ctx, this.clone()) StringIterator::create_string_iterator(ctx, this.clone())
} }
/// Initialise the `String` object on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create `String` `prototype`
let global = interpreter.global_object();
let prototype = Value::new_object(Some(global));
let length = Property::default().value(Value::from(0));
prototype.set_property("length", length);
make_builtin_fn(Self::char_at, "charAt", &prototype, 1, interpreter);
make_builtin_fn(Self::char_code_at, "charCodeAt", &prototype, 1, interpreter);
make_builtin_fn(Self::to_string, "toString", &prototype, 0, interpreter);
make_builtin_fn(Self::concat, "concat", &prototype, 1, interpreter);
make_builtin_fn(Self::repeat, "repeat", &prototype, 1, interpreter);
make_builtin_fn(Self::slice, "slice", &prototype, 2, interpreter);
make_builtin_fn(Self::starts_with, "startsWith", &prototype, 1, interpreter);
make_builtin_fn(Self::ends_with, "endsWith", &prototype, 1, interpreter);
make_builtin_fn(Self::includes, "includes", &prototype, 1, interpreter);
make_builtin_fn(Self::index_of, "indexOf", &prototype, 1, interpreter);
make_builtin_fn(
Self::last_index_of,
"lastIndexOf",
&prototype,
1,
interpreter,
);
make_builtin_fn(Self::r#match, "match", &prototype, 1, interpreter);
make_builtin_fn(Self::pad_end, "padEnd", &prototype, 1, interpreter);
make_builtin_fn(Self::pad_start, "padStart", &prototype, 1, interpreter);
make_builtin_fn(Self::trim, "trim", &prototype, 0, interpreter);
make_builtin_fn(Self::trim_start, "trimStart", &prototype, 0, interpreter);
make_builtin_fn(Self::trim_end, "trimEnd", &prototype, 0, interpreter);
make_builtin_fn(
Self::to_lowercase,
"toLowerCase",
&prototype,
0,
interpreter,
);
make_builtin_fn(
Self::to_uppercase,
"toUpperCase",
&prototype,
0,
interpreter,
);
make_builtin_fn(Self::substring, "substring", &prototype, 2, interpreter);
make_builtin_fn(Self::substr, "substr", &prototype, 2, interpreter);
make_builtin_fn(Self::value_of, "valueOf", &prototype, 0, interpreter);
make_builtin_fn(Self::match_all, "matchAll", &prototype, 1, interpreter);
make_builtin_fn(Self::replace, "replace", &prototype, 2, interpreter);
let symbol_iterator = interpreter.well_known_symbols().iterator_symbol();
let mut function = Object::function(
Function::BuiltIn(BuiltInFunction(Self::iterator), FunctionFlags::CALLABLE),
global.get_field("Function").get_field(PROTOTYPE),
);
function.insert_field("length", Value::from(0));
function.insert_field("name", Value::string("[Symbol.iterator]"));
prototype.set_field(symbol_iterator, Value::from(function));
let string_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::make_string,
global,
prototype,
true,
true,
);
(Self::NAME, string_object)
}
} }

198
boa/src/builtins/symbol/mod.rs

@ -18,13 +18,14 @@
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
property::{Attribute, Property}, builtins::BuiltIn,
gc::{Finalize, Trace},
object::ConstructorBuilder,
property::Attribute,
value::{RcString, RcSymbol, Value}, value::{RcString, RcSymbol, Value},
BoaProfiler, Context, Result, BoaProfiler, Context, Result,
}; };
use gc::{Finalize, Trace};
/// A structure that contains the JavaScript well known symbols. /// A structure that contains the JavaScript well known symbols.
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
@ -232,10 +233,64 @@ impl Symbol {
} }
} }
impl Symbol { impl BuiltIn for Symbol {
/// The name of the object. const NAME: &'static str = "Symbol";
pub(crate) const NAME: &'static str = "Symbol";
fn attribute() -> Attribute {
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE
}
fn init(context: &mut Context) -> (&'static str, Value, Attribute) {
// https://tc39.es/ecma262/#sec-well-known-symbols
let well_known_symbols = context.well_known_symbols();
let symbol_async_iterator = well_known_symbols.async_iterator_symbol();
let symbol_has_instance = well_known_symbols.has_instance_symbol();
let symbol_is_concat_spreadable = well_known_symbols.is_concat_spreadable_symbol();
let symbol_iterator = well_known_symbols.iterator_symbol();
let symbol_match = well_known_symbols.match_symbol();
let symbol_match_all = well_known_symbols.match_all_symbol();
let symbol_replace = well_known_symbols.replace_symbol();
let symbol_search = well_known_symbols.search_symbol();
let symbol_species = well_known_symbols.species_symbol();
let symbol_split = well_known_symbols.split_symbol();
let symbol_to_primitive = well_known_symbols.to_primitive_symbol();
let symbol_to_string_tag = well_known_symbols.to_string_tag_symbol();
let symbol_unscopables = well_known_symbols.unscopables_symbol();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
let symbol_object = ConstructorBuilder::with_standard_object(
context,
Self::constructor,
context.standard_objects().symbol_object().clone(),
)
.name(Self::NAME)
.length(Self::LENGTH)
.static_property("asyncIterator", symbol_async_iterator, attribute)
.static_property("hasInstance", symbol_has_instance, attribute)
.static_property("isConcatSpreadable", symbol_is_concat_spreadable, attribute)
.static_property("iterator", symbol_iterator, attribute)
.static_property("match", symbol_match, attribute)
.static_property("matchAll", symbol_match_all, attribute)
.static_property("replace", symbol_replace, attribute)
.static_property("search", symbol_search, attribute)
.static_property("species", symbol_species, attribute)
.static_property("split", symbol_split, attribute)
.static_property("toPrimitive", symbol_to_primitive, attribute)
.static_property("toStringTag", symbol_to_string_tag, attribute)
.static_property("unscopables", symbol_unscopables, attribute)
.method(Self::to_string, "toString", 0)
.callable(true)
.constructable(false)
.build();
(Self::NAME, symbol_object.into(), Self::attribute())
}
}
impl Symbol {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 0; pub(crate) const LENGTH: usize = 0;
@ -249,21 +304,6 @@ impl Symbol {
self.hash self.hash
} }
fn this_symbol_value(value: &Value, ctx: &mut Context) -> Result<RcSymbol> {
match value {
Value::Symbol(ref symbol) => return Ok(symbol.clone()),
Value::Object(ref object) => {
let object = object.borrow();
if let Some(symbol) = object.as_symbol() {
return Ok(symbol);
}
}
_ => {}
}
Err(ctx.construct_type_error("'this' is not a Symbol"))
}
/// The `Symbol()` constructor returns a value of type symbol. /// The `Symbol()` constructor returns a value of type symbol.
/// ///
/// It is incomplete as a constructor because it does not support /// It is incomplete as a constructor because it does not support
@ -275,7 +315,7 @@ impl Symbol {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-description /// [spec]: https://tc39.es/ecma262/#sec-symbol-description
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol
pub(crate) fn call(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> { pub(crate) fn constructor(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
let description = match args.get(0) { let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?), Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?),
_ => None, _ => None,
@ -284,6 +324,21 @@ impl Symbol {
Ok(ctx.construct_symbol(description).into()) Ok(ctx.construct_symbol(description).into())
} }
fn this_symbol_value(value: &Value, ctx: &mut Context) -> Result<RcSymbol> {
match value {
Value::Symbol(ref symbol) => return Ok(symbol.clone()),
Value::Object(ref object) => {
let object = object.borrow();
if let Some(symbol) = object.as_symbol() {
return Ok(symbol);
}
}
_ => {}
}
Err(ctx.construct_type_error("'this' is not a Symbol"))
}
/// `Symbol.prototype.toString()` /// `Symbol.prototype.toString()`
/// ///
/// This method returns a string representing the specified `Symbol` object. /// This method returns a string representing the specified `Symbol` object.
@ -300,103 +355,4 @@ impl Symbol {
let description = symbol.description().unwrap_or(""); let description = symbol.description().unwrap_or("");
Ok(Value::from(format!("Symbol({})", description))) Ok(Value::from(format!("Symbol({})", description)))
} }
/// Initialise the `Symbol` object on the global object.
#[inline]
pub fn init(context: &mut Context) -> (&'static str, Value) {
// Define the Well-Known Symbols
// https://tc39.es/ecma262/#sec-well-known-symbols
let well_known_symbols = context.well_known_symbols();
let symbol_async_iterator = well_known_symbols.async_iterator_symbol();
let symbol_has_instance = well_known_symbols.has_instance_symbol();
let symbol_is_concat_spreadable = well_known_symbols.is_concat_spreadable_symbol();
let symbol_iterator = well_known_symbols.iterator_symbol();
let symbol_match = well_known_symbols.match_symbol();
let symbol_match_all = well_known_symbols.match_all_symbol();
let symbol_replace = well_known_symbols.replace_symbol();
let symbol_search = well_known_symbols.search_symbol();
let symbol_species = well_known_symbols.species_symbol();
let symbol_split = well_known_symbols.split_symbol();
let symbol_to_primitive = well_known_symbols.to_primitive_symbol();
let symbol_to_string_tag = well_known_symbols.to_string_tag_symbol();
let symbol_unscopables = well_known_symbols.unscopables_symbol();
let global = context.global_object();
let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
// Create prototype object
let prototype = Value::new_object(Some(global));
make_builtin_fn(Self::to_string, "toString", &prototype, 0, context);
let symbol_object = make_constructor_fn(
Self::NAME,
Self::LENGTH,
Self::call,
global,
prototype,
false,
true,
);
{
let mut symbol_object = symbol_object.as_object_mut().unwrap();
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
symbol_object.insert_property(
"asyncIterator",
Property::data_descriptor(symbol_async_iterator.into(), attribute),
);
symbol_object.insert_property(
"hasInstance",
Property::data_descriptor(symbol_has_instance.into(), attribute),
);
symbol_object.insert_property(
"isConcatSpreadable",
Property::data_descriptor(symbol_is_concat_spreadable.into(), attribute),
);
symbol_object.insert_property(
"iterator",
Property::data_descriptor(symbol_iterator.into(), attribute),
);
symbol_object.insert_property(
"match",
Property::data_descriptor(symbol_match.into(), attribute),
);
symbol_object.insert_property(
"matchAll",
Property::data_descriptor(symbol_match_all.into(), attribute),
);
symbol_object.insert_property(
"replace",
Property::data_descriptor(symbol_replace.into(), attribute),
);
symbol_object.insert_property(
"search",
Property::data_descriptor(symbol_search.into(), attribute),
);
symbol_object.insert_property(
"species",
Property::data_descriptor(symbol_species.into(), attribute),
);
symbol_object.insert_property(
"split",
Property::data_descriptor(symbol_split.into(), attribute),
);
symbol_object.insert_property(
"toPrimitive",
Property::data_descriptor(symbol_to_primitive.into(), attribute),
);
symbol_object.insert_property(
"toStringTag",
Property::data_descriptor(symbol_to_string_tag.into(), attribute),
);
symbol_object.insert_property(
"unscopables",
Property::data_descriptor(symbol_unscopables.into(), attribute),
);
}
(Self::NAME, symbol_object)
}
} }

19
boa/src/builtins/undefined/mod.rs

@ -9,24 +9,25 @@
//! [spec]: https://tc39.es/ecma262/#sec-undefined //! [spec]: https://tc39.es/ecma262/#sec-undefined
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/undefined
use crate::{builtins::BuiltIn, property::Attribute, BoaProfiler, Context, Value};
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
use crate::{BoaProfiler, Context, Value};
/// JavaScript global `undefined` property. /// JavaScript global `undefined` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Undefined; pub(crate) struct Undefined;
impl Undefined { impl BuiltIn for Undefined {
/// The binding name of the property. const NAME: &'static str = "undefined";
pub(crate) const NAME: &'static str = "undefined";
fn attribute() -> Attribute {
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
}
/// Initialize the `undefined` property on the global object. fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
#[inline]
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) {
let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let _timer = BoaProfiler::global().start_event(Self::NAME, "init");
(Self::NAME, Value::undefined()) (Self::NAME, Value::undefined(), Self::attribute())
} }
} }

144
boa/src/class.rs

@ -61,12 +61,11 @@
//! [class-trait]: ./trait.Class.html //! [class-trait]: ./trait.Class.html
use crate::{ use crate::{
builtins::function::{BuiltInFunction, Function, FunctionFlags, NativeFunction}, builtins::function::NativeFunction,
object::{GcObject, NativeObject, Object, ObjectData, PROTOTYPE}, object::{ConstructorBuilder, GcObject, NativeObject, ObjectData},
property::{Attribute, Property, PropertyKey}, property::{Attribute, PropertyKey},
Context, Result, Value, Context, Result, Value,
}; };
use std::fmt::Debug;
/// Native class. /// Native class.
pub trait Class: NativeObject + Sized { pub trait Class: NativeObject + Sized {
@ -108,157 +107,84 @@ impl<T: Class> ClassConstructor for T {
/// Class builder which allows adding methods and static methods to the class. /// Class builder which allows adding methods and static methods to the class.
#[derive(Debug)] #[derive(Debug)]
pub struct ClassBuilder<'context> { pub struct ClassBuilder<'context> {
/// The current context. builder: ConstructorBuilder<'context>,
context: &'context mut Context,
/// The constructor object.
object: GcObject,
/// The prototype of the object.
prototype: GcObject,
} }
impl<'context> ClassBuilder<'context> { impl<'context> ClassBuilder<'context> {
#[inline]
pub(crate) fn new<T>(context: &'context mut Context) -> Self pub(crate) fn new<T>(context: &'context mut Context) -> Self
where where
T: ClassConstructor, T: ClassConstructor,
{ {
let global = context.global_object(); let mut builder = ConstructorBuilder::new(context, T::raw_constructor);
builder.name(T::NAME);
let prototype = { builder.length(T::LENGTH);
let object_prototype = global.get_field("Object").get_field(PROTOTYPE); Self { builder }
let object = Object::create(object_prototype);
GcObject::new(object)
};
// Create the native function
let function = Function::BuiltIn(
BuiltInFunction(T::raw_constructor),
FunctionFlags::CONSTRUCTABLE,
);
// Get reference to Function.prototype
// Create the function object and point its instance prototype to Function.prototype
let mut constructor =
Object::function(function, global.get_field("Function").get_field(PROTOTYPE));
let length = Property::data_descriptor(
T::LENGTH.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert_property("length", length);
let name = Property::data_descriptor(
T::NAME.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert_property("name", name);
let constructor = GcObject::new(constructor);
prototype
.borrow_mut()
.insert_field("constructor", constructor.clone().into());
constructor
.borrow_mut()
.insert_field(PROTOTYPE, prototype.clone().into());
Self {
context,
object: constructor,
prototype,
}
} }
pub(crate) fn build(self) -> GcObject { #[inline]
self.object pub(crate) fn build(mut self) -> GcObject {
self.builder.build()
} }
/// Add a method to the class. /// Add a method to the class.
/// ///
/// It is added to `prototype`. /// It is added to `prototype`.
pub fn method<N>(&mut self, name: N, length: usize, function: NativeFunction) #[inline]
pub fn method<N>(&mut self, name: N, length: usize, function: NativeFunction) -> &mut Self
where where
N: Into<String>, N: AsRef<str>,
{ {
let name = name.into(); self.builder.method(function, name.as_ref(), length);
let mut function = Object::function( self
Function::BuiltIn(function.into(), FunctionFlags::CALLABLE),
self.context
.global_object()
.get_field("Function")
.get_field("prototype"),
);
function.insert_field("length", Value::from(length));
function.insert_field("name", Value::from(name.as_str()));
self.prototype
.borrow_mut()
.insert_field(name, Value::from(function));
} }
/// Add a static method to the class. /// Add a static method to the class.
/// ///
/// It is added to class object itself. /// It is added to class object itself.
pub fn static_method<N>(&mut self, name: N, length: usize, function: NativeFunction) #[inline]
pub fn static_method<N>(
&mut self,
name: N,
length: usize,
function: NativeFunction,
) -> &mut Self
where where
N: Into<String>, N: AsRef<str>,
{ {
let name = name.into(); self.builder.static_method(function, name.as_ref(), length);
let mut function = Object::function( self
Function::BuiltIn(function.into(), FunctionFlags::CALLABLE),
self.context
.global_object()
.get_field("Function")
.get_field("prototype"),
);
function.insert_field("length", Value::from(length));
function.insert_field("name", Value::from(name.as_str()));
self.object
.borrow_mut()
.insert_field(name, Value::from(function));
} }
/// Add a property to the class, with the specified attribute. /// Add a property to the class, with the specified attribute.
/// ///
/// It is added to `prototype`. /// It is added to `prototype`.
#[inline] #[inline]
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where where
K: Into<PropertyKey>, K: Into<PropertyKey>,
V: Into<Value>, V: Into<Value>,
{ {
// We bitwise or (`|`) with `Attribute::default()` (`READONLY | NON_ENUMERABLE | PERMANENT`) self.builder.property(key, value, attribute);
// so we dont get an empty attribute. self
let property = Property::data_descriptor(value.into(), attribute | Attribute::default());
self.prototype
.borrow_mut()
.insert_property(key.into(), property);
} }
/// Add a static property to the class, with the specified attribute. /// Add a static property to the class, with the specified attribute.
/// ///
/// It is added to class object itself. /// It is added to class object itself.
#[inline] #[inline]
pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where where
K: Into<PropertyKey>, K: Into<PropertyKey>,
V: Into<Value>, V: Into<Value>,
{ {
// We bitwise or (`|`) with `Attribute::default()` (`READONLY | NON_ENUMERABLE | PERMANENT`) self.builder.static_property(key, value, attribute);
// so we dont get an empty attribute. self
let property = Property::data_descriptor(value.into(), attribute | Attribute::default());
self.object
.borrow_mut()
.insert_property(key.into(), property);
} }
/// Return the current context. /// Return the current context.
#[inline]
pub fn context(&mut self) -> &'_ mut Context { pub fn context(&mut self) -> &'_ mut Context {
self.context self.builder.context()
} }
} }

145
boa/src/context.rs

@ -28,11 +28,136 @@ use crate::{
}; };
use std::result::Result as StdResult; use std::result::Result as StdResult;
/// Store a builtin constructor (such as `Object`) and its corresponding prototype.
#[derive(Debug, Clone)]
pub struct StandardConstructor {
pub(crate) constructor: GcObject,
pub(crate) prototype: GcObject,
}
impl Default for StandardConstructor {
fn default() -> Self {
Self {
constructor: GcObject::new(Object::default()),
prototype: GcObject::new(Object::default()),
}
}
}
impl StandardConstructor {
/// Return the constructor object.
///
/// This is the same as `Object`, `Array`, etc.
#[inline]
pub fn constructor(&self) -> GcObject {
self.constructor.clone()
}
/// Return the prototype of the constructor object.
///
/// This is the same as `Object.prototype`, `Array.prototype`, etc
#[inline]
pub fn prototype(&self) -> GcObject {
self.prototype.clone()
}
}
/// Cached core standard objects.
#[derive(Debug, Clone, Default)]
pub struct StandardObjects {
object: StandardConstructor,
function: StandardConstructor,
array: StandardConstructor,
bigint: StandardConstructor,
number: StandardConstructor,
boolean: StandardConstructor,
string: StandardConstructor,
regexp: StandardConstructor,
symbol: StandardConstructor,
error: StandardConstructor,
type_error: StandardConstructor,
referece_error: StandardConstructor,
range_error: StandardConstructor,
syntax_error: StandardConstructor,
}
impl StandardObjects {
#[inline]
pub fn object_object(&self) -> &StandardConstructor {
&self.object
}
#[inline]
pub fn function_object(&self) -> &StandardConstructor {
&self.function
}
#[inline]
pub fn array_object(&self) -> &StandardConstructor {
&self.array
}
#[inline]
pub fn bigint_object(&self) -> &StandardConstructor {
&self.bigint
}
#[inline]
pub fn number_object(&self) -> &StandardConstructor {
&self.number
}
#[inline]
pub fn boolean_object(&self) -> &StandardConstructor {
&self.boolean
}
#[inline]
pub fn string_object(&self) -> &StandardConstructor {
&self.string
}
#[inline]
pub fn regexp_object(&self) -> &StandardConstructor {
&self.regexp
}
#[inline]
pub fn symbol_object(&self) -> &StandardConstructor {
&self.symbol
}
#[inline]
pub fn error_object(&self) -> &StandardConstructor {
&self.error
}
#[inline]
pub fn reference_error_object(&self) -> &StandardConstructor {
&self.referece_error
}
#[inline]
pub fn type_error_object(&self) -> &StandardConstructor {
&self.type_error
}
#[inline]
pub fn range_error_object(&self) -> &StandardConstructor {
&self.range_error
}
#[inline]
pub fn syntax_error_object(&self) -> &StandardConstructor {
&self.syntax_error
}
}
/// Javascript context. It is the primary way to interact with the runtime. /// Javascript context. It is the primary way to interact with the runtime.
/// ///
/// For each `Context` instance a new instance of runtime is created. /// `Context`s constructed in a thread share the same runtime, therefore it
/// It means that it is safe to use different contexts in different threads, /// is possible to share objects from one context to another context, but they
/// but each `Context` instance must be used only from a single thread. /// have to be in the same thread.
#[derive(Debug)] #[derive(Debug)]
pub struct Context { pub struct Context {
/// realm holds both the global object and the environment /// realm holds both the global object and the environment
@ -52,7 +177,11 @@ pub struct Context {
/// Cached well known symbols /// Cached well known symbols
well_known_symbols: WellKnownSymbols, well_known_symbols: WellKnownSymbols,
/// Cached iterator prototypes.
iterator_prototypes: IteratorPrototypes, iterator_prototypes: IteratorPrototypes,
/// Cached standard objects and their prototypes
standard_objects: StandardObjects,
} }
impl Default for Context { impl Default for Context {
@ -67,6 +196,7 @@ impl Default for Context {
console: Console::default(), console: Console::default(),
well_known_symbols, well_known_symbols,
iterator_prototypes: IteratorPrototypes::default(), iterator_prototypes: IteratorPrototypes::default(),
standard_objects: Default::default(),
}; };
// Add new builtIns to Context Realm // Add new builtIns to Context Realm
@ -468,7 +598,7 @@ impl Context {
self.global_object() self.global_object()
.as_object_mut() .as_object_mut()
.unwrap() .unwrap()
.insert_property(T::NAME, property); .insert(T::NAME, property);
Ok(()) Ok(())
} }
@ -522,8 +652,15 @@ impl Context {
&self.well_known_symbols &self.well_known_symbols
} }
/// Return the cached iterator prototypes.
#[inline] #[inline]
pub fn iterator_prototypes(&self) -> &IteratorPrototypes { pub fn iterator_prototypes(&self) -> &IteratorPrototypes {
&self.iterator_prototypes &self.iterator_prototypes
} }
/// Return the core standard objects.
#[inline]
pub fn standard_objects(&self) -> &StandardObjects {
&self.standard_objects
}
} }

3
boa/src/lib.rs

@ -46,13 +46,14 @@ pub mod realm;
pub mod syntax; pub mod syntax;
pub mod value; pub mod value;
mod context; pub mod context;
use std::result::Result as StdResult; use std::result::Result as StdResult;
pub(crate) use crate::{exec::Executable, profiler::BoaProfiler}; pub(crate) use crate::{exec::Executable, profiler::BoaProfiler};
// Export things to root level // Export things to root level
#[doc(inline)]
pub use crate::{context::Context, value::Value}; pub use crate::{context::Context, value::Value};
use crate::syntax::{ use crate::syntax::{

25
boa/src/object/internal_methods.rs

@ -168,7 +168,7 @@ impl Object {
return false; return false;
} }
self.insert_property(key, desc); self.insert(key, desc);
return true; return true;
} }
// If every field is absent we don't need to set anything // If every field is absent we don't need to set anything
@ -207,7 +207,7 @@ impl Object {
current.set = None; current.set = None;
} }
self.insert_property(key, current); self.insert(key, current);
return true; return true;
// 7 // 7
} else if current.is_data_descriptor() && desc.is_data_descriptor() { } else if current.is_data_descriptor() && desc.is_data_descriptor() {
@ -247,7 +247,7 @@ impl Object {
return true; return true;
} }
// 9 // 9
self.insert_property(key, desc); self.insert(key, desc);
true true
} }
@ -336,7 +336,7 @@ impl Object {
/// Helper function for property insertion. /// Helper function for property insertion.
#[inline] #[inline]
pub(crate) fn insert_property<K>(&mut self, key: K, property: Property) -> Option<Property> pub(crate) fn insert<K>(&mut self, key: K, property: Property) -> Option<Property>
where where
K: Into<PropertyKey>, K: Into<PropertyKey>,
{ {
@ -366,15 +366,24 @@ impl Object {
/// If a field was already in the object with the same name that a `Some` is returned /// If a field was already in the object with the same name that a `Some` is returned
/// with that field, otherwise None is retuned. /// with that field, otherwise None is retuned.
#[inline] #[inline]
pub(crate) fn insert_field<K>(&mut self, key: K, value: Value) -> Option<Property> pub(crate) fn insert_property<K, V>(
&mut self,
key: K,
value: V,
attribute: Attribute,
) -> Option<Property>
where where
K: Into<PropertyKey>, K: Into<PropertyKey>,
V: Into<Value>,
{ {
self.insert_property( self.insert(
key.into(), key.into(),
Property::data_descriptor( Property::data_descriptor(
value, value.into(),
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, attribute
| Attribute::HAS_WRITABLE
| Attribute::HAS_ENUMERABLE
| Attribute::HAS_CONFIGURABLE,
), ),
) )
} }

535
boa/src/object/mod.rs

@ -2,17 +2,24 @@
use crate::{ use crate::{
builtins::{ builtins::{
array::array_iterator::ArrayIterator, function::Function, map::ordered_map::OrderedMap, array::array_iterator::ArrayIterator,
string::string_iterator::StringIterator, BigInt, Date, RegExp, function::{BuiltInFunction, Function, FunctionFlags, NativeFunction},
map::ordered_map::OrderedMap,
string::string_iterator::StringIterator,
BigInt, Date, RegExp,
}, },
property::{Property, PropertyKey}, context::StandardConstructor,
gc::{Finalize, Trace},
property::{Attribute, Property, PropertyKey},
value::{RcBigInt, RcString, RcSymbol, Value}, value::{RcBigInt, RcString, RcSymbol, Value},
BoaProfiler, BoaProfiler, Context,
}; };
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::fmt::{Debug, Display, Error, Formatter}; use std::{
use std::{any::Any, result::Result as StdResult}; any::Any,
fmt::{self, Debug, Display},
ops::{Deref, DerefMut},
};
mod gcobject; mod gcobject;
mod internal_methods; mod internal_methods;
@ -36,10 +43,12 @@ pub trait NativeObject: Debug + Any + Trace {
} }
impl<T: Any + Debug + Trace> NativeObject for T { impl<T: Any + Debug + Trace> NativeObject for T {
#[inline]
fn as_any(&self) -> &dyn Any { fn as_any(&self) -> &dyn Any {
self as &dyn Any self as &dyn Any
} }
#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any { fn as_mut_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any self as &mut dyn Any
} }
@ -83,7 +92,7 @@ pub enum ObjectData {
} }
impl Display for ObjectData { impl Display for ObjectData {
fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), Error> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!( write!(
f, f,
"{}", "{}",
@ -131,6 +140,7 @@ impl Object {
} }
/// Return a new ObjectData struct, with `kind` set to Ordinary /// Return a new ObjectData struct, with `kind` set to Ordinary
#[inline]
pub fn function(function: Function, prototype: Value) -> Self { pub fn function(function: Function, prototype: Value) -> Self {
let _timer = BoaProfiler::global().start_event("Object::Function", "object"); let _timer = BoaProfiler::global().start_event("Object::Function", "object");
@ -151,6 +161,7 @@ impl Object {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-objectcreate /// [spec]: https://tc39.es/ecma262/#sec-objectcreate
// TODO: proto should be a &Value here // TODO: proto should be a &Value here
#[inline]
pub fn create(proto: Value) -> Self { pub fn create(proto: Value) -> Self {
let mut obj = Self::default(); let mut obj = Self::default();
obj.prototype = proto; obj.prototype = proto;
@ -158,6 +169,7 @@ impl Object {
} }
/// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument. /// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
#[inline]
pub fn boolean(value: bool) -> Self { pub fn boolean(value: bool) -> Self {
Self { Self {
data: ObjectData::Boolean(value), data: ObjectData::Boolean(value),
@ -170,6 +182,7 @@ impl Object {
} }
/// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument. /// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
#[inline]
pub fn number(value: f64) -> Self { pub fn number(value: f64) -> Self {
Self { Self {
data: ObjectData::Number(value), data: ObjectData::Number(value),
@ -182,6 +195,7 @@ impl Object {
} }
/// Return a new `String` object whose `[[StringData]]` internal slot is set to argument. /// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
#[inline]
pub fn string<S>(value: S) -> Self pub fn string<S>(value: S) -> Self
where where
S: Into<RcString>, S: Into<RcString>,
@ -197,6 +211,7 @@ impl Object {
} }
/// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument. /// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
#[inline]
pub fn bigint(value: RcBigInt) -> Self { pub fn bigint(value: RcBigInt) -> Self {
Self { Self {
data: ObjectData::BigInt(value), data: ObjectData::BigInt(value),
@ -209,6 +224,7 @@ impl Object {
} }
/// Create a new native object of type `T`. /// Create a new native object of type `T`.
#[inline]
pub fn native_object<T>(value: T) -> Self pub fn native_object<T>(value: T) -> Self
where where
T: NativeObject, T: NativeObject,
@ -429,10 +445,13 @@ impl Object {
matches!(self.data, ObjectData::Ordinary) matches!(self.data, ObjectData::Ordinary)
} }
#[inline]
pub fn prototype_instance(&self) -> &Value { pub fn prototype_instance(&self) -> &Value {
&self.prototype &self.prototype
} }
#[track_caller]
#[inline]
pub fn set_prototype_instance(&mut self, prototype: Value) { pub fn set_prototype_instance(&mut self, prototype: Value) {
assert!(prototype.is_null() || prototype.is_object()); assert!(prototype.is_null() || prototype.is_object());
self.prototype = prototype self.prototype = prototype
@ -448,16 +467,17 @@ impl Object {
} }
/// Returns `true` if it holds an Rust type that implements `NativeObject`. /// Returns `true` if it holds an Rust type that implements `NativeObject`.
#[inline]
pub fn is_native_object(&self) -> bool { pub fn is_native_object(&self) -> bool {
matches!(self.data, ObjectData::NativeObject(_)) matches!(self.data, ObjectData::NativeObject(_))
} }
/// Reeturn `true` if it is a native object and the native type is `T`. /// Reeturn `true` if it is a native object and the native type is `T`.
#[inline]
pub fn is<T>(&self) -> bool pub fn is<T>(&self) -> bool
where where
T: NativeObject, T: NativeObject,
{ {
use std::ops::Deref;
match self.data { match self.data {
ObjectData::NativeObject(ref object) => object.deref().as_any().is::<T>(), ObjectData::NativeObject(ref object) => object.deref().as_any().is::<T>(),
_ => false, _ => false,
@ -466,11 +486,11 @@ impl Object {
/// Downcast a reference to the object, /// Downcast a reference to the object,
/// if the object is type native object type `T`. /// if the object is type native object type `T`.
#[inline]
pub fn downcast_ref<T>(&self) -> Option<&T> pub fn downcast_ref<T>(&self) -> Option<&T>
where where
T: NativeObject, T: NativeObject,
{ {
use std::ops::Deref;
match self.data { match self.data {
ObjectData::NativeObject(ref object) => object.deref().as_any().downcast_ref::<T>(), ObjectData::NativeObject(ref object) => object.deref().as_any().downcast_ref::<T>(),
_ => None, _ => None,
@ -479,11 +499,11 @@ impl Object {
/// Downcast a mutable reference to the object, /// Downcast a mutable reference to the object,
/// if the object is type native object type `T`. /// if the object is type native object type `T`.
#[inline]
pub fn downcast_mut<T>(&mut self) -> Option<&mut T> pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where where
T: NativeObject, T: NativeObject,
{ {
use std::ops::DerefMut;
match self.data { match self.data {
ObjectData::NativeObject(ref mut object) => { ObjectData::NativeObject(ref mut object) => {
object.deref_mut().as_mut_any().downcast_mut::<T>() object.deref_mut().as_mut_any().downcast_mut::<T>()
@ -492,3 +512,496 @@ impl Object {
} }
} }
} }
/// The functions binding.
///
/// Specifies what is the name of the function object (`name` property),
/// and the binding name of the function object which can be different
/// from the function name.
///
/// The only way to construct this is with the `From` trait.
///
/// There are two implementations:
/// - From a single type `T` which implements `Into<FunctionBinding>` which sets the binding
/// name and the function name to the same value
/// - From a tuple `(B: Into<PropertyKey>, N: AsRef<str>)` the `B` is the binding name
/// and the `N` is the function name.
#[derive(Debug, Clone)]
pub struct FunctionBinding {
binding: PropertyKey,
name: RcString,
}
impl From<&str> for FunctionBinding {
#[inline]
fn from(name: &str) -> Self {
let name: RcString = name.into();
Self {
binding: name.clone().into(),
name,
}
}
}
impl From<String> for FunctionBinding {
#[inline]
fn from(name: String) -> Self {
let name: RcString = name.into();
Self {
binding: name.clone().into(),
name,
}
}
}
impl From<RcString> for FunctionBinding {
#[inline]
fn from(name: RcString) -> 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(),
}
}
}
/// Builder for creating native function objects
#[derive(Debug)]
pub struct FunctionBuilder<'context> {
context: &'context mut Context,
function: BuiltInFunction,
name: Option<String>,
length: usize,
callable: bool,
constructable: bool,
}
impl<'context> FunctionBuilder<'context> {
/// Create a new `FunctionBuilder`
#[inline]
pub fn new(context: &'context mut Context, function: NativeFunction) -> Self {
Self {
context,
function: function.into(),
name: None,
length: 0,
callable: true,
constructable: false,
}
}
/// Specify the name property of object function object.
///
/// The default is `""` (empty string).
#[inline]
pub fn name<N>(&mut self, name: N) -> &mut Self
where
N: AsRef<str>,
{
self.name = Some(name.as_ref().into());
self
}
/// Specify the length property of object function object.
///
/// How many arguments this function takes.
///
/// The default is `0`.
#[inline]
pub fn length(&mut self, length: usize) -> &mut Self {
self.length = length;
self
}
/// Specify the whether the object function object can be called.
///
/// The default is `true`.
#[inline]
pub fn callable(&mut self, yes: bool) -> &mut Self {
self.callable = yes;
self
}
/// Specify the whether the object function object can be called with `new` keyword.
///
/// The default is `false`.
#[inline]
pub fn constructable(&mut self, yes: bool) -> &mut Self {
self.constructable = yes;
self
}
/// Build the function object.
#[inline]
pub fn build(&mut self) -> GcObject {
let mut function = Object::function(
Function::BuiltIn(
self.function,
FunctionFlags::from_parameters(self.callable, self.constructable),
),
self.context
.standard_objects()
.function_object()
.prototype()
.into(),
);
let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT;
if let Some(name) = self.name.take() {
function.insert_property("name", name, attribute);
} else {
function.insert_property("name", "", attribute);
}
function.insert_property("length", self.length, attribute);
GcObject::new(function)
}
}
/// Builder for creating objects with properties.
///
/// # Examples
///
/// ```
/// # use boa::{Context, Value, object::ObjectInitializer, property::Attribute};
/// let mut context = Context::new();
/// let object = ObjectInitializer::new(&mut context)
/// .property("hello", "world", Attribute::all())
/// .property(1, 1, Attribute::all())
/// .function(|_, _, _| Ok(Value::undefined()), "func", 0)
/// .build();
/// ```
///
/// The equivalent in JavaScript would be:
/// ```text
/// let object = {
/// hello: "world",
/// "1": 1,
/// func: function() {}
/// }
/// ```
#[derive(Debug)]
pub struct ObjectInitializer<'context> {
context: &'context mut Context,
object: GcObject,
}
impl<'context> ObjectInitializer<'context> {
/// Create a new `ObjectBuilder`.
#[inline]
pub fn new(context: &'context mut Context) -> Self {
let object = context.construct_object();
Self { context, object }
}
/// Add a function to the object.
#[inline]
pub fn function<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionBuilder::new(self.context, function)
.name(binding.name)
.length(length)
.callable(true)
.constructable(false)
.build();
self.object.borrow_mut().insert_property(
binding.binding,
function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
);
self
}
/// Add a property to the object.
#[inline]
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<Value>,
{
let property = Property::data_descriptor(value.into(), attribute);
self.object.borrow_mut().insert(key, property);
self
}
/// Build the object.
#[inline]
pub fn build(&mut self) -> GcObject {
self.object.clone()
}
}
/// Builder for creating constructors objects, like `Array`.
pub struct ConstructorBuilder<'context> {
context: &'context mut Context,
constrcutor_function: NativeFunction,
constructor_object: GcObject,
prototype: GcObject,
name: Option<String>,
length: usize,
callable: bool,
constructable: bool,
inherit: Option<Value>,
}
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.constructor_object)
.field("prototype", &self.prototype)
.field("inherit", &self.inherit)
.field("callable", &self.callable)
.field("constructable", &self.constructable)
.finish()
}
}
impl<'context> ConstructorBuilder<'context> {
/// Create a new `ConstructorBuilder`.
#[inline]
pub fn new(context: &'context mut Context, constructor: NativeFunction) -> Self {
Self {
context,
constrcutor_function: constructor,
constructor_object: GcObject::new(Object::default()),
prototype: GcObject::new(Object::default()),
length: 0,
name: None,
callable: true,
constructable: true,
inherit: None,
}
}
#[inline]
pub(crate) fn with_standard_object(
context: &'context mut Context,
constructor: NativeFunction,
object: StandardConstructor,
) -> Self {
Self {
context,
constrcutor_function: constructor,
constructor_object: object.constructor,
prototype: object.prototype,
length: 0,
name: None,
callable: true,
constructable: true,
inherit: None,
}
}
/// Add new method to the constructors prototype.
#[inline]
pub fn method<B>(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionBuilder::new(self.context, function)
.name(binding.name)
.length(length)
.callable(true)
.constructable(false)
.build();
self.prototype.borrow_mut().insert_property(
binding.binding,
function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
);
self
}
/// Add new static method to the constructors object itself.
#[inline]
pub fn static_method<B>(
&mut self,
function: NativeFunction,
binding: B,
length: usize,
) -> &mut Self
where
B: Into<FunctionBinding>,
{
let binding = binding.into();
let function = FunctionBuilder::new(self.context, function)
.name(binding.name)
.length(length)
.callable(true)
.constructable(false)
.build();
self.constructor_object.borrow_mut().insert_property(
binding.binding,
function,
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
);
self
}
/// Add new property to the constructors prototype.
#[inline]
pub fn property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<Value>,
{
let property = Property::data_descriptor(value.into(), attribute);
self.prototype.borrow_mut().insert(key, property);
self
}
/// Add new static property to the constructors object itself.
#[inline]
pub fn static_property<K, V>(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self
where
K: Into<PropertyKey>,
V: Into<Value>,
{
let property = Property::data_descriptor(value.into(), attribute);
self.constructor_object.borrow_mut().insert(key, property);
self
}
/// Specify how many arguments the constructor function takes.
///
/// Default is `0`.
#[inline]
pub fn length(&mut self, length: usize) -> &mut Self {
self.length = length;
self
}
/// Specify the name of the constructor function.
///
/// Default is `"[object]"`
#[inline]
pub fn name<N>(&mut self, name: N) -> &mut Self
where
N: AsRef<str>,
{
self.name = Some(name.as_ref().into());
self
}
/// Specify whether the constructor function can be called.
///
/// Default is `true`
#[inline]
pub fn callable(&mut self, callable: bool) -> &mut Self {
self.callable = callable;
self
}
/// Specify whether the constructor function can be called with `new` keyword.
///
/// Default is `true`
#[inline]
pub fn constructable(&mut self, constructable: bool) -> &mut Self {
self.constructable = constructable;
self
}
/// Specify the prototype this constructor object inherits from.
///
/// Default is `Object.prototype`
#[inline]
pub fn inherit(&mut self, prototype: Value) -> &mut Self {
assert!(prototype.is_object() || prototype.is_null());
self.inherit = Some(prototype);
self
}
/// Return the current context.
#[inline]
pub fn context(&mut self) -> &'_ mut Context {
self.context
}
/// Build the constructor function object.
pub fn build(&mut self) -> GcObject {
// Create the native function
let function = Function::BuiltIn(
self.constrcutor_function.into(),
FunctionFlags::from_parameters(self.callable, self.constructable),
);
let length = Property::data_descriptor(
self.length.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
let name = Property::data_descriptor(
self.name
.take()
.unwrap_or_else(|| String::from("[object]"))
.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
{
let mut constructor = self.constructor_object.borrow_mut();
constructor.data = ObjectData::Function(function);
constructor.insert("length", length);
constructor.insert("name", name);
constructor.set_prototype_instance(
self.context
.standard_objects()
.function_object()
.prototype()
.into(),
);
constructor.insert_property(
PROTOTYPE,
self.prototype.clone(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
}
{
let mut prototype = self.prototype.borrow_mut();
prototype.insert_property(
"constructor",
self.constructor_object.clone(),
Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE,
);
if let Some(proto) = self.inherit.take() {
prototype.set_prototype_instance(proto);
} else {
prototype.set_prototype_instance(
self.context
.standard_objects()
.object_object()
.prototype()
.into(),
);
}
}
self.constructor_object.clone()
}
}

4
boa/src/value/conversions.rs

@ -127,7 +127,7 @@ where
fn from(value: &[T]) -> Self { fn from(value: &[T]) -> Self {
let mut array = Object::default(); let mut array = Object::default();
for (i, item) in value.iter().enumerate() { for (i, item) in value.iter().enumerate() {
array.insert_property(i, Property::default().value(item.clone().into())); array.insert(i, Property::default().value(item.clone().into()));
} }
Self::from(array) Self::from(array)
} }
@ -140,7 +140,7 @@ where
fn from(value: Vec<T>) -> Self { fn from(value: Vec<T>) -> Self {
let mut array = Object::default(); let mut array = Object::default();
for (i, item) in value.into_iter().enumerate() { for (i, item) in value.into_iter().enumerate() {
array.insert_property(i, Property::default().value(item.into())); array.insert(i, Property::default().value(item.into()));
} }
Value::from(array) Value::from(array)
} }

90
boa/src/value/mod.rs

@ -165,7 +165,7 @@ impl Value {
} }
/// Convert from a JSON value to a JS value /// Convert from a JSON value to a JS value
pub fn from_json(json: JSONValue, interpreter: &mut Context) -> Self { pub fn from_json(json: JSONValue, context: &mut Context) -> Self {
match json { match json {
JSONValue::Number(v) => { JSONValue::Number(v) => {
if let Some(Ok(integer_32)) = v.as_i64().map(i32::try_from) { if let Some(Ok(integer_32)) = v.as_i64().map(i32::try_from) {
@ -177,18 +177,15 @@ impl Value {
JSONValue::String(v) => Self::string(v), JSONValue::String(v) => Self::string(v),
JSONValue::Bool(v) => Self::boolean(v), JSONValue::Bool(v) => Self::boolean(v),
JSONValue::Array(vs) => { JSONValue::Array(vs) => {
let global_array_prototype = interpreter let array_prototype = context.standard_objects().array_object().prototype();
.global_object() let new_obj: Value =
.get_field("Array") Object::with_prototype(array_prototype.into(), ObjectData::Array).into();
.get_field(PROTOTYPE);
let new_obj_obj = Object::with_prototype(global_array_prototype, ObjectData::Array);
let new_obj = Value::object(new_obj_obj);
let length = vs.len(); let length = vs.len();
for (idx, json) in vs.into_iter().enumerate() { for (idx, json) in vs.into_iter().enumerate() {
new_obj.set_property( new_obj.set_property(
idx.to_string(), idx.to_string(),
Property::data_descriptor( Property::data_descriptor(
Self::from_json(json, interpreter), Self::from_json(json, context),
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
), ),
); );
@ -200,9 +197,9 @@ impl Value {
new_obj new_obj
} }
JSONValue::Object(obj) => { JSONValue::Object(obj) => {
let new_obj = Value::new_object(Some(interpreter.global_object())); let new_obj = Value::new_object(Some(context.global_object()));
for (key, json) in obj.into_iter() { for (key, json) in obj.into_iter() {
let value = Self::from_json(json, interpreter); let value = Self::from_json(json, context);
new_obj.set_property( new_obj.set_property(
key, key,
Property::data_descriptor( Property::data_descriptor(
@ -498,7 +495,7 @@ impl Value {
let _timer = BoaProfiler::global().start_event("Value::update_property", "value"); let _timer = BoaProfiler::global().start_event("Value::update_property", "value");
if let Some(ref mut object) = self.as_object_mut() { if let Some(ref mut object) = self.as_object_mut() {
object.insert_property(field, new_property); object.insert(field, new_property);
} }
} }
@ -584,7 +581,7 @@ impl Value {
K: Into<PropertyKey>, K: Into<PropertyKey>,
{ {
if let Some(mut object) = self.as_object_mut() { if let Some(mut object) = self.as_object_mut() {
object.insert_property(key.into(), property.clone()); object.insert(key.into(), property.clone());
} }
property property
} }
@ -691,81 +688,48 @@ impl Value {
Err(ctx.construct_type_error("cannot convert 'null' or 'undefined' to object")) Err(ctx.construct_type_error("cannot convert 'null' or 'undefined' to object"))
} }
Value::Boolean(boolean) => { Value::Boolean(boolean) => {
let proto = ctx let prototype = ctx.standard_objects().boolean_object().prototype();
.realm()
.environment
.get_binding_value("Boolean")
.expect("Boolean was not initialized")
.get_field(PROTOTYPE);
Ok(GcObject::new(Object::with_prototype( Ok(GcObject::new(Object::with_prototype(
proto, prototype.into(),
ObjectData::Boolean(*boolean), ObjectData::Boolean(*boolean),
))) )))
} }
Value::Integer(integer) => { Value::Integer(integer) => {
let proto = ctx let prototype = ctx.standard_objects().number_object().prototype();
.realm()
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
.get_field(PROTOTYPE);
Ok(GcObject::new(Object::with_prototype( Ok(GcObject::new(Object::with_prototype(
proto, prototype.into(),
ObjectData::Number(f64::from(*integer)), ObjectData::Number(f64::from(*integer)),
))) )))
} }
Value::Rational(rational) => { Value::Rational(rational) => {
let proto = ctx let prototype = ctx.standard_objects().number_object().prototype();
.realm()
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
.get_field(PROTOTYPE);
Ok(GcObject::new(Object::with_prototype( Ok(GcObject::new(Object::with_prototype(
proto, prototype.into(),
ObjectData::Number(*rational), ObjectData::Number(*rational),
))) )))
} }
Value::String(ref string) => { Value::String(ref string) => {
let proto = ctx let prototype = ctx.standard_objects().string_object().prototype();
.realm()
.environment let mut object =
.get_binding_value("String") Object::with_prototype(prototype.into(), ObjectData::String(string.clone()));
.expect("String was not initialized")
.get_field(PROTOTYPE);
let mut obj = Object::with_prototype(proto, ObjectData::String(string.clone()));
// Make sure the correct length is set on our new string object // Make sure the correct length is set on our new string object
obj.set("length".into(), string.chars().count().into()); object.set("length".into(), string.chars().count().into());
Ok(GcObject::new(obj)) Ok(GcObject::new(object))
} }
Value::Symbol(ref symbol) => { Value::Symbol(ref symbol) => {
let proto = ctx let prototype = ctx.standard_objects().symbol_object().prototype();
.realm()
.environment
.get_binding_value("Symbol")
.expect("Symbol was not initialized")
.get_field(PROTOTYPE);
Ok(GcObject::new(Object::with_prototype( Ok(GcObject::new(Object::with_prototype(
proto, prototype.into(),
ObjectData::Symbol(symbol.clone()), ObjectData::Symbol(symbol.clone()),
))) )))
} }
Value::BigInt(ref bigint) => { Value::BigInt(ref bigint) => {
let proto = ctx let prototype = ctx.standard_objects().bigint_object().prototype();
.realm() Ok(GcObject::new(Object::with_prototype(
.environment prototype.into(),
.get_binding_value("BigInt")
.expect("BigInt was not initialized")
.get_field(PROTOTYPE);
let bigint_obj = GcObject::new(Object::with_prototype(
proto,
ObjectData::BigInt(bigint.clone()), ObjectData::BigInt(bigint.clone()),
)); )))
Ok(bigint_obj)
} }
Value::Object(gcobject) => Ok(gcobject.clone()), Value::Object(gcobject) => Ok(gcobject.clone()),
} }

11
boa/src/value/rcstring.rs

@ -33,24 +33,28 @@ impl Display for RcString {
} }
impl PartialEq<str> for RcString { impl PartialEq<str> for RcString {
#[inline]
fn eq(&self, other: &str) -> bool { fn eq(&self, other: &str) -> bool {
self.as_str() == other self.as_str() == other
} }
} }
impl PartialEq<RcString> for str { impl PartialEq<RcString> for str {
#[inline]
fn eq(&self, other: &RcString) -> bool { fn eq(&self, other: &RcString) -> bool {
self == other.as_str() self == other.as_str()
} }
} }
impl PartialEq<&str> for RcString { impl PartialEq<&str> for RcString {
#[inline]
fn eq(&self, other: &&str) -> bool { fn eq(&self, other: &&str) -> bool {
self.as_str() == *other self.as_str() == *other
} }
} }
impl PartialEq<RcString> for &str { impl PartialEq<RcString> for &str {
#[inline]
fn eq(&self, other: &RcString) -> bool { fn eq(&self, other: &RcString) -> bool {
*self == other.as_str() *self == other.as_str()
} }
@ -72,6 +76,13 @@ impl Borrow<str> for RcString {
} }
} }
impl AsRef<str> for RcString {
#[inline]
fn as_ref(&self) -> &str {
self.as_str()
}
}
impl From<String> for RcString { impl From<String> for RcString {
#[inline] #[inline]
fn from(string: String) -> Self { fn from(string: String) -> Self {

Loading…
Cancel
Save