From a73760ecb359da02e64703c22a45ef0caef3cdbf Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sun, 27 Sep 2020 18:59:13 +0200 Subject: [PATCH 01/19] Initial builtins refactor - Add BuiltIn trait - Add ConstructorBuilder - Add ObjectBuilder - Cache core standard objects --- boa/src/builtins/array/mod.rs | 214 ++++++--------- boa/src/builtins/bigint/mod.rs | 94 +++---- boa/src/builtins/boolean/mod.rs | 83 +++--- boa/src/builtins/console/mod.rs | 65 ++--- boa/src/builtins/date/mod.rs | 412 ++++++---------------------- boa/src/builtins/error/mod.rs | 58 ++-- boa/src/builtins/error/range.rs | 56 ++-- boa/src/builtins/error/reference.rs | 55 ++-- boa/src/builtins/error/syntax.rs | 56 ++-- boa/src/builtins/error/type.rs | 56 ++-- boa/src/builtins/function/mod.rs | 52 ++-- boa/src/builtins/global_this/mod.rs | 18 +- boa/src/builtins/infinity/mod.rs | 12 +- boa/src/builtins/json/mod.rs | 24 +- boa/src/builtins/map/mod.rs | 168 ++++++------ boa/src/builtins/math/mod.rs | 121 ++++---- boa/src/builtins/mod.rs | 315 +++++++++++++++++++-- boa/src/builtins/nan/mod.rs | 13 +- boa/src/builtins/number/mod.rs | 188 +++++-------- boa/src/builtins/object/mod.rs | 109 +++----- boa/src/builtins/regexp/mod.rs | 78 +++--- boa/src/builtins/string/mod.rs | 167 +++++------ boa/src/builtins/symbol/mod.rs | 193 +++++-------- boa/src/builtins/undefined/mod.rs | 15 +- boa/src/context.rs | 126 +++++++++ boa/src/lib.rs | 2 +- boa/src/object/mod.rs | 1 + boa/src/value/mod.rs | 86 ++---- 28 files changed, 1347 insertions(+), 1490 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index f90f3dbea62..f0186550c52 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -12,8 +12,8 @@ #[cfg(test)] mod tests; -use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ + builtins::{BuiltIn, ConstructorBuilder}, object::{ObjectData, PROTOTYPE}, property::{Attribute, Property}, value::{same_value_zero, Value}, @@ -25,17 +25,99 @@ use std::cmp::{max, min}; #[derive(Debug, Clone, Copy)] pub(crate) struct Array; +impl BuiltIn for Array { + const NAME: &'static str = "Array"; + + fn init(context: &mut Context) -> (&'static str, Value, Attribute) { + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + + 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()) + .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) + // Static Methods + .static_method(Self::is_array, "isArray", 1) + .build(); + + (Self::NAME, array, 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() @@ -45,14 +127,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) } @@ -101,52 +176,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 @@ -1090,67 +1119,4 @@ impl Array { } Ok(accumulator) } - - /// 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, - ); - - 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) - } } diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 3fa59a5162f..badc65aebbe 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}, + builtins::{BuiltIn, ConstructorBuilder}, object::ObjectData, + property::Attribute, value::{RcBigInt, Value}, BoaProfiler, Context, Result, }; @@ -40,13 +41,53 @@ 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 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, 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 +119,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 +217,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 7ccb02b72b6..cc95f4460d4 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -12,20 +12,54 @@ #[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, ConstructorBuilder}, + object::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 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, 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 +81,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 +107,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 b9504f7a9e2..bce40f1ba03 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -17,7 +17,8 @@ mod tests; use crate::{ - builtins::function::make_builtin_fn, + builtins::{BuiltIn, ObjectBuilder}, + property::Attribute, value::{display_obj, RcString, Value}, BoaProfiler, Context, Result, }; @@ -136,6 +137,37 @@ pub(crate) struct Console { groups: Vec, } +impl BuiltIn for Console { + const NAME: &'static str = "console"; + + fn init(context: &mut Context) -> (&'static str, Value, Attribute) { + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + let console = ObjectBuilder::new(context) + .static_method(Self::assert, "assert", 0) + .static_method(Self::clear, "clear", 0) + .static_method(Self::debug, "debug", 0) + .static_method(Self::error, "error", 0) + .static_method(Self::info, "info", 0) + .static_method(Self::log, "log", 0) + .static_method(Self::trace, "trace", 0) + .static_method(Self::warn, "warn", 0) + .static_method(Self::error, "exception", 0) + .static_method(Self::count, "count", 0) + .static_method(Self::count_reset, "countReset", 0) + .static_method(Self::group, "group", 0) + .static_method(Self::group, "groupCollapsed", 0) + .static_method(Self::group_end, "groupEnd", 0) + .static_method(Self::time, "time", 0) + .static_method(Self::time_log, "timeLog", 0) + .static_method(Self::time_end, "timeEnd", 0) + .static_method(Self::dir, "dir", 0) + .static_method(Self::dir, "dirxml", 0) + .build(); + + (Self::NAME, console, Self::attribute()) + } +} + impl Console { /// The name of the object. pub(crate) const NAME: &'static str = "console"; @@ -495,35 +527,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 9a6779aa903..2a1e273b766 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -2,7 +2,7 @@ mod tests; use crate::{ - builtins::function::{make_builtin_fn, make_constructor_fn}, + builtins::{BuiltIn, ConstructorBuilder}, object::ObjectData, value::{PreferredType, Value}, BoaProfiler, Context, Result, @@ -104,10 +104,85 @@ 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 init(context: &mut Context) -> (&'static str, Value, crate::property::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, Self::attribute()) + } +} +impl Date { /// The amount of arguments this function object takes. pub(crate) const LENGTH: usize = 7; @@ -243,7 +318,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 +1311,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 5321b62c7cf..b610cf60837 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}, + builtins::{BuiltIn, ConstructorBuilder}, object::ObjectData, profiler::BoaProfiler, + property::Attribute, Context, Result, Value, }; @@ -35,15 +36,37 @@ 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 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, 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)?); } @@ -74,29 +97,4 @@ impl Error { 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) - } } diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 9fea28d9a1c..8929fe3f243 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}, + builtins::{BuiltIn, ConstructorBuilder}, object::ObjectData, profiler::BoaProfiler, + property::Attribute, Context, Result, Value, }; @@ -20,15 +21,35 @@ 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 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 range_error_object = ConstructorBuilder::with_standard_object( + context, + Self::constructor, + context.standard_objects().range_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, range_error_object, 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)?); } @@ -56,29 +77,4 @@ impl RangeError { 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 144e055fc02..d93e7134b0a 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -10,24 +10,45 @@ //! [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}, + builtins::{BuiltIn, ConstructorBuilder}, object::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 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 range_error_object = ConstructorBuilder::with_standard_object( + context, + Self::constructor, + context.standard_objects().reference_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, range_error_object, 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)?); } @@ -55,28 +76,4 @@ impl ReferenceError { 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 086b558def8..51e9f93adfe 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}, + builtins::{BuiltIn, ConstructorBuilder}, object::ObjectData, profiler::BoaProfiler, + property::Attribute, Context, Result, Value, }; @@ -22,15 +23,35 @@ 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 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 range_error_object = ConstructorBuilder::with_standard_object( + context, + Self::constructor, + context.standard_objects().syntax_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, range_error_object, 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)?); } @@ -58,29 +79,4 @@ impl SyntaxError { // 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 e2e301c12f2..7c66d12cdf3 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}, + builtins::{BuiltIn, ConstructorBuilder}, object::ObjectData, + property::Attribute, BoaProfiler, Context, Result, Value, }; @@ -25,15 +26,35 @@ 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 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 range_error_object = ConstructorBuilder::with_standard_object( + context, + Self::constructor, + context.standard_objects().type_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, range_error_object, 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)?); } @@ -61,29 +82,4 @@ impl TypeError { 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 2b0d029fe77..f694e605e34 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -12,7 +12,7 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function use crate::{ - builtins::Array, + builtins::{Array, BuiltIn, ConstructorBuilder}, environment::lexical_environment::Environment, object::{Object, ObjectData, PROTOTYPE}, property::{Attribute, Property}, @@ -195,17 +195,6 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { 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. @@ -305,15 +294,36 @@ pub fn make_builtin_fn( .insert_field(name, Value::from(function)); } -/// 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; + +impl BuiltInFunctionObject { + pub const LENGTH: usize = 1; + + 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"; - let function_object = - make_constructor_fn("Function", 1, make_function, global, prototype, true, true); + fn init(context: &mut Context) -> (&'static str, Value, Attribute) { + let _timer = BoaProfiler::global().start_event("function", "init"); - ("Function", function_object) + 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, Self::attribute()) + } } diff --git a/boa/src/builtins/global_this/mod.rs b/boa/src/builtins/global_this/mod.rs index 23e45020bda..ff422727879 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,16 @@ 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 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 b1e8c26da73..cc110df8711 100644 --- a/boa/src/builtins/infinity/mod.rs +++ b/boa/src/builtins/infinity/mod.rs @@ -12,21 +12,19 @@ #[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 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/json/mod.rs b/boa/src/builtins/json/mod.rs index dae55bf357d..d376bb7929b 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -14,8 +14,8 @@ //! [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, ObjectBuilder}, + property::{Attribute, Property, PropertyKey}, BoaProfiler, Context, Result, Value, }; use serde_json::{self, Value as JSONValue}; @@ -28,9 +28,6 @@ mod tests; pub(crate) struct Json; impl Json { - /// The name of the object. - pub(crate) const NAME: &'static str = "JSON"; - /// `JSON.parse( text[, reviver] )` /// /// This `JSON` method parses a JSON string, constructing the JavaScript value or object described by the string. @@ -175,17 +172,18 @@ impl Json { Ok(Value::from(object.to_json(ctx)?.to_string())) } } +} + +impl BuiltIn for Json { + const NAME: &'static str = "JSON"; - /// Initialise the `JSON` object on the global object. - #[inline] - pub(crate) fn init(interpreter: &mut Context) -> (&'static str, Value) { - let global = interpreter.global_object(); + fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - let json = Value::new_object(Some(global)); + let mut builder = ObjectBuilder::new(context); - make_builtin_fn(Self::parse, "parse", &json, 2, interpreter); - make_builtin_fn(Self::stringify, "stringify", &json, 3, interpreter); + builder.static_method(Self::parse, "parse", 2); + builder.static_method(Self::stringify, "stringify", 3); - (Self::NAME, json) + (Self::NAME, builder.build(), Self::attribute()) } } diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 98918556c55..7a684bed3e8 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -1,7 +1,7 @@ #![allow(clippy::mutable_key_type)] -use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ + builtins::{BuiltIn, ConstructorBuilder}, object::{ObjectData, PROTOTYPE}, property::{Attribute, Property}, BoaProfiler, Context, Result, Value, @@ -15,11 +15,88 @@ 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 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, 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 { + // 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()) + } + /// Helper function to set the size property. fn set_size(this: &Value, size: usize) { let size = Property::data_descriptor( @@ -221,89 +298,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 d17769e9e04..b409bcfcf28 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -11,7 +11,11 @@ //! [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, ObjectBuilder}, + property::Attribute, + BoaProfiler, Context, Result, Value, +}; use std::f64; #[cfg(test)] @@ -22,9 +26,6 @@ mod tests; pub(crate) struct Math; impl Math { - /// The name of the object. - pub(crate) const NAME: &'static str = "Math"; - /// Get the absolute value of a number. /// /// More information: @@ -632,69 +633,61 @@ 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)); - } +impl BuiltIn for Math { + const NAME: &'static str = "Math"; - 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) { + fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - (Self::NAME, Self::create(interpreter)) + let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; + let object = ObjectBuilder::new(context) + .static_property("E", f64::consts::E, attribute) + .static_property("LN2", f64::consts::LN_2, attribute) + .static_property("LN10", f64::consts::LN_10, attribute) + .static_property("LOG2E", f64::consts::LOG2_E, attribute) + .static_property("LOG10E", f64::consts::LOG10_E, attribute) + .static_property("SQRT1_2", 0.5_f64.sqrt(), attribute) + .static_property("SQRT2", f64::consts::SQRT_2, attribute) + .static_property("PI", f64::consts::PI, attribute) + .static_method(Self::abs, "abs", 1) + .static_method(Self::acos, "acos", 1) + .static_method(Self::acosh, "acosh", 1) + .static_method(Self::asin, "asin", 1) + .static_method(Self::asinh, "asinh", 1) + .static_method(Self::atan, "atan", 1) + .static_method(Self::atanh, "atanh", 1) + .static_method(Self::atan2, "atan2", 2) + .static_method(Self::cbrt, "cbrt", 1) + .static_method(Self::ceil, "ceil", 1) + .static_method(Self::clz32, "clz32", 1) + .static_method(Self::cos, "cos", 1) + .static_method(Self::cosh, "cosh", 1) + .static_method(Self::exp, "exp", 1) + .static_method(Self::expm1, "expm1", 1) + .static_method(Self::floor, "floor", 1) + .static_method(Self::fround, "fround", 1) + .static_method(Self::hypot, "hypot", 1) + .static_method(Self::imul, "imul", 1) + .static_method(Self::log, "log", 1) + .static_method(Self::log1p, "log1p", 1) + .static_method(Self::log10, "log10", 1) + .static_method(Self::log2, "log2", 1) + .static_method(Self::max, "max", 2) + .static_method(Self::min, "min", 2) + .static_method(Self::pow, "pow", 2) + .static_method(Self::random, "random", 0) + .static_method(Self::round, "round", 1) + .static_method(Self::sign, "sign", 1) + .static_method(Self::sin, "sin", 1) + .static_method(Self::sinh, "sinh", 1) + .static_method(Self::sqrt, "sqrt", 1) + .static_method(Self::tan, "tan", 1) + .static_method(Self::tanh, "tanh", 1) + .static_method(Self::trunc, "trunc", 1) + .build(); + + (Self::NAME, object, Self::attribute()) } } diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 9e1241d84a1..303fc0fa677 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -27,6 +27,7 @@ pub(crate) use self::{ console::Console, date::Date, error::{Error, RangeError, ReferenceError, SyntaxError, TypeError}, + function::BuiltInFunctionObject, global_this::GlobalThis, infinity::Infinity, json::Json, @@ -34,54 +35,316 @@ 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::{ + builtins::function::{Function, FunctionFlags, NativeFunction}, + context::StandardConstructor, + object::{GcObject, Object, ObjectData, PROTOTYPE}, + property::{Attribute, Property, PropertyKey}, + Context, Value, +}; + +#[derive(Debug)] +pub struct ObjectBuilder<'context> { + context: &'context mut Context, + object: GcObject, +} + +impl<'context> ObjectBuilder<'context> { + pub fn new(context: &'context mut Context) -> Self { + let object = context.construct_object(); + Self { context, object } + } + + pub fn static_method( + &mut self, + function: NativeFunction, + name: &str, + length: usize, + ) -> &mut Self { + let mut function = Object::function( + Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), + self.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + function.insert_field("length", length.into()); + function.insert_field("name", name.into()); + + self.object.borrow_mut().insert_field(name, function.into()); + self + } + + 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.object.borrow_mut().insert_property(key, property); + self + } + + fn build(&mut self) -> Value { + self.object.clone().into() + } +} + +use std::string::String as StdString; + +#[allow(missing_debug_implementations)] +pub struct ConstructorBuilder<'context> { + context: &'context mut Context, + constrcutor_function: NativeFunction, + constructor_object: GcObject, + prototype: GcObject, + name: StdString, + length: usize, + callable: bool, + constructable: bool, +} + +impl<'context> ConstructorBuilder<'context> { + pub fn new(context: &'context mut Context, constructor: NativeFunction) -> Self { + let prototype = GcObject::new(Object::create( + context + .standard_objects() + .object_object() + .prototype() + .into(), + )); + let constructor_object = GcObject::new(Object::create( + context + .standard_objects() + .function_object() + .prototype() + .into(), + )); + Self { + context, + constrcutor_function: constructor, + constructor_object, + prototype, + length: 0, + name: "[Object]".to_string(), + callable: true, + constructable: true, + } + } + + pub(crate) fn with_standard_object( + context: &'context mut Context, + constructor: NativeFunction, + object: StandardConstructor, + ) -> Self { + let this = Self { + context, + constrcutor_function: constructor, + constructor_object: object.constructor, + prototype: object.prototype, + length: 0, + name: "[Object]".to_string(), + callable: true, + constructable: true, + }; + + this.prototype.borrow_mut().set_prototype_instance( + this.context + .standard_objects() + .object_object() + .prototype() + .into(), + ); + + this.constructor_object.borrow_mut().set_prototype_instance( + this.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + + this + } + + pub fn method(&mut self, function: NativeFunction, name: &str, length: usize) -> &mut Self { + let mut function = Object::function( + Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), + self.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + function.insert_field("length", length.into()); + function.insert_field("name", name.into()); + + self.prototype + .borrow_mut() + .insert_field(name, function.into()); + self + } + + pub fn static_method( + &mut self, + function: NativeFunction, + name: &str, + length: usize, + ) -> &mut Self { + let mut function = Object::function( + Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), + self.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + function.insert_field("length", length.into()); + function.insert_field("name", name.into()); + + self.constructor_object + .borrow_mut() + .insert_field(name, function.into()); + self + } + + 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_property(key, property); + self + } + + 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_property(key, property); + self + } + + pub fn length(&mut self, length: usize) -> &mut Self { + self.length = length; + self + } + + pub fn name(&mut self, name: N) -> &mut Self + where + N: Into, + { + self.name = name.into(); + self + } + + pub fn callable(&mut self, callable: bool) -> &mut Self { + self.callable = callable; + self + } + + pub fn constructable(&mut self, constructable: bool) -> &mut Self { + self.constructable = constructable; + self + } + + fn build(&mut self) -> Value { + // 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 mut name = StdString::new(); + std::mem::swap(&mut self.name, &mut name); + let name = Property::data_descriptor( + name.into(), + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); + + { + let mut constructor = self.constructor_object.borrow_mut(); + constructor.data = ObjectData::Function(function); + constructor.insert_property("length", length); + constructor.insert_property("name", name); + + constructor.insert_field(PROTOTYPE, self.prototype.clone().into()); + } + + { + let mut prototype = self.prototype.borrow_mut(); + prototype.insert_field("constructor", self.constructor_object.clone().into()); + } + + self.constructor_object.clone().into() + } +} + +pub trait BuiltIn { + /// The binding name of the property. + const NAME: &'static str; + + fn attribute() -> Attribute { + Attribute::all() + } + fn init(context: &mut Context) -> (&'static str, Value, Attribute); +} /// Initializes builtin objects and functions #[inline] -pub fn init(interpreter: &mut Context) { - let globals = [ - // The `Function` global must be initialized before other types. - function::init, - Object::init, +pub fn init(context: &mut Context) { + let globals2 = [ + // 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, + RegExp::init, Symbol::init, - Console::init, - // Global error types. Error::init, RangeError::init, ReferenceError::init, TypeError::init, SyntaxError::init, - // Global properties. - NaN::init, - Infinity::init, - GlobalThis::init, - Undefined::init, ]; - 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 global_object = if let Value::Object(global) = context.global_object() { + global.clone() + } else { + unreachable!("global object should always be an object") + }; + + for init in &globals2 { + let (name, value, attribute) = init(context); + let property = Property::data_descriptor(value, attribute); + global_object.borrow_mut().insert_property(name, property); } } diff --git a/boa/src/builtins/nan/mod.rs b/boa/src/builtins/nan/mod.rs index 1dfb55c3c5d..48b4b0cc5b2 100644 --- a/boa/src/builtins/nan/mod.rs +++ b/boa/src/builtins/nan/mod.rs @@ -13,21 +13,18 @@ #[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 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 2a92f63d10a..36a47117b07 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::{ + builtins::{BuiltIn, ConstructorBuilder}, object::ObjectData, + property::Attribute, value::{AbstractRelation, Value}, BoaProfiler, Context, Result, }; @@ -40,10 +42,63 @@ 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 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, Self::attribute()) + } +} + +impl Number { /// The amount of arguments this function object takes. pub(crate) const LENGTH: usize = 1; @@ -93,6 +148,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 +192,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 +771,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 429303797e4..b691bb0849f 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}, + builtins::{BuiltIn, ConstructorBuilder}, object::{Object as BuiltinObject, ObjectData}, - property::Property, + property::{Attribute, Property}, value::{same_value, Value}, BoaProfiler, Context, Result, }; @@ -28,15 +28,50 @@ mod tests; #[derive(Debug, Clone, Copy)] pub struct Object; +impl BuiltIn for Object { + const NAME: &'static str = "Object"; + + 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) + .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(); + + context + .standard_objects() + .object_object() + .prototype + .borrow_mut() + .set_prototype_instance(Value::null()); + + (Self::NAME, object, 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 +227,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 e21e5e2cf6f..4a5ca3f7794 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::{ + builtins::{BuiltIn, ConstructorBuilder}, + gc::{empty_trace, Finalize, Trace}, object::ObjectData, - property::Property, + 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,32 @@ pub struct RegExp { } unsafe impl Trace for RegExp { - unsafe_empty_trace!(); + empty_trace!(); +} + +impl BuiltIn for RegExp { + const NAME: &'static str = "RegExp"; + + 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, Self::attribute()) + } } impl RegExp { @@ -69,7 +93,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 +498,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 03e6b2ae995..13afab22e1e 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -12,11 +12,10 @@ #[cfg(test)] mod tests; -use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - builtins::RegExp, + builtins::{BuiltIn, ConstructorBuilder, RegExp}, object::{Object, ObjectData}, - property::Property, + property::Attribute, value::{RcString, Value}, BoaProfiler, Context, Result, }; @@ -31,40 +30,66 @@ use std::{ #[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 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 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) + .build(); + + (Self::NAME, string_object, 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) { @@ -81,6 +106,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] @@ -686,7 +726,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) } @@ -1037,13 +1077,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, @@ -1052,7 +1092,7 @@ impl String { Ok(arg.clone()) } } - None => RegExp::make_regexp( + None => RegExp::constructor( &Value::from(Object::default()), &[Value::from(""), Value::from("g")], ctx, @@ -1061,73 +1101,4 @@ impl String { RegExp::match_all(&re, this.to_string(ctx)?.to_string()) } - - /// 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 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 3eae3a8d610..419c3b34bb4 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -18,13 +18,13 @@ #[cfg(test)] mod tests; -use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - property::{Attribute, Property}, + builtins::{BuiltIn, ConstructorBuilder}, + gc::{Finalize, Trace}, + 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 +232,60 @@ 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 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, Self::attribute()) + } +} + +impl Symbol { /// The amount of arguments this function object takes. pub(crate) const LENGTH: usize = 0; @@ -249,21 +299,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 +310,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 +319,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 +350,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 76a4357901d..a8af0b62b7f 100644 --- a/boa/src/builtins/undefined/mod.rs +++ b/boa/src/builtins/undefined/mod.rs @@ -9,24 +9,21 @@ //! [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"; - /// 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/context.rs b/boa/src/context.rs index aa7b64ddbc4..b4536436b12 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -27,6 +27,123 @@ use crate::{ }; use std::result::Result as StdResult; +#[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 { + #[inline] + pub fn constructor(&self) -> GcObject { + self.constructor.clone() + } + + #[inline] + pub fn prototype(&self) -> GcObject { + self.prototype.clone() + } +} + +#[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. @@ -50,6 +167,9 @@ pub struct Context { /// Cached well known symbols well_known_symbols: WellKnownSymbols, + + /// Cached standard objects and their prototypes + standard_objects: StandardObjects, } impl Default for Context { @@ -63,6 +183,7 @@ impl Default for Context { symbol_count, console: Console::default(), well_known_symbols, + standard_objects: Default::default(), }; // Add new builtIns to Context Realm @@ -517,4 +638,9 @@ impl Context { pub fn well_known_symbols(&self) -> &WellKnownSymbols { &self.well_known_symbols } + + #[inline] + pub fn standard_objects(&self) -> &StandardObjects { + &self.standard_objects + } } diff --git a/boa/src/lib.rs b/boa/src/lib.rs index 3718825c4e5..a5b4060422b 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -46,7 +46,7 @@ pub mod realm; pub mod syntax; pub mod value; -mod context; +pub mod context; use std::result::Result as StdResult; diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index fe92ddc76c6..6de7cd8514b 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -396,6 +396,7 @@ impl Object { &self.prototype } + #[track_caller] pub fn set_prototype_instance(&mut self, prototype: Value) { assert!(prototype.is_null() || prototype.is_object()); self.prototype = prototype diff --git a/boa/src/value/mod.rs b/boa/src/value/mod.rs index cb3d53e3628..81b3a02a4a3 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( @@ -668,81 +665,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()), } From ee5a9c3a5efc20ed7be0bee49c19a991634d7d44 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 28 Sep 2020 20:50:23 +0200 Subject: [PATCH 02/19] More efficient ConstructorBuilder::build() --- boa/src/builtins/mod.rs | 55 ++++++++++++++--------------------------- 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 303fc0fa677..bf01fd85549 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -113,25 +113,11 @@ pub struct ConstructorBuilder<'context> { impl<'context> ConstructorBuilder<'context> { pub fn new(context: &'context mut Context, constructor: NativeFunction) -> Self { - let prototype = GcObject::new(Object::create( - context - .standard_objects() - .object_object() - .prototype() - .into(), - )); - let constructor_object = GcObject::new(Object::create( - context - .standard_objects() - .function_object() - .prototype() - .into(), - )); Self { context, constrcutor_function: constructor, - constructor_object, - prototype, + constructor_object: GcObject::new(Object::default()), + prototype: GcObject::new(Object::default()), length: 0, name: "[Object]".to_string(), callable: true, @@ -144,7 +130,7 @@ impl<'context> ConstructorBuilder<'context> { constructor: NativeFunction, object: StandardConstructor, ) -> Self { - let this = Self { + Self { context, constrcutor_function: constructor, constructor_object: object.constructor, @@ -153,25 +139,7 @@ impl<'context> ConstructorBuilder<'context> { name: "[Object]".to_string(), callable: true, constructable: true, - }; - - this.prototype.borrow_mut().set_prototype_instance( - this.context - .standard_objects() - .object_object() - .prototype() - .into(), - ); - - this.constructor_object.borrow_mut().set_prototype_instance( - this.context - .standard_objects() - .function_object() - .prototype() - .into(), - ); - - this + } } pub fn method(&mut self, function: NativeFunction, name: &str, length: usize) -> &mut Self { @@ -284,12 +252,27 @@ impl<'context> ConstructorBuilder<'context> { constructor.insert_property("length", length); constructor.insert_property("name", name); + constructor.set_prototype_instance( + self.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + constructor.insert_field(PROTOTYPE, self.prototype.clone().into()); } { let mut prototype = self.prototype.borrow_mut(); prototype.insert_field("constructor", self.constructor_object.clone().into()); + prototype.set_prototype_instance( + self.context + .standard_objects() + .object_object() + .prototype() + .into(), + ); } self.constructor_object.clone().into() From dbd7ecf3bade2e6c72146fa64f274197232e847c Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 28 Sep 2020 21:01:27 +0200 Subject: [PATCH 03/19] Allow to specify which prototype to inherit --- boa/src/builtins/mod.rs | 28 +++++++++++++++++++++------- boa/src/builtins/object/mod.rs | 8 +------- 2 files changed, 22 insertions(+), 14 deletions(-) diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index bf01fd85549..6183eada91c 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -109,6 +109,7 @@ pub struct ConstructorBuilder<'context> { length: usize, callable: bool, constructable: bool, + inherit: Option, } impl<'context> ConstructorBuilder<'context> { @@ -122,6 +123,7 @@ impl<'context> ConstructorBuilder<'context> { name: "[Object]".to_string(), callable: true, constructable: true, + inherit: None, } } @@ -139,6 +141,7 @@ impl<'context> ConstructorBuilder<'context> { name: "[Object]".to_string(), callable: true, constructable: true, + inherit: None, } } @@ -228,6 +231,12 @@ impl<'context> ConstructorBuilder<'context> { self } + pub fn inherit(&mut self, prototype: Value) -> &mut Self { + assert!(prototype.is_object() || prototype.is_null()); + self.inherit = Some(prototype); + self + } + fn build(&mut self) -> Value { // Create the native function let function = Function::BuiltIn( @@ -266,13 +275,18 @@ impl<'context> ConstructorBuilder<'context> { { let mut prototype = self.prototype.borrow_mut(); prototype.insert_field("constructor", self.constructor_object.clone().into()); - prototype.set_prototype_instance( - self.context - .standard_objects() - .object_object() - .prototype() - .into(), - ); + + 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().into() diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index b691bb0849f..579a0a69aa8 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -41,6 +41,7 @@ impl BuiltIn for Object { ) .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) @@ -51,13 +52,6 @@ impl BuiltIn for Object { .static_method(Self::is, "is", 2) .build(); - context - .standard_objects() - .object_object() - .prototype - .borrow_mut() - .set_prototype_instance(Value::null()); - (Self::NAME, object, Self::attribute()) } } From cbf718c731247066cad74f53095f1a753c209b8e Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 28 Sep 2020 21:18:50 +0200 Subject: [PATCH 04/19] Refactor object property insertion --- boa/src/builtins/function/mod.rs | 21 ++++++------- boa/src/builtins/mod.rs | 48 +++++++++++++++++------------- boa/src/class.rs | 40 ++++++++++++------------- boa/src/context.rs | 2 +- boa/src/object/internal_methods.rs | 22 +++++++++----- boa/src/value/conversions.rs | 4 +-- boa/src/value/mod.rs | 4 +-- 7 files changed, 78 insertions(+), 63 deletions(-) diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index f694e605e34..d1456b1a6da 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -188,7 +188,7 @@ 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; } @@ -226,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 } @@ -286,12 +287,12 @@ pub fn make_builtin_fn( .get_field("Function") .get_field("prototype"), ); - function.insert_field("length", Value::from(length)); + function.insert_property("length", Value::from(length), Attribute::all()); parent .as_object_mut() .unwrap() - .insert_field(name, Value::from(function)); + .insert_property(name, Value::from(function), Attribute::all()); } #[derive(Debug, Clone, Copy)] diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 6183eada91c..492afe28e5f 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -75,10 +75,12 @@ impl<'context> ObjectBuilder<'context> { .prototype() .into(), ); - function.insert_field("length", length.into()); - function.insert_field("name", name.into()); + function.insert_property("length", length.into(), Attribute::all()); + function.insert_property("name", name.into(), Attribute::all()); - self.object.borrow_mut().insert_field(name, function.into()); + self.object + .borrow_mut() + .insert_property(name, function.into(), Attribute::all()); self } @@ -88,7 +90,7 @@ impl<'context> ObjectBuilder<'context> { V: Into, { let property = Property::data_descriptor(value.into(), attribute); - self.object.borrow_mut().insert_property(key, property); + self.object.borrow_mut().insert(key, property); self } @@ -154,12 +156,12 @@ impl<'context> ConstructorBuilder<'context> { .prototype() .into(), ); - function.insert_field("length", length.into()); - function.insert_field("name", name.into()); + function.insert_property("length", length.into(), Attribute::all()); + function.insert_property("name", name.into(), Attribute::all()); self.prototype .borrow_mut() - .insert_field(name, function.into()); + .insert_property(name, function.into(), Attribute::all()); self } @@ -177,12 +179,14 @@ impl<'context> ConstructorBuilder<'context> { .prototype() .into(), ); - function.insert_field("length", length.into()); - function.insert_field("name", name.into()); + function.insert_property("length", length.into(), Attribute::all()); + function.insert_property("name", name.into(), Attribute::all()); - self.constructor_object - .borrow_mut() - .insert_field(name, function.into()); + self.constructor_object.borrow_mut().insert_property( + name, + function.into(), + Attribute::all(), + ); self } @@ -192,7 +196,7 @@ impl<'context> ConstructorBuilder<'context> { V: Into, { let property = Property::data_descriptor(value.into(), attribute); - self.prototype.borrow_mut().insert_property(key, property); + self.prototype.borrow_mut().insert(key, property); self } @@ -202,9 +206,7 @@ impl<'context> ConstructorBuilder<'context> { V: Into, { let property = Property::data_descriptor(value.into(), attribute); - self.constructor_object - .borrow_mut() - .insert_property(key, property); + self.constructor_object.borrow_mut().insert(key, property); self } @@ -258,8 +260,8 @@ impl<'context> ConstructorBuilder<'context> { { let mut constructor = self.constructor_object.borrow_mut(); constructor.data = ObjectData::Function(function); - constructor.insert_property("length", length); - constructor.insert_property("name", name); + constructor.insert("length", length); + constructor.insert("name", name); constructor.set_prototype_instance( self.context @@ -269,12 +271,16 @@ impl<'context> ConstructorBuilder<'context> { .into(), ); - constructor.insert_field(PROTOTYPE, self.prototype.clone().into()); + constructor.insert_property(PROTOTYPE, self.prototype.clone().into(), Attribute::all()); } { let mut prototype = self.prototype.borrow_mut(); - prototype.insert_field("constructor", self.constructor_object.clone().into()); + prototype.insert_property( + "constructor", + self.constructor_object.clone().into(), + Attribute::all(), + ); if let Some(proto) = self.inherit.take() { prototype.set_prototype_instance(proto); @@ -342,6 +348,6 @@ pub fn init(context: &mut Context) { for init in &globals2 { let (name, value, attribute) = init(context); let property = Property::data_descriptor(value, attribute); - global_object.borrow_mut().insert_property(name, property); + global_object.borrow_mut().insert(name, property); } } diff --git a/boa/src/class.rs b/boa/src/class.rs index e3d4200421a..b8c9d411e0d 100644 --- a/boa/src/class.rs +++ b/boa/src/class.rs @@ -146,23 +146,27 @@ impl<'context> ClassBuilder<'context> { T::LENGTH.into(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); - constructor.insert_property("length", length); + constructor.insert("length", length); let name = Property::data_descriptor( T::NAME.into(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); - constructor.insert_property("name", name); + constructor.insert("name", name); let constructor = GcObject::new(constructor); - prototype - .borrow_mut() - .insert_field("constructor", constructor.clone().into()); + prototype.borrow_mut().insert_property( + "constructor", + constructor.clone().into(), + Attribute::all(), + ); - constructor - .borrow_mut() - .insert_field(PROTOTYPE, prototype.clone().into()); + constructor.borrow_mut().insert_property( + PROTOTYPE, + prototype.clone().into(), + Attribute::all(), + ); Self { context, @@ -191,12 +195,12 @@ impl<'context> ClassBuilder<'context> { .get_field("prototype"), ); - function.insert_field("length", Value::from(length)); - function.insert_field("name", Value::from(name.as_str())); + function.insert_property("length", Value::from(length), Attribute::all()); + function.insert_property("name", Value::from(name.as_str()), Attribute::all()); self.prototype .borrow_mut() - .insert_field(name, Value::from(function)); + .insert_property(name, Value::from(function), Attribute::all()); } /// Add a static method to the class. @@ -215,12 +219,12 @@ impl<'context> ClassBuilder<'context> { .get_field("prototype"), ); - function.insert_field("length", Value::from(length)); - function.insert_field("name", Value::from(name.as_str())); + function.insert_property("length", Value::from(length), Attribute::all()); + function.insert_property("name", Value::from(name.as_str()), Attribute::all()); self.object .borrow_mut() - .insert_field(name, Value::from(function)); + .insert_property(name, Value::from(function), Attribute::all()); } /// Add a property to the class, with the specified attribute. @@ -235,9 +239,7 @@ impl<'context> ClassBuilder<'context> { // 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.prototype.borrow_mut().insert(key.into(), property); } /// Add a static property to the class, with the specified attribute. @@ -252,9 +254,7 @@ impl<'context> ClassBuilder<'context> { // 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.object.borrow_mut().insert(key.into(), property); } /// Return the current context. diff --git a/boa/src/context.rs b/boa/src/context.rs index b4536436b12..e292bb1d2c4 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -585,7 +585,7 @@ impl Context { self.global_object() .as_object_mut() .unwrap() - .insert_property(T::NAME, property); + .insert(T::NAME, property); Ok(()) } diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index 7401fac90b7..32089690bf7 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,23 @@ 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: Value, + attribute: Attribute, + ) -> Option where K: Into, { - self.insert_property( + self.insert( key.into(), Property::data_descriptor( value, - Attribute::WRITABLE | Attribute::ENUMERABLE | Attribute::CONFIGURABLE, + attribute + | Attribute::HAS_WRITABLE + | Attribute::HAS_ENUMERABLE + | Attribute::HAS_CONFIGURABLE, ), ) } diff --git a/boa/src/value/conversions.rs b/boa/src/value/conversions.rs index 5e47b216847..ca4dfaf1439 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 81b3a02a4a3..12a93d4a905 100644 --- a/boa/src/value/mod.rs +++ b/boa/src/value/mod.rs @@ -472,7 +472,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); } } @@ -558,7 +558,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 } From 6d47673bb9849a5077bc3463ee17c0b11882ab27 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 28 Sep 2020 21:51:42 +0200 Subject: [PATCH 05/19] Made ClassBuilder use ConstructorBuilder --- boa/src/builtins/mod.rs | 7 ++- boa/src/class.rs | 126 +++++++--------------------------------- 2 files changed, 28 insertions(+), 105 deletions(-) diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 492afe28e5f..3858e0ee443 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -239,7 +239,12 @@ impl<'context> ConstructorBuilder<'context> { self } - fn build(&mut self) -> Value { + pub fn context(&mut self) -> &'_ mut Context { + self.context + } + + // TODO: Should return GcObject + pub fn build(&mut self) -> Value { // Create the native function let function = Function::BuiltIn( self.constrcutor_function.into(), diff --git a/boa/src/class.rs b/boa/src/class.rs index b8c9d411e0d..6afcbef3504 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, ConstructorBuilder}, + object::{GcObject, NativeObject, ObjectData}, + property::{Attribute, PropertyKey}, Context, Result, Value, }; -use std::fmt::Debug; /// Native class. pub trait Class: NativeObject + Sized { @@ -106,16 +105,9 @@ impl ClassConstructor for T { } /// Class builder which allows adding methods and static methods to the class. -#[derive(Debug)] +#[allow(missing_debug_implementations)] 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> { @@ -123,60 +115,18 @@ impl<'context> ClassBuilder<'context> { 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("length", length); - - let name = Property::data_descriptor( - T::NAME.into(), - Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, - ); - constructor.insert("name", name); - - let constructor = GcObject::new(constructor); - - prototype.borrow_mut().insert_property( - "constructor", - constructor.clone().into(), - Attribute::all(), - ); - - constructor.borrow_mut().insert_property( - PROTOTYPE, - prototype.clone().into(), - Attribute::all(), - ); - - 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 + pub(crate) fn build(mut self) -> GcObject { + if let Value::Object(ref object) = self.builder.build() { + object.clone() + } else { + unreachable!() + } } /// Add a method to the class. @@ -186,21 +136,8 @@ impl<'context> ClassBuilder<'context> { where N: Into, { - 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_property("length", Value::from(length), Attribute::all()); - function.insert_property("name", Value::from(name.as_str()), Attribute::all()); - - self.prototype - .borrow_mut() - .insert_property(name, Value::from(function), Attribute::all()); + // TODO: \/ + self.builder.method(function, &name.into(), length); } /// Add a static method to the class. @@ -210,21 +147,7 @@ impl<'context> ClassBuilder<'context> { where N: Into, { - 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_property("length", Value::from(length), Attribute::all()); - function.insert_property("name", Value::from(name.as_str()), Attribute::all()); - - self.object - .borrow_mut() - .insert_property(name, Value::from(function), Attribute::all()); + self.builder.static_method(function, &name.into(), length); } /// Add a property to the class, with the specified attribute. @@ -236,10 +159,7 @@ impl<'context> ClassBuilder<'context> { 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(key.into(), property); + self.builder.property(key, value, attribute); } /// Add a static property to the class, with the specified attribute. @@ -251,14 +171,12 @@ impl<'context> ClassBuilder<'context> { 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(key.into(), property); + self.builder.static_property(key, value, attribute); } /// Return the current context. + #[inline] pub fn context(&mut self) -> &'_ mut Context { - self.context + self.builder.context() } } From 6e74e35356962e0d31080c0d27f9b5950ca55b7b Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 28 Sep 2020 21:59:51 +0200 Subject: [PATCH 06/19] Make ConstructorBuilder::build() return GcObject --- boa/src/builtins/array/mod.rs | 2 +- boa/src/builtins/bigint/mod.rs | 2 +- boa/src/builtins/boolean/mod.rs | 2 +- boa/src/builtins/date/mod.rs | 2 +- boa/src/builtins/error/mod.rs | 2 +- boa/src/builtins/error/range.rs | 2 +- boa/src/builtins/error/reference.rs | 2 +- boa/src/builtins/error/syntax.rs | 2 +- boa/src/builtins/error/type.rs | 2 +- boa/src/builtins/function/mod.rs | 2 +- boa/src/builtins/map/mod.rs | 2 +- boa/src/builtins/mod.rs | 5 ++--- boa/src/builtins/number/mod.rs | 2 +- boa/src/builtins/object/mod.rs | 2 +- boa/src/builtins/regexp/mod.rs | 2 +- boa/src/builtins/string/mod.rs | 2 +- boa/src/builtins/symbol/mod.rs | 2 +- boa/src/class.rs | 6 +----- 18 files changed, 19 insertions(+), 24 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index f0186550c52..f83d5b0bfe5 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -65,7 +65,7 @@ impl BuiltIn for Array { .static_method(Self::is_array, "isArray", 1) .build(); - (Self::NAME, array, Self::attribute()) + (Self::NAME, array.into(), Self::attribute()) } } diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index badc65aebbe..66029bd64d9 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -62,7 +62,7 @@ impl BuiltIn for BigInt { .constructable(false) .build(); - (Self::NAME, bigint_object, Self::attribute()) + (Self::NAME, bigint_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index cc95f4460d4..d4ff002f97d 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -41,7 +41,7 @@ impl BuiltIn for Boolean { .method(Self::value_of, "valueOf", 0) .build(); - (Self::NAME, boolean_object, Self::attribute()) + (Self::NAME, boolean_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index 2a1e273b766..e7e9d6da639 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -178,7 +178,7 @@ impl BuiltIn for Date { .static_method(Self::utc, "UTC", 7) .build(); - (Self::NAME, date_object, Self::attribute()) + (Self::NAME, date_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index b610cf60837..b143da26e82 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -55,7 +55,7 @@ impl BuiltIn for Error { .method(Self::to_string, "toString", 0) .build(); - (Self::NAME, error_object, Self::attribute()) + (Self::NAME, error_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 8929fe3f243..4812af24a42 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -40,7 +40,7 @@ impl BuiltIn for RangeError { .method(Self::to_string, "toString", 0) .build(); - (Self::NAME, range_error_object, Self::attribute()) + (Self::NAME, range_error_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index d93e7134b0a..307e03ab697 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -39,7 +39,7 @@ impl BuiltIn for ReferenceError { .method(Self::to_string, "toString", 0) .build(); - (Self::NAME, range_error_object, Self::attribute()) + (Self::NAME, range_error_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 51e9f93adfe..51cad6fc192 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -42,7 +42,7 @@ impl BuiltIn for SyntaxError { .method(Self::to_string, "toString", 0) .build(); - (Self::NAME, range_error_object, Self::attribute()) + (Self::NAME, range_error_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index 7c66d12cdf3..a7dec0f44b9 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -45,7 +45,7 @@ impl BuiltIn for TypeError { .method(Self::to_string, "toString", 0) .build(); - (Self::NAME, range_error_object, Self::attribute()) + (Self::NAME, range_error_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index d1456b1a6da..71c1d414aa1 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -325,6 +325,6 @@ impl BuiltIn for BuiltInFunctionObject { .length(Self::LENGTH) .build(); - (Self::NAME, function_object, Self::attribute()) + (Self::NAME, function_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 7a684bed3e8..b61cb018154 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -33,7 +33,7 @@ impl BuiltIn for Map { .callable(false) .build(); - (Self::NAME, map_object, Self::attribute()) + (Self::NAME, map_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 3858e0ee443..88cf5e1b5e5 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -243,8 +243,7 @@ impl<'context> ConstructorBuilder<'context> { self.context } - // TODO: Should return GcObject - pub fn build(&mut self) -> Value { + pub fn build(&mut self) -> GcObject { // Create the native function let function = Function::BuiltIn( self.constrcutor_function.into(), @@ -300,7 +299,7 @@ impl<'context> ConstructorBuilder<'context> { } } - self.constructor_object.clone().into() + self.constructor_object.clone() } } diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 36a47117b07..7c719bfca02 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -94,7 +94,7 @@ impl BuiltIn for Number { 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, Self::attribute()) + (Self::NAME, number_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 579a0a69aa8..b12f56beeec 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -52,7 +52,7 @@ impl BuiltIn for Object { .static_method(Self::is, "is", 2) .build(); - (Self::NAME, object, Self::attribute()) + (Self::NAME, object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 4a5ca3f7794..344fe8e43c5 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -81,7 +81,7 @@ impl BuiltIn for RegExp { // TODO: add them RegExp accessor properties - (Self::NAME, regexp_object, Self::attribute()) + (Self::NAME, regexp_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 13afab22e1e..2a4f21c7c87 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -71,7 +71,7 @@ impl BuiltIn for String { .method(Self::replace, "replace", 2) .build(); - (Self::NAME, string_object, Self::attribute()) + (Self::NAME, string_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 419c3b34bb4..8574821cf6f 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -281,7 +281,7 @@ impl BuiltIn for Symbol { .constructable(false) .build(); - (Self::NAME, symbol_object, Self::attribute()) + (Self::NAME, symbol_object.into(), Self::attribute()) } } diff --git a/boa/src/class.rs b/boa/src/class.rs index 6afcbef3504..2ae9a7afea0 100644 --- a/boa/src/class.rs +++ b/boa/src/class.rs @@ -122,11 +122,7 @@ impl<'context> ClassBuilder<'context> { } pub(crate) fn build(mut self) -> GcObject { - if let Value::Object(ref object) = self.builder.build() { - object.clone() - } else { - unreachable!() - } + self.builder.build() } /// Add a method to the class. From b8abcfca341729f91f52b8ae8e03838989d26c37 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 28 Sep 2020 22:17:06 +0200 Subject: [PATCH 07/19] Implement Debug for ClassBuilder and ConstructorBuilder --- boa/src/builtins/mod.rs | 21 ++++++++++++++++++--- boa/src/class.rs | 2 +- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 88cf5e1b5e5..7e00c750f70 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -48,6 +48,10 @@ use crate::{ property::{Attribute, Property, PropertyKey}, Context, Value, }; +use std::{ + fmt::{self, Debug}, + string::String as StdString, +}; #[derive(Debug)] pub struct ObjectBuilder<'context> { @@ -99,9 +103,6 @@ impl<'context> ObjectBuilder<'context> { } } -use std::string::String as StdString; - -#[allow(missing_debug_implementations)] pub struct ConstructorBuilder<'context> { context: &'context mut Context, constrcutor_function: NativeFunction, @@ -114,6 +115,20 @@ pub struct ConstructorBuilder<'context> { 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> { pub fn new(context: &'context mut Context, constructor: NativeFunction) -> Self { Self { diff --git a/boa/src/class.rs b/boa/src/class.rs index 2ae9a7afea0..5c5afd9dcc1 100644 --- a/boa/src/class.rs +++ b/boa/src/class.rs @@ -105,7 +105,7 @@ impl ClassConstructor for T { } /// Class builder which allows adding methods and static methods to the class. -#[allow(missing_debug_implementations)] +#[derive(Debug)] pub struct ClassBuilder<'context> { builder: ConstructorBuilder<'context>, } From 19421265bb9b8b4046578b2c3b2dcc891bc1ebdf Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 28 Sep 2020 22:55:15 +0200 Subject: [PATCH 08/19] Make ClassBuilder methods return &mut Self --- boa/src/builtins/function/mod.rs | 4 ++-- boa/src/builtins/mod.rs | 28 +++++++++++++--------------- boa/src/class.rs | 17 +++++++++++++---- boa/src/object/internal_methods.rs | 7 ++++--- 4 files changed, 32 insertions(+), 24 deletions(-) diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 71c1d414aa1..8d65b0fec6a 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -287,12 +287,12 @@ pub fn make_builtin_fn( .get_field("Function") .get_field("prototype"), ); - function.insert_property("length", Value::from(length), Attribute::all()); + function.insert_property("length", length, Attribute::all()); parent .as_object_mut() .unwrap() - .insert_property(name, Value::from(function), Attribute::all()); + .insert_property(name, function, Attribute::all()); } #[derive(Debug, Clone, Copy)] diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 7e00c750f70..2e11787cec7 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -79,12 +79,12 @@ impl<'context> ObjectBuilder<'context> { .prototype() .into(), ); - function.insert_property("length", length.into(), Attribute::all()); - function.insert_property("name", name.into(), Attribute::all()); + function.insert_property("length", length, Attribute::all()); + function.insert_property("name", name, Attribute::all()); self.object .borrow_mut() - .insert_property(name, function.into(), Attribute::all()); + .insert_property(name, function, Attribute::all()); self } @@ -171,12 +171,12 @@ impl<'context> ConstructorBuilder<'context> { .prototype() .into(), ); - function.insert_property("length", length.into(), Attribute::all()); - function.insert_property("name", name.into(), Attribute::all()); + function.insert_property("length", length, Attribute::all()); + function.insert_property("name", name, Attribute::all()); self.prototype .borrow_mut() - .insert_property(name, function.into(), Attribute::all()); + .insert_property(name, function, Attribute::all()); self } @@ -194,14 +194,12 @@ impl<'context> ConstructorBuilder<'context> { .prototype() .into(), ); - function.insert_property("length", length.into(), Attribute::all()); - function.insert_property("name", name.into(), Attribute::all()); + function.insert_property("length", length, Attribute::all()); + function.insert_property("name", name, Attribute::all()); - self.constructor_object.borrow_mut().insert_property( - name, - function.into(), - Attribute::all(), - ); + self.constructor_object + .borrow_mut() + .insert_property(name, function, Attribute::all()); self } @@ -290,14 +288,14 @@ impl<'context> ConstructorBuilder<'context> { .into(), ); - constructor.insert_property(PROTOTYPE, self.prototype.clone().into(), Attribute::all()); + constructor.insert_property(PROTOTYPE, self.prototype.clone(), Attribute::all()); } { let mut prototype = self.prototype.borrow_mut(); prototype.insert_property( "constructor", - self.constructor_object.clone().into(), + self.constructor_object.clone(), Attribute::all(), ); diff --git a/boa/src/class.rs b/boa/src/class.rs index 5c5afd9dcc1..38987569c17 100644 --- a/boa/src/class.rs +++ b/boa/src/class.rs @@ -128,46 +128,55 @@ impl<'context> ClassBuilder<'context> { /// Add a method to the class. /// /// It is added to `prototype`. - pub fn method(&mut self, name: N, length: usize, function: NativeFunction) + pub fn method(&mut self, name: N, length: usize, function: NativeFunction) -> &mut Self where N: Into, { // TODO: \/ self.builder.method(function, &name.into(), 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) + pub fn static_method( + &mut self, + name: N, + length: usize, + function: NativeFunction, + ) -> &mut Self where N: Into, { self.builder.static_method(function, &name.into(), 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, { 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, { self.builder.static_property(key, value, attribute); + self } /// Return the current context. diff --git a/boa/src/object/internal_methods.rs b/boa/src/object/internal_methods.rs index 32089690bf7..4a9f44e4ad6 100644 --- a/boa/src/object/internal_methods.rs +++ b/boa/src/object/internal_methods.rs @@ -366,19 +366,20 @@ 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_property( + pub(crate) fn insert_property( &mut self, key: K, - value: Value, + value: V, attribute: Attribute, ) -> Option where K: Into, + V: Into, { self.insert( key.into(), Property::data_descriptor( - value, + value.into(), attribute | Attribute::HAS_WRITABLE | Attribute::HAS_ENUMERABLE From 031dea845e278978361266fe0e46f3bb155bad53 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 29 Sep 2020 00:10:08 +0200 Subject: [PATCH 09/19] Moved ConstructorBuilder and ObjectBuilder --- boa/src/builtins/array/mod.rs | 4 +- boa/src/builtins/bigint/mod.rs | 4 +- boa/src/builtins/boolean/mod.rs | 4 +- boa/src/builtins/console/mod.rs | 3 +- boa/src/builtins/date/mod.rs | 4 +- boa/src/builtins/error/mod.rs | 4 +- boa/src/builtins/error/range.rs | 4 +- boa/src/builtins/error/reference.rs | 4 +- boa/src/builtins/error/syntax.rs | 4 +- boa/src/builtins/error/type.rs | 4 +- boa/src/builtins/function/mod.rs | 4 +- boa/src/builtins/json/mod.rs | 31 +-- boa/src/builtins/map/mod.rs | 4 +- boa/src/builtins/math/mod.rs | 119 ++++++------ boa/src/builtins/mod.rs | 272 +-------------------------- boa/src/builtins/number/mod.rs | 4 +- boa/src/builtins/object/mod.rs | 4 +- boa/src/builtins/regexp/mod.rs | 4 +- boa/src/builtins/string/mod.rs | 4 +- boa/src/builtins/symbol/mod.rs | 3 +- boa/src/class.rs | 4 +- boa/src/object/mod.rs | 282 +++++++++++++++++++++++++++- 22 files changed, 388 insertions(+), 386 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index f83d5b0bfe5..b9d414af88b 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -13,8 +13,8 @@ mod tests; use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::{ObjectData, PROTOTYPE}, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData, PROTOTYPE}, property::{Attribute, Property}, value::{same_value_zero, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 66029bd64d9..1445033c97e 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -13,8 +13,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, property::Attribute, value::{RcBigInt, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index d4ff002f97d..a4b5a0fbb4c 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -13,8 +13,8 @@ mod tests; use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, property::Attribute, BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index bce40f1ba03..51b30ad0702 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -17,7 +17,8 @@ mod tests; use crate::{ - builtins::{BuiltIn, ObjectBuilder}, + builtins::BuiltIn, + object::ObjectBuilder, property::Attribute, value::{display_obj, RcString, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index e7e9d6da639..7dc845e8bfe 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -2,8 +2,8 @@ mod tests; use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, value::{PreferredType, Value}, BoaProfiler, Context, Result, }; diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index b143da26e82..b7140b0895d 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -11,8 +11,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, profiler::BoaProfiler, property::Attribute, Context, Result, Value, diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 4812af24a42..b523c7fa5ff 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -10,8 +10,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RangeError use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, profiler::BoaProfiler, property::Attribute, Context, Result, Value, diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index 307e03ab697..7b9a94b417d 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -10,8 +10,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/ReferenceError use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, profiler::BoaProfiler, property::Attribute, Context, Result, Value, diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 51cad6fc192..5c2cd0007b8 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -12,8 +12,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/SyntaxError use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, profiler::BoaProfiler, property::Attribute, Context, Result, Value, diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index a7dec0f44b9..db71659dea5 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -16,8 +16,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/TypeError use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, property::Attribute, BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 8d65b0fec6a..ec75437e2d0 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, BuiltIn, ConstructorBuilder}, + 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, diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index d376bb7929b..6db8fa49d41 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -14,7 +14,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON use crate::{ - builtins::{BuiltIn, ObjectBuilder}, + builtins::BuiltIn, + object::ObjectBuilder, property::{Attribute, Property, PropertyKey}, BoaProfiler, Context, Result, Value, }; @@ -27,6 +28,20 @@ mod tests; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct Json; +impl BuiltIn for Json { + const NAME: &'static str = "JSON"; + + fn init(context: &mut Context) -> (&'static str, Value, Attribute) { + let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); + let mut builder = ObjectBuilder::new(context); + + builder.static_method(Self::parse, "parse", 2); + builder.static_method(Self::stringify, "stringify", 3); + + (Self::NAME, builder.build(), Self::attribute()) + } +} + impl Json { /// `JSON.parse( text[, reviver] )` /// @@ -173,17 +188,3 @@ impl Json { } } } - -impl BuiltIn for Json { - const NAME: &'static str = "JSON"; - - fn init(context: &mut Context) -> (&'static str, Value, Attribute) { - let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - let mut builder = ObjectBuilder::new(context); - - builder.static_method(Self::parse, "parse", 2); - builder.static_method(Self::stringify, "stringify", 3); - - (Self::NAME, builder.build(), Self::attribute()) - } -} diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index b61cb018154..7c5b4a7c855 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 crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::{ObjectData, PROTOTYPE}, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData, PROTOTYPE}, property::{Attribute, Property}, BoaProfiler, Context, Result, Value, }; diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index b409bcfcf28..0fc0857654a 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -12,9 +12,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math use crate::{ - builtins::{BuiltIn, ObjectBuilder}, - property::Attribute, - BoaProfiler, Context, Result, Value, + builtins::BuiltIn, object::ObjectBuilder, property::Attribute, BoaProfiler, Context, Result, + Value, }; use std::f64; @@ -25,6 +24,63 @@ mod tests; #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct Math; +impl BuiltIn for Math { + const NAME: &'static str = "Math"; + + 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 = ObjectBuilder::new(context) + .static_property("E", f64::consts::E, attribute) + .static_property("LN2", f64::consts::LN_2, attribute) + .static_property("LN10", f64::consts::LN_10, attribute) + .static_property("LOG2E", f64::consts::LOG2_E, attribute) + .static_property("LOG10E", f64::consts::LOG10_E, attribute) + .static_property("SQRT1_2", 0.5_f64.sqrt(), attribute) + .static_property("SQRT2", f64::consts::SQRT_2, attribute) + .static_property("PI", f64::consts::PI, attribute) + .static_method(Self::abs, "abs", 1) + .static_method(Self::acos, "acos", 1) + .static_method(Self::acosh, "acosh", 1) + .static_method(Self::asin, "asin", 1) + .static_method(Self::asinh, "asinh", 1) + .static_method(Self::atan, "atan", 1) + .static_method(Self::atanh, "atanh", 1) + .static_method(Self::atan2, "atan2", 2) + .static_method(Self::cbrt, "cbrt", 1) + .static_method(Self::ceil, "ceil", 1) + .static_method(Self::clz32, "clz32", 1) + .static_method(Self::cos, "cos", 1) + .static_method(Self::cosh, "cosh", 1) + .static_method(Self::exp, "exp", 1) + .static_method(Self::expm1, "expm1", 1) + .static_method(Self::floor, "floor", 1) + .static_method(Self::fround, "fround", 1) + .static_method(Self::hypot, "hypot", 1) + .static_method(Self::imul, "imul", 1) + .static_method(Self::log, "log", 1) + .static_method(Self::log1p, "log1p", 1) + .static_method(Self::log10, "log10", 1) + .static_method(Self::log2, "log2", 1) + .static_method(Self::max, "max", 2) + .static_method(Self::min, "min", 2) + .static_method(Self::pow, "pow", 2) + .static_method(Self::random, "random", 0) + .static_method(Self::round, "round", 1) + .static_method(Self::sign, "sign", 1) + .static_method(Self::sin, "sin", 1) + .static_method(Self::sinh, "sinh", 1) + .static_method(Self::sqrt, "sqrt", 1) + .static_method(Self::tan, "tan", 1) + .static_method(Self::tanh, "tanh", 1) + .static_method(Self::trunc, "trunc", 1) + .build(); + + (Self::NAME, object, Self::attribute()) + } +} + impl Math { /// Get the absolute value of a number. /// @@ -634,60 +690,3 @@ impl Math { .into()) } } - -impl BuiltIn for Math { - const NAME: &'static str = "Math"; - - 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 = ObjectBuilder::new(context) - .static_property("E", f64::consts::E, attribute) - .static_property("LN2", f64::consts::LN_2, attribute) - .static_property("LN10", f64::consts::LN_10, attribute) - .static_property("LOG2E", f64::consts::LOG2_E, attribute) - .static_property("LOG10E", f64::consts::LOG10_E, attribute) - .static_property("SQRT1_2", 0.5_f64.sqrt(), attribute) - .static_property("SQRT2", f64::consts::SQRT_2, attribute) - .static_property("PI", f64::consts::PI, attribute) - .static_method(Self::abs, "abs", 1) - .static_method(Self::acos, "acos", 1) - .static_method(Self::acosh, "acosh", 1) - .static_method(Self::asin, "asin", 1) - .static_method(Self::asinh, "asinh", 1) - .static_method(Self::atan, "atan", 1) - .static_method(Self::atanh, "atanh", 1) - .static_method(Self::atan2, "atan2", 2) - .static_method(Self::cbrt, "cbrt", 1) - .static_method(Self::ceil, "ceil", 1) - .static_method(Self::clz32, "clz32", 1) - .static_method(Self::cos, "cos", 1) - .static_method(Self::cosh, "cosh", 1) - .static_method(Self::exp, "exp", 1) - .static_method(Self::expm1, "expm1", 1) - .static_method(Self::floor, "floor", 1) - .static_method(Self::fround, "fround", 1) - .static_method(Self::hypot, "hypot", 1) - .static_method(Self::imul, "imul", 1) - .static_method(Self::log, "log", 1) - .static_method(Self::log1p, "log1p", 1) - .static_method(Self::log10, "log10", 1) - .static_method(Self::log2, "log2", 1) - .static_method(Self::max, "max", 2) - .static_method(Self::min, "min", 2) - .static_method(Self::pow, "pow", 2) - .static_method(Self::random, "random", 0) - .static_method(Self::round, "round", 1) - .static_method(Self::sign, "sign", 1) - .static_method(Self::sin, "sin", 1) - .static_method(Self::sinh, "sinh", 1) - .static_method(Self::sqrt, "sqrt", 1) - .static_method(Self::tan, "tan", 1) - .static_method(Self::tanh, "tanh", 1) - .static_method(Self::trunc, "trunc", 1) - .build(); - - (Self::NAME, object, Self::attribute()) - } -} diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 2e11787cec7..40c6fe83d8d 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -42,279 +42,9 @@ pub(crate) use self::{ undefined::Undefined, }; use crate::{ - builtins::function::{Function, FunctionFlags, NativeFunction}, - context::StandardConstructor, - object::{GcObject, Object, ObjectData, PROTOTYPE}, - property::{Attribute, Property, PropertyKey}, + property::{Attribute, Property}, Context, Value, }; -use std::{ - fmt::{self, Debug}, - string::String as StdString, -}; - -#[derive(Debug)] -pub struct ObjectBuilder<'context> { - context: &'context mut Context, - object: GcObject, -} - -impl<'context> ObjectBuilder<'context> { - pub fn new(context: &'context mut Context) -> Self { - let object = context.construct_object(); - Self { context, object } - } - - pub fn static_method( - &mut self, - function: NativeFunction, - name: &str, - length: usize, - ) -> &mut Self { - let mut function = Object::function( - Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), - self.context - .standard_objects() - .function_object() - .prototype() - .into(), - ); - function.insert_property("length", length, Attribute::all()); - function.insert_property("name", name, Attribute::all()); - - self.object - .borrow_mut() - .insert_property(name, function, Attribute::all()); - self - } - - 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.object.borrow_mut().insert(key, property); - self - } - - fn build(&mut self) -> Value { - self.object.clone().into() - } -} - -pub struct ConstructorBuilder<'context> { - context: &'context mut Context, - constrcutor_function: NativeFunction, - constructor_object: GcObject, - prototype: GcObject, - name: StdString, - 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> { - 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: "[Object]".to_string(), - callable: true, - constructable: true, - inherit: None, - } - } - - 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: "[Object]".to_string(), - callable: true, - constructable: true, - inherit: None, - } - } - - pub fn method(&mut self, function: NativeFunction, name: &str, length: usize) -> &mut Self { - let mut function = Object::function( - Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), - self.context - .standard_objects() - .function_object() - .prototype() - .into(), - ); - function.insert_property("length", length, Attribute::all()); - function.insert_property("name", name, Attribute::all()); - - self.prototype - .borrow_mut() - .insert_property(name, function, Attribute::all()); - self - } - - pub fn static_method( - &mut self, - function: NativeFunction, - name: &str, - length: usize, - ) -> &mut Self { - let mut function = Object::function( - Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), - self.context - .standard_objects() - .function_object() - .prototype() - .into(), - ); - function.insert_property("length", length, Attribute::all()); - function.insert_property("name", name, Attribute::all()); - - self.constructor_object - .borrow_mut() - .insert_property(name, function, Attribute::all()); - self - } - - 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 - } - - 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 - } - - pub fn length(&mut self, length: usize) -> &mut Self { - self.length = length; - self - } - - pub fn name(&mut self, name: N) -> &mut Self - where - N: Into, - { - self.name = name.into(); - self - } - - pub fn callable(&mut self, callable: bool) -> &mut Self { - self.callable = callable; - self - } - - pub fn constructable(&mut self, constructable: bool) -> &mut Self { - self.constructable = constructable; - self - } - - pub fn inherit(&mut self, prototype: Value) -> &mut Self { - assert!(prototype.is_object() || prototype.is_null()); - self.inherit = Some(prototype); - self - } - - pub fn context(&mut self) -> &'_ mut Context { - self.context - } - - 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 mut name = StdString::new(); - std::mem::swap(&mut self.name, &mut name); - let name = Property::data_descriptor( - name.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::all()); - } - - { - let mut prototype = self.prototype.borrow_mut(); - prototype.insert_property( - "constructor", - self.constructor_object.clone(), - Attribute::all(), - ); - - 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() - } -} pub trait BuiltIn { /// The binding name of the property. diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 7c719bfca02..ca67a205204 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -15,8 +15,8 @@ use super::function::make_builtin_fn; use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::ObjectData, + builtins::BuiltIn, + object::{ConstructorBuilder, ObjectData}, property::Attribute, value::{AbstractRelation, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index b12f56beeec..532142692fd 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -14,8 +14,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, - object::{Object as BuiltinObject, ObjectData}, + builtins::BuiltIn, + object::{ConstructorBuilder, Object as BuiltinObject, ObjectData}, property::{Attribute, Property}, value::{same_value, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 344fe8e43c5..ba88eb03e2b 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -10,9 +10,9 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, + builtins::BuiltIn, gc::{empty_trace, Finalize, Trace}, - object::ObjectData, + object::{ConstructorBuilder, ObjectData}, property::{Attribute, Property}, value::{RcString, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 2a4f21c7c87..2d3a476084f 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -13,8 +13,8 @@ mod tests; use crate::{ - builtins::{BuiltIn, ConstructorBuilder, RegExp}, - object::{Object, ObjectData}, + builtins::{BuiltIn, RegExp}, + object::{ConstructorBuilder, Object, ObjectData}, property::Attribute, value::{RcString, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 8574821cf6f..2c7ea5cbe8f 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -19,8 +19,9 @@ mod tests; use crate::{ - builtins::{BuiltIn, ConstructorBuilder}, + builtins::BuiltIn, gc::{Finalize, Trace}, + object::ConstructorBuilder, property::Attribute, value::{RcString, RcSymbol, Value}, BoaProfiler, Context, Result, diff --git a/boa/src/class.rs b/boa/src/class.rs index 38987569c17..190561cc10a 100644 --- a/boa/src/class.rs +++ b/boa/src/class.rs @@ -61,8 +61,8 @@ //! [class-trait]: ./trait.Class.html use crate::{ - builtins::{function::NativeFunction, ConstructorBuilder}, - object::{GcObject, NativeObject, ObjectData}, + builtins::function::NativeFunction, + object::{ConstructorBuilder, GcObject, NativeObject, ObjectData}, property::{Attribute, PropertyKey}, Context, Result, Value, }; diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 6de7cd8514b..5e30edaa1e9 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -1,15 +1,22 @@ //! This module implements the Rust representation of a JavaScript object. use crate::{ - builtins::{function::Function, map::ordered_map::OrderedMap, BigInt, Date, RegExp}, - property::{Property, PropertyKey}, + builtins::{ + function::{Function, FunctionFlags, NativeFunction}, + map::ordered_map::OrderedMap, + BigInt, Date, RegExp, + }, + context::StandardConstructor, + 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}, +}; mod gcobject; mod internal_methods; @@ -78,7 +85,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, "{}", @@ -456,3 +463,266 @@ impl Object { } } } + +#[derive(Debug)] +pub struct ObjectBuilder<'context> { + context: &'context mut Context, + object: GcObject, +} + +impl<'context> ObjectBuilder<'context> { + pub fn new(context: &'context mut Context) -> Self { + let object = context.construct_object(); + Self { context, object } + } + + pub fn static_method( + &mut self, + function: NativeFunction, + name: &str, + length: usize, + ) -> &mut Self { + let mut function = Object::function( + Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), + self.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + function.insert_property("length", length, Attribute::all()); + function.insert_property("name", name, Attribute::all()); + + self.object + .borrow_mut() + .insert_property(name, function, Attribute::all()); + self + } + + 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.object.borrow_mut().insert(key, property); + self + } + + pub fn build(&mut self) -> Value { + self.object.clone().into() + } +} + +pub struct ConstructorBuilder<'context> { + context: &'context mut Context, + constrcutor_function: NativeFunction, + constructor_object: GcObject, + prototype: GcObject, + name: String, + 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> { + 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: "[Object]".to_string(), + callable: true, + constructable: true, + inherit: None, + } + } + + 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: "[Object]".to_string(), + callable: true, + constructable: true, + inherit: None, + } + } + + pub fn method(&mut self, function: NativeFunction, name: &str, length: usize) -> &mut Self { + let mut function = Object::function( + Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), + self.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + function.insert_property("length", length, Attribute::all()); + function.insert_property("name", name, Attribute::all()); + + self.prototype + .borrow_mut() + .insert_property(name, function, Attribute::all()); + self + } + + pub fn static_method( + &mut self, + function: NativeFunction, + name: &str, + length: usize, + ) -> &mut Self { + let mut function = Object::function( + Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), + self.context + .standard_objects() + .function_object() + .prototype() + .into(), + ); + function.insert_property("length", length, Attribute::all()); + function.insert_property("name", name, Attribute::all()); + + self.constructor_object + .borrow_mut() + .insert_property(name, function, Attribute::all()); + self + } + + 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 + } + + 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 + } + + pub fn length(&mut self, length: usize) -> &mut Self { + self.length = length; + self + } + + pub fn name(&mut self, name: N) -> &mut Self + where + N: Into, + { + self.name = name.into(); + self + } + + pub fn callable(&mut self, callable: bool) -> &mut Self { + self.callable = callable; + self + } + + pub fn constructable(&mut self, constructable: bool) -> &mut Self { + self.constructable = constructable; + self + } + + pub fn inherit(&mut self, prototype: Value) -> &mut Self { + assert!(prototype.is_object() || prototype.is_null()); + self.inherit = Some(prototype); + self + } + + pub fn context(&mut self) -> &'_ mut Context { + self.context + } + + 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 mut name = String::new(); + std::mem::swap(&mut self.name, &mut name); + let name = Property::data_descriptor( + name.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::all()); + } + + { + let mut prototype = self.prototype.borrow_mut(); + prototype.insert_property( + "constructor", + self.constructor_object.clone(), + Attribute::all(), + ); + + 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() + } +} From 451bb30bd7e77f2ea871e40f4b8481f47de5c90f Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 29 Sep 2020 00:25:18 +0200 Subject: [PATCH 10/19] Make ObjectBuilder::build() return a GcObject --- boa/src/builtins/console/mod.rs | 2 +- boa/src/builtins/json/mod.rs | 9 +++++---- boa/src/builtins/math/mod.rs | 2 +- boa/src/object/mod.rs | 4 ++-- 4 files changed, 9 insertions(+), 8 deletions(-) diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 51b30ad0702..f3703dac8e2 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -165,7 +165,7 @@ impl BuiltIn for Console { .static_method(Self::dir, "dirxml", 0) .build(); - (Self::NAME, console, Self::attribute()) + (Self::NAME, console.into(), Self::attribute()) } } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 6db8fa49d41..ec4afd2221f 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -33,12 +33,13 @@ impl BuiltIn for Json { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - let mut builder = ObjectBuilder::new(context); - builder.static_method(Self::parse, "parse", 2); - builder.static_method(Self::stringify, "stringify", 3); + let json_object = ObjectBuilder::new(context) + .static_method(Self::parse, "parse", 2) + .static_method(Self::stringify, "stringify", 3) + .build(); - (Self::NAME, builder.build(), Self::attribute()) + (Self::NAME, json_object.into(), Self::attribute()) } } diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index 0fc0857654a..1e618ba153e 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -77,7 +77,7 @@ impl BuiltIn for Math { .static_method(Self::trunc, "trunc", 1) .build(); - (Self::NAME, object, Self::attribute()) + (Self::NAME, object.into(), Self::attribute()) } } diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 5e30edaa1e9..49000daabd8 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -509,8 +509,8 @@ impl<'context> ObjectBuilder<'context> { self } - pub fn build(&mut self) -> Value { - self.object.clone().into() + pub fn build(&mut self) -> GcObject { + self.object.clone() } } From 72b0fdbf8a1644b4ba3be4280ef0f23ac22a4956 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 29 Sep 2020 00:43:43 +0200 Subject: [PATCH 11/19] Fixed global objects/properies attributes --- boa/src/builtins/array/mod.rs | 4 ++++ boa/src/builtins/bigint/mod.rs | 4 ++++ boa/src/builtins/boolean/mod.rs | 4 ++++ boa/src/builtins/console/mod.rs | 4 ++++ boa/src/builtins/date/mod.rs | 7 ++++++- boa/src/builtins/error/mod.rs | 4 ++++ boa/src/builtins/error/range.rs | 4 ++++ boa/src/builtins/error/reference.rs | 4 ++++ boa/src/builtins/error/syntax.rs | 4 ++++ boa/src/builtins/error/type.rs | 4 ++++ boa/src/builtins/function/mod.rs | 4 ++++ boa/src/builtins/global_this/mod.rs | 4 ++++ boa/src/builtins/infinity/mod.rs | 5 ++++- boa/src/builtins/json/mod.rs | 4 ++++ boa/src/builtins/map/mod.rs | 4 ++++ boa/src/builtins/math/mod.rs | 4 ++++ boa/src/builtins/mod.rs | 4 +--- boa/src/builtins/nan/mod.rs | 4 ++++ boa/src/builtins/number/mod.rs | 4 ++++ boa/src/builtins/object/mod.rs | 4 ++++ boa/src/builtins/regexp/mod.rs | 4 ++++ boa/src/builtins/string/mod.rs | 4 ++++ boa/src/builtins/symbol/mod.rs | 4 ++++ boa/src/builtins/undefined/mod.rs | 4 ++++ 24 files changed, 95 insertions(+), 5 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index b9d414af88b..944818bcef6 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -28,6 +28,10 @@ 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"); diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 1445033c97e..82d2c629034 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -44,6 +44,10 @@ pub struct BigInt(num_bigint::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"); diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index a4b5a0fbb4c..2a1357c0265 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -27,6 +27,10 @@ impl BuiltIn for Boolean { /// The name of the object. 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"); diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index f3703dac8e2..2e503099501 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -141,6 +141,10 @@ pub(crate) struct Console { 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 = ObjectBuilder::new(context) diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index 7dc845e8bfe..f604f6a4933 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -4,6 +4,7 @@ mod tests; use crate::{ builtins::BuiltIn, object::{ConstructorBuilder, ObjectData}, + property::Attribute, value::{PreferredType, Value}, BoaProfiler, Context, Result, }; @@ -107,7 +108,11 @@ impl Default for Date { impl BuiltIn for Date { const NAME: &'static str = "Date"; - fn init(context: &mut Context) -> (&'static str, Value, crate::property::Attribute) { + 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) diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index b7140b0895d..6d5d4c15f52 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -39,6 +39,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index b523c7fa5ff..8766e9f3c76 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -24,6 +24,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index 7b9a94b417d..ce392f6cb3e 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -23,6 +23,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 5c2cd0007b8..3d47dcb2241 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -26,6 +26,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index db71659dea5..306fdedb74a 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -29,6 +29,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index ec75437e2d0..de6278a1470 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -313,6 +313,10 @@ impl BuiltInFunctionObject { 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"); diff --git a/boa/src/builtins/global_this/mod.rs b/boa/src/builtins/global_this/mod.rs index ff422727879..42822e40e95 100644 --- a/boa/src/builtins/global_this/mod.rs +++ b/boa/src/builtins/global_this/mod.rs @@ -21,6 +21,10 @@ pub(crate) struct GlobalThis; impl BuiltIn for GlobalThis { const NAME: &'static str = "globalThis"; + 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"); diff --git a/boa/src/builtins/infinity/mod.rs b/boa/src/builtins/infinity/mod.rs index cc110df8711..8691ddc8c7c 100644 --- a/boa/src/builtins/infinity/mod.rs +++ b/boa/src/builtins/infinity/mod.rs @@ -21,7 +21,10 @@ pub(crate) struct Infinity; impl BuiltIn for Infinity { const NAME: &'static str = "Infinity"; - /// Initialize the `Infinity` property on the global object. + 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"); diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index ec4afd2221f..53fb83b3e24 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -31,6 +31,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index 7c5b4a7c855..9eeee6e0abb 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -18,6 +18,10 @@ pub(crate) struct Map(OrderedMap); 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"); diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index 1e618ba153e..5d4cef90cfa 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -27,6 +27,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 40c6fe83d8d..c9195ccef80 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -50,9 +50,7 @@ pub trait BuiltIn { /// The binding name of the property. const NAME: &'static str; - fn attribute() -> Attribute { - Attribute::all() - } + fn attribute() -> Attribute; fn init(context: &mut Context) -> (&'static str, Value, Attribute); } diff --git a/boa/src/builtins/nan/mod.rs b/boa/src/builtins/nan/mod.rs index 48b4b0cc5b2..5c5b4194514 100644 --- a/boa/src/builtins/nan/mod.rs +++ b/boa/src/builtins/nan/mod.rs @@ -22,6 +22,10 @@ pub(crate) struct NaN; impl BuiltIn for NaN { const NAME: &'static str = "NaN"; + 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"); diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index ca67a205204..4d5cb218bc7 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -45,6 +45,10 @@ const PARSE_FLOAT_MAX_ARG_COUNT: usize = 1; 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"); diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 532142692fd..173df33f4fc 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -31,6 +31,10 @@ 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"); diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index ba88eb03e2b..327c0997aea 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -63,6 +63,10 @@ unsafe impl Trace for RegExp { 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"); diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 2d3a476084f..0ce525e153f 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -33,6 +33,10 @@ pub(crate) struct 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"); diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 2c7ea5cbe8f..04dd9f08ce7 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -236,6 +236,10 @@ impl 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(); diff --git a/boa/src/builtins/undefined/mod.rs b/boa/src/builtins/undefined/mod.rs index a8af0b62b7f..4f389a1e6c2 100644 --- a/boa/src/builtins/undefined/mod.rs +++ b/boa/src/builtins/undefined/mod.rs @@ -21,6 +21,10 @@ pub(crate) struct Undefined; impl BuiltIn for Undefined { const NAME: &'static str = "undefined"; + 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"); From 24b3da40110fb85302ac3aafa75d0e54368e0020 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 29 Sep 2020 01:03:38 +0200 Subject: [PATCH 12/19] Fix docs --- boa/src/builtins/mod.rs | 2 +- boa/src/context.rs | 6 +++--- boa/src/lib.rs | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index c9195ccef80..3333b65d340 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -46,7 +46,7 @@ use crate::{ Context, Value, }; -pub trait BuiltIn { +pub(crate) trait BuiltIn { /// The binding name of the property. const NAME: &'static str; diff --git a/boa/src/context.rs b/boa/src/context.rs index e292bb1d2c4..be40cbfe14f 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -146,9 +146,9 @@ impl StandardObjects { /// 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 in the same thread. #[derive(Debug)] pub struct Context { /// realm holds both the global object and the environment diff --git a/boa/src/lib.rs b/boa/src/lib.rs index a5b4060422b..c432e6c85f7 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -53,6 +53,7 @@ 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::{ From 938ef2790f851ce8223280c4afbac9c055fcd72a Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 30 Sep 2020 05:37:09 +0200 Subject: [PATCH 13/19] Fixed function prototype and attributes --- boa/src/object/mod.rs | 68 ++++++++++++++++++++++++++----------------- 1 file changed, 41 insertions(+), 27 deletions(-) diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 49000daabd8..3e850e208d7 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -490,12 +490,15 @@ impl<'context> ObjectBuilder<'context> { .prototype() .into(), ); - function.insert_property("length", length, Attribute::all()); - function.insert_property("name", name, Attribute::all()); - - self.object - .borrow_mut() - .insert_property(name, function, Attribute::all()); + let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; + function.insert_property("length", length, attribute); + function.insert_property("name", name, attribute); + + self.object.borrow_mut().insert_property( + name, + function, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ); self } @@ -519,7 +522,7 @@ pub struct ConstructorBuilder<'context> { constrcutor_function: NativeFunction, constructor_object: GcObject, prototype: GcObject, - name: String, + name: Option, length: usize, callable: bool, constructable: bool, @@ -548,7 +551,7 @@ impl<'context> ConstructorBuilder<'context> { constructor_object: GcObject::new(Object::default()), prototype: GcObject::new(Object::default()), length: 0, - name: "[Object]".to_string(), + name: None, callable: true, constructable: true, inherit: None, @@ -566,7 +569,7 @@ impl<'context> ConstructorBuilder<'context> { constructor_object: object.constructor, prototype: object.prototype, length: 0, - name: "[Object]".to_string(), + name: None, callable: true, constructable: true, inherit: None, @@ -582,12 +585,15 @@ impl<'context> ConstructorBuilder<'context> { .prototype() .into(), ); - function.insert_property("length", length, Attribute::all()); - function.insert_property("name", name, Attribute::all()); - - self.prototype - .borrow_mut() - .insert_property(name, function, Attribute::all()); + let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; + function.insert_property("length", length, attribute); + function.insert_property("name", name, attribute); + + self.prototype.borrow_mut().insert_property( + name, + function, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ); self } @@ -605,12 +611,15 @@ impl<'context> ConstructorBuilder<'context> { .prototype() .into(), ); - function.insert_property("length", length, Attribute::all()); - function.insert_property("name", name, Attribute::all()); - - self.constructor_object - .borrow_mut() - .insert_property(name, function, Attribute::all()); + let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; + function.insert_property("length", length, attribute); + function.insert_property("name", name, attribute); + + self.constructor_object.borrow_mut().insert_property( + name, + function, + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, + ); self } @@ -643,7 +652,7 @@ impl<'context> ConstructorBuilder<'context> { where N: Into, { - self.name = name.into(); + self.name = Some(name.into()); self } @@ -678,10 +687,11 @@ impl<'context> ConstructorBuilder<'context> { self.length.into(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); - let mut name = String::new(); - std::mem::swap(&mut self.name, &mut name); let name = Property::data_descriptor( - name.into(), + self.name + .take() + .unwrap_or_else(|| String::from("[object]")) + .into(), Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, ); @@ -699,7 +709,11 @@ impl<'context> ConstructorBuilder<'context> { .into(), ); - constructor.insert_property(PROTOTYPE, self.prototype.clone(), Attribute::all()); + constructor.insert_property( + PROTOTYPE, + self.prototype.clone(), + Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT, + ); } { @@ -707,7 +721,7 @@ impl<'context> ConstructorBuilder<'context> { prototype.insert_property( "constructor", self.constructor_object.clone(), - Attribute::all(), + Attribute::WRITABLE | Attribute::NON_ENUMERABLE | Attribute::CONFIGURABLE, ); if let Some(proto) = self.inherit.take() { From 23105e07dbabd0e44d341b738922048882fde662 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 30 Sep 2020 05:40:02 +0200 Subject: [PATCH 14/19] Rename some ObjectBuilder methods --- boa/src/builtins/console/mod.rs | 38 +++++++-------- boa/src/builtins/json/mod.rs | 4 +- boa/src/builtins/math/mod.rs | 86 ++++++++++++++++----------------- boa/src/object/mod.rs | 9 +--- 4 files changed, 66 insertions(+), 71 deletions(-) diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 2e503099501..d1b07000da6 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -148,25 +148,25 @@ impl BuiltIn for Console { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let console = ObjectBuilder::new(context) - .static_method(Self::assert, "assert", 0) - .static_method(Self::clear, "clear", 0) - .static_method(Self::debug, "debug", 0) - .static_method(Self::error, "error", 0) - .static_method(Self::info, "info", 0) - .static_method(Self::log, "log", 0) - .static_method(Self::trace, "trace", 0) - .static_method(Self::warn, "warn", 0) - .static_method(Self::error, "exception", 0) - .static_method(Self::count, "count", 0) - .static_method(Self::count_reset, "countReset", 0) - .static_method(Self::group, "group", 0) - .static_method(Self::group, "groupCollapsed", 0) - .static_method(Self::group_end, "groupEnd", 0) - .static_method(Self::time, "time", 0) - .static_method(Self::time_log, "timeLog", 0) - .static_method(Self::time_end, "timeEnd", 0) - .static_method(Self::dir, "dir", 0) - .static_method(Self::dir, "dirxml", 0) + .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()) diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 53fb83b3e24..f55eff57f6c 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -39,8 +39,8 @@ impl BuiltIn for Json { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let json_object = ObjectBuilder::new(context) - .static_method(Self::parse, "parse", 2) - .static_method(Self::stringify, "stringify", 3) + .function(Self::parse, "parse", 2) + .function(Self::stringify, "stringify", 3) .build(); (Self::NAME, json_object.into(), Self::attribute()) diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index 5d4cef90cfa..d2a0cefedb0 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -36,49 +36,49 @@ impl BuiltIn for Math { let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; let object = ObjectBuilder::new(context) - .static_property("E", f64::consts::E, attribute) - .static_property("LN2", f64::consts::LN_2, attribute) - .static_property("LN10", f64::consts::LN_10, attribute) - .static_property("LOG2E", f64::consts::LOG2_E, attribute) - .static_property("LOG10E", f64::consts::LOG10_E, attribute) - .static_property("SQRT1_2", 0.5_f64.sqrt(), attribute) - .static_property("SQRT2", f64::consts::SQRT_2, attribute) - .static_property("PI", f64::consts::PI, attribute) - .static_method(Self::abs, "abs", 1) - .static_method(Self::acos, "acos", 1) - .static_method(Self::acosh, "acosh", 1) - .static_method(Self::asin, "asin", 1) - .static_method(Self::asinh, "asinh", 1) - .static_method(Self::atan, "atan", 1) - .static_method(Self::atanh, "atanh", 1) - .static_method(Self::atan2, "atan2", 2) - .static_method(Self::cbrt, "cbrt", 1) - .static_method(Self::ceil, "ceil", 1) - .static_method(Self::clz32, "clz32", 1) - .static_method(Self::cos, "cos", 1) - .static_method(Self::cosh, "cosh", 1) - .static_method(Self::exp, "exp", 1) - .static_method(Self::expm1, "expm1", 1) - .static_method(Self::floor, "floor", 1) - .static_method(Self::fround, "fround", 1) - .static_method(Self::hypot, "hypot", 1) - .static_method(Self::imul, "imul", 1) - .static_method(Self::log, "log", 1) - .static_method(Self::log1p, "log1p", 1) - .static_method(Self::log10, "log10", 1) - .static_method(Self::log2, "log2", 1) - .static_method(Self::max, "max", 2) - .static_method(Self::min, "min", 2) - .static_method(Self::pow, "pow", 2) - .static_method(Self::random, "random", 0) - .static_method(Self::round, "round", 1) - .static_method(Self::sign, "sign", 1) - .static_method(Self::sin, "sin", 1) - .static_method(Self::sinh, "sinh", 1) - .static_method(Self::sqrt, "sqrt", 1) - .static_method(Self::tan, "tan", 1) - .static_method(Self::tanh, "tanh", 1) - .static_method(Self::trunc, "trunc", 1) + .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()) diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 3e850e208d7..9bb9ff91f70 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -476,12 +476,7 @@ impl<'context> ObjectBuilder<'context> { Self { context, object } } - pub fn static_method( - &mut self, - function: NativeFunction, - name: &str, - length: usize, - ) -> &mut Self { + pub fn function(&mut self, function: NativeFunction, name: &str, length: usize) -> &mut Self { let mut function = Object::function( Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), self.context @@ -502,7 +497,7 @@ impl<'context> ObjectBuilder<'context> { self } - pub fn static_property(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self + pub fn property(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self where K: Into, V: Into, From c0336274e056060061d234caf82bba3bc57755ba Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 30 Sep 2020 06:13:14 +0200 Subject: [PATCH 15/19] doc ObjectBuilder and ConstructorBuilder --- boa/src/object/mod.rs | 49 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 9bb9ff91f70..6d688ac6f6f 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -464,6 +464,28 @@ impl Object { } } +/// Builder for creating objects with properties. +/// +/// # Examples +/// +/// ``` +/// # use boa::{Context, object::ObjectBuilder, Attribute}; +/// let mut context = Context::new(); +/// let object = ObjectBuilder::new(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 ObjectBuilder<'context> { context: &'context mut Context, @@ -471,11 +493,13 @@ pub struct ObjectBuilder<'context> { } impl<'context> ObjectBuilder<'context> { + /// Create a new `ObjectBuilder`. pub fn new(context: &'context mut Context) -> Self { let object = context.construct_object(); Self { context, object } } + /// Add a function to the object. pub fn function(&mut self, function: NativeFunction, name: &str, length: usize) -> &mut Self { let mut function = Object::function( Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), @@ -497,6 +521,7 @@ impl<'context> ObjectBuilder<'context> { self } + /// Add a property to the object. pub fn property(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self where K: Into, @@ -507,11 +532,13 @@ impl<'context> ObjectBuilder<'context> { self } + /// Build the object. 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, @@ -539,6 +566,7 @@ impl Debug for ConstructorBuilder<'_> { } impl<'context> ConstructorBuilder<'context> { + /// Create a new `ConstructorBuilder`. pub fn new(context: &'context mut Context, constructor: NativeFunction) -> Self { Self { context, @@ -571,6 +599,7 @@ impl<'context> ConstructorBuilder<'context> { } } + /// Add new method to the constructors prototype. pub fn method(&mut self, function: NativeFunction, name: &str, length: usize) -> &mut Self { let mut function = Object::function( Function::BuiltIn(function.into(), FunctionFlags::CALLABLE), @@ -592,6 +621,7 @@ impl<'context> ConstructorBuilder<'context> { self } + /// Add new static method to the constructors object itself. pub fn static_method( &mut self, function: NativeFunction, @@ -618,6 +648,7 @@ impl<'context> ConstructorBuilder<'context> { self } + /// Add new property to the constructors prototype. pub fn property(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self where K: Into, @@ -628,6 +659,7 @@ impl<'context> ConstructorBuilder<'context> { self } + /// Add new static property to the constructors object itself. pub fn static_property(&mut self, key: K, value: V, attribute: Attribute) -> &mut Self where K: Into, @@ -638,11 +670,17 @@ impl<'context> ConstructorBuilder<'context> { self } + /// Specify how many arguments the constructor function takes. + /// + /// Default is `0`. pub fn length(&mut self, length: usize) -> &mut Self { self.length = length; self } + /// Specify the name of the constructor function. + /// + /// Default is `"[object]"` pub fn name(&mut self, name: N) -> &mut Self where N: Into, @@ -651,26 +689,37 @@ impl<'context> ConstructorBuilder<'context> { self } + /// Specify whether the constructor function can be called. + /// + /// Default is `true` 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` 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` 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. 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( From 91d0572a6213a4b157736de95c7a9284edc8fe3d Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 30 Sep 2020 06:15:56 +0200 Subject: [PATCH 16/19] Rename ObjectBuilder to ObjectInitializer --- boa/src/builtins/console/mod.rs | 4 ++-- boa/src/builtins/json/mod.rs | 4 ++-- boa/src/builtins/math/mod.rs | 6 +++--- boa/src/object/mod.rs | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index d1b07000da6..ba94d72bace 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -18,7 +18,7 @@ mod tests; use crate::{ builtins::BuiltIn, - object::ObjectBuilder, + object::ObjectInitializer, property::Attribute, value::{display_obj, RcString, Value}, BoaProfiler, Context, Result, @@ -147,7 +147,7 @@ impl BuiltIn for Console { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - let console = ObjectBuilder::new(context) + let console = ObjectInitializer::new(context) .function(Self::assert, "assert", 0) .function(Self::clear, "clear", 0) .function(Self::debug, "debug", 0) diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index f55eff57f6c..dc44292045b 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -15,7 +15,7 @@ use crate::{ builtins::BuiltIn, - object::ObjectBuilder, + object::ObjectInitializer, property::{Attribute, Property, PropertyKey}, BoaProfiler, Context, Result, Value, }; @@ -38,7 +38,7 @@ impl BuiltIn for Json { fn init(context: &mut Context) -> (&'static str, Value, Attribute) { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); - let json_object = ObjectBuilder::new(context) + let json_object = ObjectInitializer::new(context) .function(Self::parse, "parse", 2) .function(Self::stringify, "stringify", 3) .build(); diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index d2a0cefedb0..e4167efe159 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -12,8 +12,8 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math use crate::{ - builtins::BuiltIn, object::ObjectBuilder, property::Attribute, BoaProfiler, Context, Result, - Value, + builtins::BuiltIn, object::ObjectInitializer, property::Attribute, BoaProfiler, Context, + Result, Value, }; use std::f64; @@ -35,7 +35,7 @@ impl BuiltIn for Math { let _timer = BoaProfiler::global().start_event(Self::NAME, "init"); let attribute = Attribute::READONLY | Attribute::NON_ENUMERABLE | Attribute::PERMANENT; - let object = ObjectBuilder::new(context) + 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) diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index 6d688ac6f6f..f25c9b3dfc3 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -487,12 +487,12 @@ impl Object { /// } /// ``` #[derive(Debug)] -pub struct ObjectBuilder<'context> { +pub struct ObjectInitializer<'context> { context: &'context mut Context, object: GcObject, } -impl<'context> ObjectBuilder<'context> { +impl<'context> ObjectInitializer<'context> { /// Create a new `ObjectBuilder`. pub fn new(context: &'context mut Context) -> Self { let object = context.construct_object(); From 5804418c516a17decdcc506f45854153e5a03bb0 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 30 Sep 2020 06:24:34 +0200 Subject: [PATCH 17/19] doc cached standard objects --- boa/src/context.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/boa/src/context.rs b/boa/src/context.rs index be40cbfe14f..d2f239be3f7 100644 --- a/boa/src/context.rs +++ b/boa/src/context.rs @@ -27,6 +27,7 @@ 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, @@ -54,6 +55,7 @@ impl StandardConstructor { } } +/// Cached core standard objects. #[derive(Debug, Clone, Default)] pub struct StandardObjects { object: StandardConstructor, @@ -639,6 +641,7 @@ impl Context { &self.well_known_symbols } + /// Return the core standard objects. #[inline] pub fn standard_objects(&self) -> &StandardObjects { &self.standard_objects From 7c326f20688d2823a44246c1f2745d2979e156bf Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Fri, 2 Oct 2020 00:53:10 +0200 Subject: [PATCH 18/19] Fix test --- boa/src/object/mod.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/boa/src/object/mod.rs b/boa/src/object/mod.rs index f25c9b3dfc3..6eb80e65c99 100644 --- a/boa/src/object/mod.rs +++ b/boa/src/object/mod.rs @@ -469,11 +469,11 @@ impl Object { /// # Examples /// /// ``` -/// # use boa::{Context, object::ObjectBuilder, Attribute}; +/// # use boa::{Context, Value, object::ObjectInitializer, property::Attribute}; /// let mut context = Context::new(); -/// let object = ObjectBuilder::new(context) +/// let object = ObjectInitializer::new(&mut context) /// .property("hello", "world", Attribute::all()) -/// .property(1, 1 Attribute::all()) +/// .property(1, 1, Attribute::all()) /// .function(|_, _, _| Ok(Value::undefined()), "func", 0) /// .build(); /// ``` From 0bea2029710551b9980b6de40a175f1219334bdf Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Fri, 2 Oct 2020 02:14:55 +0200 Subject: [PATCH 19/19] Set error object types to inherit from `Error.prototype` --- boa/src/builtins/error/mod.rs | 12 ++++-------- boa/src/builtins/error/range.rs | 21 ++------------------- boa/src/builtins/error/reference.rs | 21 ++------------------- boa/src/builtins/error/syntax.rs | 21 ++------------------- boa/src/builtins/error/type.rs | 21 ++------------------- 5 files changed, 12 insertions(+), 84 deletions(-) diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index 6d5d4c15f52..61d73599a53 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -92,13 +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() - ))) + 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 8766e9f3c76..e8dd56e3f7e 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -31,6 +31,7 @@ impl BuiltIn for RangeError { 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, @@ -39,9 +40,9 @@ impl BuiltIn for RangeError { ) .name(Self::NAME) .length(Self::LENGTH) + .inherit(error_prototype.into()) .property("name", Self::NAME, attribute) .property("message", "", attribute) - .method(Self::to_string, "toString", 0) .build(); (Self::NAME, range_error_object.into(), Self::attribute()) @@ -63,22 +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))) - } } diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index ce392f6cb3e..8943b0148d0 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -30,6 +30,7 @@ impl BuiltIn for ReferenceError { 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, @@ -38,9 +39,9 @@ impl BuiltIn for ReferenceError { ) .name(Self::NAME) .length(Self::LENGTH) + .inherit(error_prototype.into()) .property("name", Self::NAME, attribute) .property("message", "", attribute) - .method(Self::to_string, "toString", 0) .build(); (Self::NAME, range_error_object.into(), Self::attribute()) @@ -62,22 +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))) - } } diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 3d47dcb2241..7c60227c016 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -33,6 +33,7 @@ impl BuiltIn for SyntaxError { 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, @@ -41,9 +42,9 @@ impl BuiltIn for SyntaxError { ) .name(Self::NAME) .length(Self::LENGTH) + .inherit(error_prototype.into()) .property("name", Self::NAME, attribute) .property("message", "", attribute) - .method(Self::to_string, "toString", 0) .build(); (Self::NAME, range_error_object.into(), Self::attribute()) @@ -65,22 +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()) - } } diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index 306fdedb74a..803dd3d1568 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -36,6 +36,7 @@ impl BuiltIn for TypeError { 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, @@ -44,9 +45,9 @@ impl BuiltIn for TypeError { ) .name(Self::NAME) .length(Self::LENGTH) + .inherit(error_prototype.into()) .property("name", Self::NAME, attribute) .property("message", "", attribute) - .method(Self::to_string, "toString", 0) .build(); (Self::NAME, range_error_object.into(), Self::attribute()) @@ -68,22 +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))) - } }