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 => {
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())),
&[index.into(), element_value],
ctx,

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

@ -13,10 +13,10 @@ pub mod array_iterator;
#[cfg(test)]
mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::array::array_iterator::{ArrayIterationKind, ArrayIterator},
object::{ObjectData, PROTOTYPE},
builtins::BuiltIn,
object::{ConstructorBuilder, FunctionBuilder, ObjectData, PROTOTYPE},
property::{Attribute, Property},
value::{same_value_zero, Value},
BoaProfiler, Context, Result,
@ -27,17 +27,122 @@ use std::cmp::{max, min};
#[derive(Debug, Clone, Copy)]
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 {
/// The name of the object.
pub(crate) const NAME: &'static str = "Array";
const LENGTH: usize = 1;
/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;
fn constructor(this: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
// 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.
pub(crate) fn new_array(interpreter: &Context) -> Result<Value> {
pub(crate) fn new_array(context: &Context) -> Result<Value> {
let array = Value::new_object(Some(
&interpreter
&context
.realm()
.environment
.get_global_object()
@ -47,14 +152,7 @@ impl Array {
array
.as_object_mut()
.expect("array object")
.set_prototype_instance(
interpreter
.realm()
.environment
.get_binding_value("Array")
.expect("Array was not initialized")
.get_field(PROTOTYPE),
);
.set_prototype_instance(context.standard_objects().array_object().prototype().into());
array.set_field("length", Value::from(0));
Ok(array)
}
@ -103,52 +201,6 @@ impl Array {
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 )`
///
/// 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
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn values(
this: &Value,
_args: &[Value],
interpreter: &mut Context,
) -> Result<Value> {
ArrayIterator::create_array_iterator(interpreter, this.clone(), ArrayIterationKind::Value)
pub(crate) fn values(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::Value)
}
/// `Array.prototype.keys( )`
@ -1121,8 +1169,8 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.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> {
ArrayIterator::create_array_iterator(interpreter, this.clone(), ArrayIterationKind::Key)
pub(crate) fn keys(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::Key)
}
/// `Array.prototype.entries( )`
@ -1135,87 +1183,7 @@ impl Array {
///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.values
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/values
pub(crate) fn entries(
this: &Value,
_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)
pub(crate) fn entries(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::KeyAndValue)
}
}

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
use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{RcBigInt, Value},
BoaProfiler, Context, Result,
};
@ -40,13 +41,57 @@ mod tests;
#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)]
pub struct BigInt(num_bigint::BigInt);
impl BigInt {
/// The name of the object.
pub(crate) const NAME: &'static str = "BigInt";
impl BuiltIn for BigInt {
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.
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 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"))
}
/// `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] )`
///
/// The `toString()` method returns a string representing the specified BigInt object.
@ -194,33 +221,6 @@ impl BigInt {
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 {}

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

@ -12,20 +12,58 @@
#[cfg(test)]
mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{object::ObjectData, BoaProfiler, Context, Result, Value};
use crate::{
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
property::Attribute,
BoaProfiler, Context, Result, Value,
};
/// Boolean implementation.
#[derive(Debug, Clone, Copy)]
pub(crate) struct Boolean;
impl Boolean {
impl BuiltIn for Boolean {
/// 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.
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]].
///
/// More information:
@ -47,21 +85,6 @@ impl 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.
///
/// More information:
@ -88,30 +111,4 @@ impl Boolean {
pub(crate) fn value_of(this: &Value, _: &[Value], ctx: &mut Context) -> Result<Value> {
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;
use crate::{
builtins::function::make_builtin_fn,
builtins::BuiltIn,
object::ObjectInitializer,
property::Attribute,
value::{display_obj, RcString, Value},
BoaProfiler, Context, Result,
};
@ -136,6 +138,41 @@ pub(crate) struct Console {
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 {
/// The name of the object.
pub(crate) const NAME: &'static str = "console";
@ -495,35 +532,4 @@ impl Console {
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;
use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{PreferredType, Value},
BoaProfiler, Context, Result,
};
@ -104,10 +105,89 @@ impl Default for Date {
}
}
impl Date {
/// The name of the object.
pub(crate) const NAME: &'static str = "Date";
impl BuiltIn for Date {
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.
pub(crate) const LENGTH: usize = 7;
@ -243,7 +323,7 @@ impl Date {
///
/// [spec]: https://tc39.es/ecma262/#sec-date-constructor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/Date
pub(crate) fn make_date(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() {
Self::make_date_string()
} else if args.is_empty() {
@ -1236,333 +1316,6 @@ impl Date {
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.

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
use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn},
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
};
@ -35,15 +36,41 @@ pub(crate) use self::syntax::SyntaxError;
#[derive(Debug, Clone, Copy)]
pub(crate) struct Error;
impl Error {
/// The name of the object.
pub(crate) const NAME: &'static str = "Error";
impl BuiltIn for Error {
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.
pub(crate) const LENGTH: usize = 1;
/// `Error( message )`
///
/// 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) {
this.set_field("message", message.to_string(ctx)?);
}
@ -65,38 +92,9 @@ impl Error {
/// [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");
Ok(Value::from(format!(
"{}: {}",
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)
pub(crate) fn to_string(this: &Value, _: &[Value], context: &mut Context) -> Result<Value> {
let name = this.get_field("name").to_string(context)?;
let message = this.get_field("message").to_string(context)?;
Ok(format!("{}: {}", name, message).into())
}
}

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
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn},
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
};
@ -20,15 +21,40 @@ use crate::{
#[derive(Debug, Clone, Copy)]
pub(crate) struct RangeError;
impl RangeError {
/// The name of the object.
pub(crate) const NAME: &'static str = "RangeError";
impl BuiltIn for RangeError {
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.
pub(crate) const LENGTH: usize = 1;
/// 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) {
this.set_field("message", message.to_string(ctx)?);
}
@ -38,47 +64,4 @@ impl RangeError {
this.set_data(ObjectData::Error);
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
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn},
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
};
#[derive(Debug, Clone, Copy)]
pub(crate) struct ReferenceError;
impl ReferenceError {
/// The name of the object.
pub(crate) const NAME: &'static str = "ReferenceError";
impl BuiltIn for ReferenceError {
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.
pub(crate) const LENGTH: usize = 1;
/// 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) {
this.set_field("message", message.to_string(ctx)?);
}
@ -37,46 +63,4 @@ impl ReferenceError {
this.set_data(ObjectData::Error);
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
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn},
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
profiler::BoaProfiler,
property::Attribute,
Context, Result, Value,
};
@ -22,15 +23,40 @@ use crate::{
#[derive(Debug, Clone, Copy)]
pub(crate) struct SyntaxError;
impl SyntaxError {
/// The name of the object.
pub(crate) const NAME: &'static str = "SyntaxError";
impl BuiltIn for SyntaxError {
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.
pub(crate) const LENGTH: usize = 1;
/// 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) {
this.set_field("message", message.to_string(ctx)?);
}
@ -40,47 +66,4 @@ impl SyntaxError {
this.set_data(ObjectData::Error);
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
use crate::{
builtins::{function::make_builtin_fn, function::make_constructor_fn},
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
property::Attribute,
BoaProfiler, Context, Result, Value,
};
@ -25,15 +26,40 @@ use crate::{
#[derive(Debug, Clone, Copy)]
pub(crate) struct TypeError;
impl TypeError {
/// The name of the object.
pub(crate) const NAME: &'static str = "TypeError";
impl BuiltIn for TypeError {
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.
pub(crate) const LENGTH: usize = 1;
/// 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) {
this.set_field("message", message.to_string(ctx)?);
}
@ -43,47 +69,4 @@ impl TypeError {
this.set_data(ObjectData::Error);
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
use crate::{
builtins::Array,
builtins::{Array, BuiltIn},
environment::lexical_environment::Environment,
object::{Object, ObjectData, PROTOTYPE},
object::{ConstructorBuilder, Object, ObjectData, PROTOTYPE},
property::{Attribute, Property},
syntax::ast::node::{FormalParameter, RcStatementList},
BoaProfiler, Context, Result, Value,
@ -188,24 +188,13 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
);
obj.insert_property(index, prop);
obj.insert(index, prop);
index += 1;
}
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
///
/// This utility function handling linking the new Constructor to the prototype.
@ -237,25 +226,26 @@ pub fn make_constructor_fn(
length.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert_property("length", length);
constructor.insert("length", length);
let name = Property::data_descriptor(
name.into(),
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT,
);
constructor.insert_property("name", name);
constructor.insert("name", name);
let constructor = Value::from(constructor);
prototype
.as_object_mut()
.unwrap()
.insert_field("constructor", constructor.clone());
prototype.as_object_mut().unwrap().insert_property(
"constructor",
constructor.clone(),
Attribute::all(),
);
constructor
.as_object_mut()
.expect("constructor object")
.insert_field(PROTOTYPE, prototype);
.insert_property(PROTOTYPE, prototype, Attribute::all());
constructor
}
@ -297,23 +287,48 @@ pub fn make_builtin_fn<N>(
.get_field("Function")
.get_field("prototype"),
);
function.insert_field("length", Value::from(length));
function.insert_property("length", length, Attribute::all());
parent
.as_object_mut()
.unwrap()
.insert_field(name, Value::from(function));
.insert_property(name, function, Attribute::all());
}
/// Initialise the `Function` 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("function", "init");
let prototype = Value::new_object(Some(global));
#[derive(Debug, Clone, Copy)]
pub struct BuiltInFunctionObject;
let function_object =
make_constructor_fn("Function", 1, make_function, global, prototype, true, true);
impl BuiltInFunctionObject {
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
//! [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)]
mod tests;
@ -18,16 +18,20 @@ mod tests;
/// The JavaScript `globalThis`.
pub(crate) struct GlobalThis;
impl GlobalThis {
/// The binding name of the property.
pub(crate) const NAME: &'static str = "globalThis";
impl BuiltIn for GlobalThis {
const NAME: &'static str = "globalThis";
/// Initialize the `globalThis` property on the global object.
#[inline]
pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) {
let global = interpreter.global_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");
(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)]
mod tests;
use crate::{BoaProfiler, Context, Value};
use crate::{builtins::BuiltIn, property::Attribute, BoaProfiler, Context, Value};
/// JavaScript global `Infinity` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Infinity;
impl Infinity {
/// The binding name of the property.
pub(crate) const NAME: &'static str = "Infinity";
impl BuiltIn for Infinity {
const NAME: &'static str = "Infinity";
/// Initialize the `Infinity` property on the global object.
#[inline]
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) {
fn attribute() -> Attribute {
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
}
fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
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::{
builtins::string::string_iterator::StringIterator,
builtins::{
function::{BuiltInFunction, Function, FunctionFlags},
ArrayIterator,
},
object::GcObject,
object::{Object, PROTOTYPE},
builtins::ArrayIterator,
object::{GcObject, ObjectInitializer},
property::Property,
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
fn create_iterator_prototype(ctx: &mut Context) -> Value {
let global = ctx.global_object();
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();
iterator_prototype.set_field(symbol_iterator, Value::from(function));
iterator_prototype
let iterator_prototype = ObjectInitializer::new(ctx)
.function(
|v, _, _| Ok(v.clone()),
(symbol_iterator, "[Symbol.iterator]"),
0,
)
.build();
// TODO: return GcObject
iterator_prototype.into()
}
#[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
use crate::{
builtins::function::make_builtin_fn,
property::{Property, PropertyKey},
builtins::BuiltIn,
object::ObjectInitializer,
property::{Attribute, Property, PropertyKey},
BoaProfiler, Context, Result, Value,
};
use serde_json::{self, Value as JSONValue};
@ -27,10 +28,26 @@ mod tests;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Json;
impl Json {
/// The name of the object.
pub(crate) const NAME: &'static str = "JSON";
impl BuiltIn for Json {
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] )`
///
/// 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()))
}
}
/// 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)]
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
object::{ObjectData, PROTOTYPE},
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData, PROTOTYPE},
property::{Attribute, Property},
BoaProfiler, Context, Result, Value,
};
@ -15,11 +15,89 @@ mod tests;
#[derive(Debug, Clone)]
pub(crate) struct Map(OrderedMap<Value, Value>);
impl Map {
pub(crate) const NAME: &'static str = "Map";
impl BuiltIn for 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;
/// 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.
fn set_size(this: &Value, size: usize) {
let size = Property::data_descriptor(
@ -221,89 +299,4 @@ impl Map {
}
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
//! [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;
#[cfg(test)]
@ -21,10 +24,68 @@ mod tests;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Math;
impl Math {
/// The name of the object.
pub(crate) const NAME: &'static str = "Math";
impl BuiltIn for Math {
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.
///
/// More information:
@ -632,69 +693,4 @@ impl Math {
.map_or(f64::NAN, f64::trunc)
.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,
date::Date,
error::{Error, RangeError, ReferenceError, SyntaxError, TypeError},
function::BuiltInFunctionObject,
global_this::GlobalThis,
infinity::Infinity,
json::Json,
@ -35,54 +36,64 @@ pub(crate) use self::{
math::Math,
nan::NaN,
number::Number,
object::Object,
object::Object as BuiltInObjectObject,
regexp::RegExp,
string::String,
symbol::Symbol,
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
#[inline]
pub fn init(interpreter: &mut Context) {
pub fn init(context: &mut Context) {
let globals = [
// The `Function` global must be initialized before other types.
function::init,
Object::init,
Symbol::init,
// Global properties.
Undefined::init,
Infinity::init,
NaN::init,
GlobalThis::init,
BuiltInFunctionObject::init,
BuiltInObjectObject::init,
Math::init,
Json::init,
Console::init,
Array::init,
BigInt::init,
Boolean::init,
Date::init,
Json::init,
Map::init,
Math::init,
Number::init,
RegExp::init,
String::init,
Console::init,
// Global error types.
RegExp::init,
Symbol::init,
Error::init,
RangeError::init,
ReferenceError::init,
TypeError::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 {
let (name, value) = init(interpreter);
let global = interpreter.global_object();
match global {
Value::Object(ref global_object) => {
global_object.borrow_mut().insert_field(name, value);
}
_ => unreachable!("expect global object"),
}
let (name, value, attribute) = init(context);
let property = Property::data_descriptor(value, attribute);
global_object.borrow_mut().insert(name, property);
}
}

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

@ -13,21 +13,22 @@
#[cfg(test)]
mod tests;
use crate::{BoaProfiler, Context, Value};
use crate::{builtins::BuiltIn, property::Attribute, BoaProfiler, Context, Value};
/// JavaScript global `NaN` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct NaN;
impl NaN {
/// The binding name of the property.
pub(crate) const NAME: &'static str = "NaN";
impl BuiltIn for NaN {
const NAME: &'static str = "NaN";
/// Initialize the `NaN` property on the global object.
#[inline]
pub(crate) fn init(_interpreter: &mut Context) -> (&'static str, Value) {
fn attribute() -> Attribute {
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
}
fn init(_context: &mut Context) -> (&'static str, Value, Attribute) {
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
//! [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::{
object::ObjectData,
builtins::BuiltIn,
object::{ConstructorBuilder, ObjectData},
property::Attribute,
value::{AbstractRelation, Value},
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.
const PARSE_FLOAT_MAX_ARG_COUNT: usize = 1;
impl Number {
/// The name of the object.
pub(crate) const NAME: &'static str = "Number";
impl BuiltIn for Number {
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.
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
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`.
///
/// 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] )`
///
/// 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()
}
/// 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
/// 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
use crate::{
builtins::function::{make_builtin_fn, make_constructor_fn},
object::{Object as BuiltinObject, ObjectData},
property::Property,
builtins::BuiltIn,
object::{ConstructorBuilder, Object as BuiltinObject, ObjectData},
property::{Attribute, Property},
value::{same_value, Value},
BoaProfiler, Context, Result,
};
@ -28,15 +28,48 @@ mod tests;
#[derive(Debug, Clone, Copy)]
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 {
/// Create a new object.
pub fn make_object(_: &Value, args: &[Value], ctx: &mut Context) -> Result<Value> {
const LENGTH: usize = 1;
fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result<Value> {
if let Some(arg) = args.get(0) {
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)))
}
@ -192,66 +225,4 @@ impl Object {
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
//! [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::{
object::ObjectData,
property::Property,
builtins::BuiltIn,
gc::{empty_trace, Finalize, Trace},
object::{ConstructorBuilder, ObjectData},
property::{Attribute, Property},
value::{RcString, Value},
BoaProfiler, Context, Result,
};
use gc::{unsafe_empty_trace, Finalize, Trace};
use regex::Regex;
#[cfg(test)]
mod tests;
@ -58,7 +57,36 @@ pub struct 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 {
@ -69,7 +97,7 @@ impl RegExp {
pub(crate) const LENGTH: usize = 2;
/// 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 mut regex_body = String::new();
let mut regex_flags = String::new();
@ -474,44 +502,4 @@ impl RegExp {
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)]
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::{
builtins::RegExp,
object::{Object, ObjectData},
property::Property,
builtins::{string::string_iterator::StringIterator, BuiltIn, RegExp},
object::{ConstructorBuilder, Object, ObjectData},
property::Attribute,
value::{RcString, Value},
BoaProfiler, Context, Result,
};
@ -65,40 +61,73 @@ fn is_trailing_surrogate(value: u16) -> bool {
#[derive(Debug, Clone, Copy)]
pub(crate) struct String;
impl String {
/// The name of the object.
pub(crate) const NAME: &'static str = "String";
impl BuiltIn for String {
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.
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 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`
pub(crate) const MAX_STRING_LENGTH: f64 = u32::MAX as f64;
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"))
}
/// [[Construct]] - Creates a new instance `this`
/// `String( value )`
///
/// [[Call]] - Returns a new native `string`
/// <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
// to its Javascript Identifier (global constructor method name)
let string = match args.get(0) {
@ -115,6 +144,21 @@ impl 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
#[allow(clippy::wrong_self_convention)]
#[inline]
@ -739,7 +783,7 @@ impl String {
/// [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
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)
}
@ -1092,13 +1136,13 @@ impl String {
let re: Value = match args.get(0) {
Some(arg) => {
if arg.is_null() {
RegExp::make_regexp(
RegExp::constructor(
&Value::from(Object::default()),
&[Value::from(arg.to_string(ctx)?), Value::from("g")],
ctx,
)
} else if arg.is_undefined() {
RegExp::make_regexp(
RegExp::constructor(
&Value::from(Object::default()),
&[Value::undefined(), Value::from("g")],
ctx,
@ -1107,7 +1151,7 @@ impl String {
Ok(arg.clone())
}
}
None => RegExp::make_regexp(
None => RegExp::constructor(
&Value::from(Object::default()),
&[Value::from(""), Value::from("g")],
ctx,
@ -1120,82 +1164,4 @@ impl String {
pub(crate) fn iterator(this: &Value, _args: &[Value], ctx: &mut Context) -> Result<Value> {
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)]
mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
property::{Attribute, Property},
builtins::BuiltIn,
gc::{Finalize, Trace},
object::ConstructorBuilder,
property::Attribute,
value::{RcString, RcSymbol, Value},
BoaProfiler, Context, Result,
};
use gc::{Finalize, Trace};
/// A structure that contains the JavaScript well known symbols.
#[derive(Debug, Clone)]
@ -232,10 +233,64 @@ impl Symbol {
}
}
impl Symbol {
/// The name of the object.
pub(crate) const NAME: &'static str = "Symbol";
impl BuiltIn for Symbol {
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.
pub(crate) const LENGTH: usize = 0;
@ -249,21 +304,6 @@ impl Symbol {
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.
///
/// 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
/// [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) {
Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?),
_ => None,
@ -284,6 +324,21 @@ impl Symbol {
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()`
///
/// This method returns a string representing the specified `Symbol` object.
@ -300,103 +355,4 @@ impl Symbol {
let description = symbol.description().unwrap_or("");
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
//! [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)]
mod tests;
use crate::{BoaProfiler, Context, Value};
/// JavaScript global `undefined` property.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Undefined;
impl Undefined {
/// The binding name of the property.
pub(crate) const NAME: &'static str = "undefined";
impl BuiltIn for Undefined {
const NAME: &'static str = "undefined";
fn attribute() -> Attribute {
Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT
}
/// Initialize the `undefined` property on the global object.
#[inline]
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");
(Self::NAME, Value::undefined())
(Self::NAME, Value::undefined(), Self::attribute())
}
}

144
boa/src/class.rs

@ -61,12 +61,11 @@
//! [class-trait]: ./trait.Class.html
use crate::{
builtins::function::{BuiltInFunction, Function, FunctionFlags, NativeFunction},
object::{GcObject, NativeObject, Object, ObjectData, PROTOTYPE},
property::{Attribute, Property, PropertyKey},
builtins::function::NativeFunction,
object::{ConstructorBuilder, GcObject, NativeObject, ObjectData},
property::{Attribute, PropertyKey},
Context, Result, Value,
};
use std::fmt::Debug;
/// Native class.
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.
#[derive(Debug)]
pub struct ClassBuilder<'context> {
/// The current context.
context: &'context mut Context,
/// The constructor object.
object: GcObject,
/// The prototype of the object.
prototype: GcObject,
builder: ConstructorBuilder<'context>,
}
impl<'context> ClassBuilder<'context> {
#[inline]
pub(crate) fn new<T>(context: &'context mut Context) -> Self
where
T: ClassConstructor,
{
let global = context.global_object();
let prototype = {
let object_prototype = global.get_field("Object").get_field(PROTOTYPE);
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,
}
let mut builder = ConstructorBuilder::new(context, T::raw_constructor);
builder.name(T::NAME);
builder.length(T::LENGTH);
Self { builder }
}
pub(crate) fn build(self) -> GcObject {
self.object
#[inline]
pub(crate) fn build(mut self) -> GcObject {
self.builder.build()
}
/// Add a method to the class.
///
/// 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
N: Into<String>,
N: AsRef<str>,
{
let name = name.into();
let mut function = Object::function(
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));
self.builder.method(function, name.as_ref(), length);
self
}
/// Add a static method to the class.
///
/// 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
N: Into<String>,
N: AsRef<str>,
{
let name = name.into();
let mut function = Object::function(
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));
self.builder.static_method(function, name.as_ref(), length);
self
}
/// Add a property to the class, with the specified attribute.
///
/// It is added to `prototype`.
#[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
K: Into<PropertyKey>,
V: Into<Value>,
{
// We bitwise or (`|`) with `Attribute::default()` (`READONLY | NON_ENUMERABLE | PERMANENT`)
// so we dont get an empty attribute.
let property = Property::data_descriptor(value.into(), attribute | Attribute::default());
self.prototype
.borrow_mut()
.insert_property(key.into(), property);
self.builder.property(key, value, attribute);
self
}
/// Add a static property to the class, with the specified attribute.
///
/// It is added to class object itself.
#[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
K: Into<PropertyKey>,
V: Into<Value>,
{
// We bitwise or (`|`) with `Attribute::default()` (`READONLY | NON_ENUMERABLE | PERMANENT`)
// so we dont get an empty attribute.
let property = Property::data_descriptor(value.into(), attribute | Attribute::default());
self.object
.borrow_mut()
.insert_property(key.into(), property);
self.builder.static_property(key, value, attribute);
self
}
/// Return the current context.
#[inline]
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;
/// 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.
///
/// For each `Context` instance a new instance of runtime is created.
/// It means that it is safe to use different contexts in different threads,
/// but each `Context` instance must be used only from a single thread.
/// `Context`s constructed in a thread share the same runtime, therefore it
/// is possible to share objects from one context to another context, but they
/// have to be in the same thread.
#[derive(Debug)]
pub struct Context {
/// realm holds both the global object and the environment
@ -52,7 +177,11 @@ pub struct Context {
/// Cached well known symbols
well_known_symbols: WellKnownSymbols,
/// Cached iterator prototypes.
iterator_prototypes: IteratorPrototypes,
/// Cached standard objects and their prototypes
standard_objects: StandardObjects,
}
impl Default for Context {
@ -67,6 +196,7 @@ impl Default for Context {
console: Console::default(),
well_known_symbols,
iterator_prototypes: IteratorPrototypes::default(),
standard_objects: Default::default(),
};
// Add new builtIns to Context Realm
@ -468,7 +598,7 @@ impl Context {
self.global_object()
.as_object_mut()
.unwrap()
.insert_property(T::NAME, property);
.insert(T::NAME, property);
Ok(())
}
@ -522,8 +652,15 @@ impl Context {
&self.well_known_symbols
}
/// Return the cached iterator prototypes.
#[inline]
pub fn iterator_prototypes(&self) -> &IteratorPrototypes {
&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 value;
mod context;
pub mod context;
use std::result::Result as StdResult;
pub(crate) use crate::{exec::Executable, profiler::BoaProfiler};
// Export things to root level
#[doc(inline)]
pub use crate::{context::Context, value::Value};
use crate::syntax::{

25
boa/src/object/internal_methods.rs

@ -168,7 +168,7 @@ impl Object {
return false;
}
self.insert_property(key, desc);
self.insert(key, desc);
return true;
}
// If every field is absent we don't need to set anything
@ -207,7 +207,7 @@ impl Object {
current.set = None;
}
self.insert_property(key, current);
self.insert(key, current);
return true;
// 7
} else if current.is_data_descriptor() && desc.is_data_descriptor() {
@ -247,7 +247,7 @@ impl Object {
return true;
}
// 9
self.insert_property(key, desc);
self.insert(key, desc);
true
}
@ -336,7 +336,7 @@ impl Object {
/// Helper function for property insertion.
#[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
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
/// with that field, otherwise None is retuned.
#[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
K: Into<PropertyKey>,
V: Into<Value>,
{
self.insert_property(
self.insert(
key.into(),
Property::data_descriptor(
value,
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
value.into(),
attribute
| Attribute::HAS_WRITABLE
| Attribute::HAS_ENUMERABLE
| Attribute::HAS_CONFIGURABLE,
),
)
}

535
boa/src/object/mod.rs

@ -2,17 +2,24 @@
use crate::{
builtins::{
array::array_iterator::ArrayIterator, function::Function, map::ordered_map::OrderedMap,
string::string_iterator::StringIterator, BigInt, Date, RegExp,
array::array_iterator::ArrayIterator,
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},
BoaProfiler,
BoaProfiler, Context,
};
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap;
use std::fmt::{Debug, Display, Error, Formatter};
use std::{any::Any, result::Result as StdResult};
use std::{
any::Any,
fmt::{self, Debug, Display},
ops::{Deref, DerefMut},
};
mod gcobject;
mod internal_methods;
@ -36,10 +43,12 @@ pub trait NativeObject: Debug + Any + Trace {
}
impl<T: Any + Debug + Trace> NativeObject for T {
#[inline]
fn as_any(&self) -> &dyn Any {
self as &dyn Any
}
#[inline]
fn as_mut_any(&mut self) -> &mut dyn Any {
self as &mut dyn Any
}
@ -83,7 +92,7 @@ pub enum ObjectData {
}
impl Display for ObjectData {
fn fmt(&self, f: &mut Formatter<'_>) -> StdResult<(), Error> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"{}",
@ -131,6 +140,7 @@ impl Object {
}
/// Return a new ObjectData struct, with `kind` set to Ordinary
#[inline]
pub fn function(function: Function, prototype: Value) -> Self {
let _timer = BoaProfiler::global().start_event("Object::Function", "object");
@ -151,6 +161,7 @@ impl Object {
///
/// [spec]: https://tc39.es/ecma262/#sec-objectcreate
// TODO: proto should be a &Value here
#[inline]
pub fn create(proto: Value) -> Self {
let mut obj = Self::default();
obj.prototype = proto;
@ -158,6 +169,7 @@ impl Object {
}
/// Return a new Boolean object whose `[[BooleanData]]` internal slot is set to argument.
#[inline]
pub fn boolean(value: bool) -> Self {
Self {
data: ObjectData::Boolean(value),
@ -170,6 +182,7 @@ impl Object {
}
/// Return a new `Number` object whose `[[NumberData]]` internal slot is set to argument.
#[inline]
pub fn number(value: f64) -> Self {
Self {
data: ObjectData::Number(value),
@ -182,6 +195,7 @@ impl Object {
}
/// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
#[inline]
pub fn string<S>(value: S) -> Self
where
S: Into<RcString>,
@ -197,6 +211,7 @@ impl Object {
}
/// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
#[inline]
pub fn bigint(value: RcBigInt) -> Self {
Self {
data: ObjectData::BigInt(value),
@ -209,6 +224,7 @@ impl Object {
}
/// Create a new native object of type `T`.
#[inline]
pub fn native_object<T>(value: T) -> Self
where
T: NativeObject,
@ -429,10 +445,13 @@ impl Object {
matches!(self.data, ObjectData::Ordinary)
}
#[inline]
pub fn prototype_instance(&self) -> &Value {
&self.prototype
}
#[track_caller]
#[inline]
pub fn set_prototype_instance(&mut self, prototype: Value) {
assert!(prototype.is_null() || prototype.is_object());
self.prototype = prototype
@ -448,16 +467,17 @@ impl Object {
}
/// Returns `true` if it holds an Rust type that implements `NativeObject`.
#[inline]
pub fn is_native_object(&self) -> bool {
matches!(self.data, ObjectData::NativeObject(_))
}
/// Reeturn `true` if it is a native object and the native type is `T`.
#[inline]
pub fn is<T>(&self) -> bool
where
T: NativeObject,
{
use std::ops::Deref;
match self.data {
ObjectData::NativeObject(ref object) => object.deref().as_any().is::<T>(),
_ => false,
@ -466,11 +486,11 @@ impl Object {
/// Downcast a reference to the object,
/// if the object is type native object type `T`.
#[inline]
pub fn downcast_ref<T>(&self) -> Option<&T>
where
T: NativeObject,
{
use std::ops::Deref;
match self.data {
ObjectData::NativeObject(ref object) => object.deref().as_any().downcast_ref::<T>(),
_ => None,
@ -479,11 +499,11 @@ impl Object {
/// Downcast a mutable reference to the object,
/// if the object is type native object type `T`.
#[inline]
pub fn downcast_mut<T>(&mut self) -> Option<&mut T>
where
T: NativeObject,
{
use std::ops::DerefMut;
match self.data {
ObjectData::NativeObject(ref mut object) => {
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 {
let mut array = Object::default();
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)
}
@ -140,7 +140,7 @@ where
fn from(value: Vec<T>) -> Self {
let mut array = Object::default();
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)
}

90
boa/src/value/mod.rs

@ -165,7 +165,7 @@ impl 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 {
JSONValue::Number(v) => {
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::Bool(v) => Self::boolean(v),
JSONValue::Array(vs) => {
let global_array_prototype = interpreter
.global_object()
.get_field("Array")
.get_field(PROTOTYPE);
let new_obj_obj = Object::with_prototype(global_array_prototype, ObjectData::Array);
let new_obj = Value::object(new_obj_obj);
let array_prototype = context.standard_objects().array_object().prototype();
let new_obj: Value =
Object::with_prototype(array_prototype.into(), ObjectData::Array).into();
let length = vs.len();
for (idx, json) in vs.into_iter().enumerate() {
new_obj.set_property(
idx.to_string(),
Property::data_descriptor(
Self::from_json(json, interpreter),
Self::from_json(json, context),
Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE,
),
);
@ -200,9 +197,9 @@ impl Value {
new_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() {
let value = Self::from_json(json, interpreter);
let value = Self::from_json(json, context);
new_obj.set_property(
key,
Property::data_descriptor(
@ -498,7 +495,7 @@ impl Value {
let _timer = BoaProfiler::global().start_event("Value::update_property", "value");
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>,
{
if let Some(mut object) = self.as_object_mut() {
object.insert_property(key.into(), property.clone());
object.insert(key.into(), property.clone());
}
property
}
@ -691,81 +688,48 @@ impl Value {
Err(ctx.construct_type_error("cannot convert 'null' or 'undefined' to object"))
}
Value::Boolean(boolean) => {
let proto = ctx
.realm()
.environment
.get_binding_value("Boolean")
.expect("Boolean was not initialized")
.get_field(PROTOTYPE);
let prototype = ctx.standard_objects().boolean_object().prototype();
Ok(GcObject::new(Object::with_prototype(
proto,
prototype.into(),
ObjectData::Boolean(*boolean),
)))
}
Value::Integer(integer) => {
let proto = ctx
.realm()
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
.get_field(PROTOTYPE);
let prototype = ctx.standard_objects().number_object().prototype();
Ok(GcObject::new(Object::with_prototype(
proto,
prototype.into(),
ObjectData::Number(f64::from(*integer)),
)))
}
Value::Rational(rational) => {
let proto = ctx
.realm()
.environment
.get_binding_value("Number")
.expect("Number was not initialized")
.get_field(PROTOTYPE);
let prototype = ctx.standard_objects().number_object().prototype();
Ok(GcObject::new(Object::with_prototype(
proto,
prototype.into(),
ObjectData::Number(*rational),
)))
}
Value::String(ref string) => {
let proto = ctx
.realm()
.environment
.get_binding_value("String")
.expect("String was not initialized")
.get_field(PROTOTYPE);
let mut obj = Object::with_prototype(proto, ObjectData::String(string.clone()));
let prototype = ctx.standard_objects().string_object().prototype();
let mut object =
Object::with_prototype(prototype.into(), ObjectData::String(string.clone()));
// Make sure the correct length is set on our new string object
obj.set("length".into(), string.chars().count().into());
Ok(GcObject::new(obj))
object.set("length".into(), string.chars().count().into());
Ok(GcObject::new(object))
}
Value::Symbol(ref symbol) => {
let proto = ctx
.realm()
.environment
.get_binding_value("Symbol")
.expect("Symbol was not initialized")
.get_field(PROTOTYPE);
let prototype = ctx.standard_objects().symbol_object().prototype();
Ok(GcObject::new(Object::with_prototype(
proto,
prototype.into(),
ObjectData::Symbol(symbol.clone()),
)))
}
Value::BigInt(ref bigint) => {
let proto = ctx
.realm()
.environment
.get_binding_value("BigInt")
.expect("BigInt was not initialized")
.get_field(PROTOTYPE);
let bigint_obj = GcObject::new(Object::with_prototype(
proto,
let prototype = ctx.standard_objects().bigint_object().prototype();
Ok(GcObject::new(Object::with_prototype(
prototype.into(),
ObjectData::BigInt(bigint.clone()),
));
Ok(bigint_obj)
)))
}
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 {
#[inline]
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<RcString> for str {
#[inline]
fn eq(&self, other: &RcString) -> bool {
self == other.as_str()
}
}
impl PartialEq<&str> for RcString {
#[inline]
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<RcString> for &str {
#[inline]
fn eq(&self, other: &RcString) -> bool {
*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 {
#[inline]
fn from(string: String) -> Self {

Loading…
Cancel
Save