diff --git a/boa/src/builtins/array/array_iterator.rs b/boa/src/builtins/array/array_iterator.rs index 29beb9ad60..55593ce96a 100644 --- a/boa/src/builtins/array/array_iterator.rs +++ b/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, diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 2d25b0270d..f25bbd3041 100644 --- a/boa/src/builtins/array/mod.rs +++ b/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 { + // 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 { + pub(crate) fn new_array(context: &Context) -> Result { 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 { - // 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 { - ArrayIterator::create_array_iterator(interpreter, this.clone(), ArrayIterationKind::Value) + pub(crate) fn values(this: &Value, _: &[Value], ctx: &mut Context) -> Result { + 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 { - ArrayIterator::create_array_iterator(interpreter, this.clone(), ArrayIterationKind::Key) + pub(crate) fn keys(this: &Value, _: &[Value], ctx: &mut Context) -> Result { + 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 { - 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 { + ArrayIterator::create_array_iterator(ctx, this.clone(), ArrayIterationKind::KeyAndValue) } } diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 3fa59a5162..82d2c62903 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/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 { + 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 { - 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 {} diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index 7ccb02b72b..2a1357c026 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/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 { + // 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 { - // 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 { 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) - } } diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index b9504f7a9e..ba94d72bac 100644 --- a/boa/src/builtins/console/mod.rs +++ b/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, } +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) - } } diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index 9a6779aa90..f604f6a493 100644 --- a/boa/src/builtins/date/mod.rs +++ b/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 { + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { 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. diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index 5321b62c7c..61d73599a5 100644 --- a/boa/src/builtins/error/mod.rs +++ b/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 { + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { 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 { - 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 { + let name = this.get_field("name").to_string(context)?; + let message = this.get_field("message").to_string(context)?; + Ok(format!("{}: {}", name, message).into()) } } diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 9fea28d9a1..e8dd56e3f7 100644 --- a/boa/src/builtins/error/range.rs +++ b/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 { + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { 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 { - 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) - } } diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index 144e055fc0..3144f7a47e 100644 --- a/boa/src/builtins/error/reference.rs +++ b/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 { + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { 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 { - 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) - } } diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 086b558def..55dd588660 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/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 { + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { 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 { - 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) - } } diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index e2e301c12f..fc0ec4aa9f 100644 --- a/boa/src/builtins/error/type.rs +++ b/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 { + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { 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 { - 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) - } } diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 2b0d029fe7..de6278a147 100644 --- a/boa/src/builtins/function/mod.rs +++ b/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 { - 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( .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 { + 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()) + } } diff --git a/boa/src/builtins/global_this/mod.rs b/boa/src/builtins/global_this/mod.rs index 23e45020bd..42822e40e9 100644 --- a/boa/src/builtins/global_this/mod.rs +++ b/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(), + ) } } diff --git a/boa/src/builtins/infinity/mod.rs b/boa/src/builtins/infinity/mod.rs index b1e8c26da7..8691ddc8c7 100644 --- a/boa/src/builtins/infinity/mod.rs +++ b/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()) } } diff --git a/boa/src/builtins/iterable/mod.rs b/boa/src/builtins/iterable/mod.rs index 881f223ef5..3f3ab44209 100644 --- a/boa/src/builtins/iterable/mod.rs +++ b/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 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)] diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index dae55bf357..dc44292045 100644 --- a/boa/src/builtins/json/mod.rs +++ b/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) - } } diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 98918556c5..cf925ba40f 100644 --- a/boa/src/builtins/map/mod.rs +++ b/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); -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 { + // 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 { - // 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) - } } diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index d17769e9e0..e4167efe15 100644 --- a/boa/src/builtins/math/mod.rs +++ b/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)) - } } diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index da592c010e..46533f2cd4 100644 --- a/boa/src/builtins/mod.rs +++ b/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); } } diff --git a/boa/src/builtins/nan/mod.rs b/boa/src/builtins/nan/mod.rs index 1dfb55c3c5..5c5b419451 100644 --- a/boa/src/builtins/nan/mod.rs +++ b/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()) } } diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 2a92f63d10..4d5cb218bc 100644 --- a/boa/src/builtins/number/mod.rs +++ b/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 { + 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 { - 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: /// diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 429303797e..173df33f4f 100644 --- a/boa/src/builtins/object/mod.rs +++ b/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 { + const LENGTH: usize = 1; + + fn constructor(_: &Value, args: &[Value], context: &mut Context) -> Result { 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) - } } diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index e21e5e2cf6..327c0997ae 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/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 { + pub(crate) fn constructor(this: &Value, args: &[Value], _: &mut Context) -> Result { 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) - } } diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index cc33802623..a1a76c12b9 100644 --- a/boa/src/builtins/string/mod.rs +++ b/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 { - 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` /// - pub(crate) fn make_string(this: &Value, args: &[Value], ctx: &mut Context) -> Result { + pub(crate) fn constructor(this: &Value, args: &[Value], ctx: &mut Context) -> Result { // 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 { + 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 { - 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 { 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) - } } diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 3eae3a8d61..04dd9f08ce 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/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 { - 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 { + pub(crate) fn constructor(_: &Value, args: &[Value], ctx: &mut Context) -> Result { 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 { + 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) - } } diff --git a/boa/src/builtins/undefined/mod.rs b/boa/src/builtins/undefined/mod.rs index 76a4357901..4f389a1e6c 100644 --- a/boa/src/builtins/undefined/mod.rs +++ b/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()) } } diff --git a/boa/src/class.rs b/boa/src/class.rs index e3d4200421..e6d33b5114 100644 --- a/boa/src/class.rs +++ b/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 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(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(&mut self, name: N, length: usize, function: NativeFunction) + #[inline] + pub fn method(&mut self, name: N, length: usize, function: NativeFunction) -> &mut Self where - N: Into, + N: AsRef, { - 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(&mut self, name: N, length: usize, function: NativeFunction) + #[inline] + pub fn static_method( + &mut self, + name: N, + length: usize, + function: NativeFunction, + ) -> &mut Self where - N: Into, + N: AsRef, { - 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(&mut self, key: K, value: V, attribute: Attribute) + pub fn property(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self where K: Into, V: Into, { - // 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(&mut self, key: K, value: V, attribute: Attribute) + pub fn static_property(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self where K: Into, V: Into, { - // 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() } } diff --git a/boa/src/context.rs b/boa/src/context.rs index d6b95188df..b4cf7b4cc6 100644 --- a/boa/src/context.rs +++ b/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 + } } diff --git a/boa/src/lib.rs b/boa/src/lib.rs index 3718825c4e..c432e6c85f 100644 --- a/boa/src/lib.rs +++ b/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::{ diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index 7401fac90b..4a9f44e4ad 100644 --- a/boa/src/object/internal_methods.rs +++ b/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(&mut self, key: K, property: Property) -> Option + pub(crate) fn insert(&mut self, key: K, property: Property) -> Option where K: Into, { @@ -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(&mut self, key: K, value: Value) -> Option + pub(crate) fn insert_property( + &mut self, + key: K, + value: V, + attribute: Attribute, + ) -> Option where K: Into, + V: Into, { - 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, ), ) } diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 3776f67513..e165422492 100644 --- a/boa/src/object/mod.rs +++ b/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 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(value: S) -> Self where S: Into, @@ -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(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(&self) -> bool where T: NativeObject, { - use std::ops::Deref; match self.data { ObjectData::NativeObject(ref object) => object.deref().as_any().is::(), _ => 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(&self) -> Option<&T> where T: NativeObject, { - use std::ops::Deref; match self.data { ObjectData::NativeObject(ref object) => object.deref().as_any().downcast_ref::(), _ => 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(&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::() @@ -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` which sets the binding +/// name and the function name to the same value +/// - From a tuple `(B: Into, N: AsRef)` 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 for FunctionBinding { + #[inline] + fn from(name: String) -> Self { + let name: RcString = name.into(); + + Self { + binding: name.clone().into(), + name, + } + } +} + +impl From for FunctionBinding { + #[inline] + fn from(name: RcString) -> Self { + Self { + binding: name.clone().into(), + name, + } + } +} + +impl From<(B, N)> for FunctionBinding +where + B: Into, + N: AsRef, +{ + #[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, + 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(&mut self, name: N) -> &mut Self + where + N: AsRef, + { + 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(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self + where + B: Into, + { + 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(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self + where + K: Into, + V: Into, + { + 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, + length: usize, + callable: bool, + constructable: bool, + inherit: Option, +} + +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(&mut self, function: NativeFunction, binding: B, length: usize) -> &mut Self + where + B: Into, + { + 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( + &mut self, + function: NativeFunction, + binding: B, + length: usize, + ) -> &mut Self + where + B: Into, + { + 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(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self + where + K: Into, + V: Into, + { + 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(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self + where + K: Into, + V: Into, + { + 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(&mut self, name: N) -> &mut Self + where + N: AsRef, + { + 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() + } +} diff --git a/boa/src/value/conversions.rs b/boa/src/value/conversions.rs index 5e47b21684..ca4dfaf143 100644 --- a/boa/src/value/conversions.rs +++ b/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) -> 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) } diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index 992ada7393..c89b8b187f 100644 --- a/boa/src/value/mod.rs +++ b/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, { 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()), } diff --git a/boa/src/value/rcstring.rs b/boa/src/value/rcstring.rs index 5d8c427d2b..7ac39ea763 100644 --- a/boa/src/value/rcstring.rs +++ b/boa/src/value/rcstring.rs @@ -33,24 +33,28 @@ impl Display for RcString { } impl PartialEq for RcString { + #[inline] fn eq(&self, other: &str) -> bool { self.as_str() == other } } impl PartialEq 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 for &str { + #[inline] fn eq(&self, other: &RcString) -> bool { *self == other.as_str() } @@ -72,6 +76,13 @@ impl Borrow for RcString { } } +impl AsRef for RcString { + #[inline] + fn as_ref(&self) -> &str { + self.as_str() + } +} + impl From for RcString { #[inline] fn from(string: String) -> Self {