diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index c0faf3f354..acc1d3f860 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -102,13 +102,13 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re // between indices and values): this creates an Object with no prototype // Set Prototype - let array_prototype = ctx + let prototype = ctx .realm .global_obj .get_field_slice("Array") .get_field_slice(PROTOTYPE); - this.set_internal_slot(INSTANCE_PROTOTYPE, array_prototype); + this.set_internal_slot(INSTANCE_PROTOTYPE, 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_kind(ObjectKind::Array); @@ -778,12 +778,12 @@ pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument"); let default_value = undefined(); let value = args.get(0).unwrap_or(&default_value); - let relative_start = args.get(1).unwrap_or(&default_value).to_num() as i32; + let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32; let relative_end_val = args.get(2).unwrap_or(&default_value); let relative_end = if relative_end_val.is_undefined() { len } else { - relative_end_val.to_num() as i32 + relative_end_val.to_number() as i32 }; let start = if relative_start < 0 { max(len + relative_start, 0) @@ -979,36 +979,44 @@ pub fn some(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> } /// Create a new `Array` object. -pub fn create_constructor(global: &Value) -> Value { +pub fn create(global: &Value) -> Value { // Create prototype - let array_prototype = ValueData::new_obj(None); + let prototype = ValueData::new_obj(None); let length = Property::default().value(to_value(0_i32)); - array_prototype.set_prop_slice("length", length); - - make_builtin_fn!(concat, named "concat", with length 1, of array_prototype); - make_builtin_fn!(push, named "push", with length 1, of array_prototype); - make_builtin_fn!(index_of, named "indexOf", with length 1, of array_prototype); - make_builtin_fn!(last_index_of, named "lastIndexOf", with length 1, of array_prototype); - make_builtin_fn!(includes_value, named "includes", with length 1, of array_prototype); - make_builtin_fn!(map, named "map", with length 1, of array_prototype); - make_builtin_fn!(fill, named "fill", with length 1, of array_prototype); - make_builtin_fn!(for_each, named "forEach", with length 1, of array_prototype); - make_builtin_fn!(filter, named "filter", with length 1, of array_prototype); - make_builtin_fn!(pop, named "pop", of array_prototype); - make_builtin_fn!(join, named "join", with length 1, of array_prototype); - make_builtin_fn!(to_string, named "toString", of array_prototype); - make_builtin_fn!(reverse, named "reverse", of array_prototype); - make_builtin_fn!(shift, named "shift", of array_prototype); - make_builtin_fn!(unshift, named "unshift", with length 1, of array_prototype); - make_builtin_fn!(every, named "every", with length 1, of array_prototype); - make_builtin_fn!(find, named "find", with length 1, of array_prototype); - make_builtin_fn!(find_index, named "findIndex", with length 1, of array_prototype); - make_builtin_fn!(slice, named "slice", with length 2, of array_prototype); - make_builtin_fn!(some, named "some", with length 2, of array_prototype); - - let array = make_constructor_fn!(make_array, make_array, global, array_prototype); + + prototype.set_prop_slice("length", length); + + make_builtin_fn!(concat, named "concat", with length 1, of prototype); + make_builtin_fn!(push, named "push", with length 1, of prototype); + make_builtin_fn!(index_of, named "indexOf", with length 1, of prototype); + make_builtin_fn!(last_index_of, named "lastIndexOf", with length 1, of prototype); + make_builtin_fn!(includes_value, named "includes", with length 1, of prototype); + make_builtin_fn!(map, named "map", with length 1, of prototype); + make_builtin_fn!(fill, named "fill", with length 1, of prototype); + make_builtin_fn!(for_each, named "forEach", with length 1, of prototype); + make_builtin_fn!(filter, named "filter", with length 1, of prototype); + make_builtin_fn!(pop, named "pop", of prototype); + make_builtin_fn!(join, named "join", with length 1, of prototype); + make_builtin_fn!(to_string, named "toString", of prototype); + make_builtin_fn!(reverse, named "reverse", of prototype); + make_builtin_fn!(shift, named "shift", of prototype); + make_builtin_fn!(unshift, named "unshift", with length 1, of prototype); + make_builtin_fn!(every, named "every", with length 1, of prototype); + make_builtin_fn!(find, named "find", with length 1, of prototype); + make_builtin_fn!(find_index, named "findIndex", with length 1, of prototype); + make_builtin_fn!(slice, named "slice", with length 2, of prototype); + make_builtin_fn!(some, named "some", with length 2, of prototype); + + let array = make_constructor_fn!(make_array, make_array, global, prototype); + // Static Methods make_builtin_fn!(is_array, named "isArray", with length 1, of array); array } + +/// Initialise the `Array` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("Array", create(global)); +} diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index 83b017c8c6..5b01fedda5 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -70,18 +70,6 @@ pub fn value_of(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVal Ok(this_boolean_value(this)) } -/// Create a new `Boolean` object -pub fn create_constructor(global: &Value) -> Value { - // Create Prototype - // https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object - let boolean_prototype = ValueData::new_obj(Some(global)); - boolean_prototype.set_internal_slot("BooleanData", to_boolean(&to_value(false))); - make_builtin_fn!(to_string, named "toString", of boolean_prototype); - make_builtin_fn!(value_of, named "valueOf", of boolean_prototype); - - make_constructor_fn!(construct_boolean, call_boolean, global, boolean_prototype) -} - // === Utility Functions === /// [toBoolean](https://tc39.es/ecma262/#sec-toboolean) /// Creates a new boolean value from the input @@ -109,3 +97,22 @@ pub fn this_boolean_value(value: &Value) -> Value { _ => to_value(false), } } + +/// Create a new `Boolean` object. +pub fn create(global: &Value) -> Value { + // Create Prototype + // https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object + let prototype = ValueData::new_obj(Some(global)); + prototype.set_internal_slot("BooleanData", to_boolean(&to_value(false))); + + make_builtin_fn!(to_string, named "toString", of prototype); + make_builtin_fn!(value_of, named "valueOf", of prototype); + + make_constructor_fn!(construct_boolean, call_boolean, global, prototype) +} + +/// Initialise the `Boolean` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("Boolean", create(global)); +} diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index bf78bb8608..2d625016f7 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -6,7 +6,7 @@ use crate::{builtins::value::same_value, forward, forward_val}; #[test] fn check_boolean_constructor_is_function() { let global = ValueData::new_obj(None); - let boolean_constructor = create_constructor(&global); + let boolean_constructor = create(&global); assert_eq!(boolean_constructor.is_function(), true); } diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index c61073f2c0..6783b57d06 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -19,11 +19,12 @@ mod tests; use crate::{ builtins::{ object::InternalState, - value::{display_obj, from_value, to_value, FromValue, ResultValue, Value, ValueData}, + value::{ + display_obj, from_value, to_value, undefined, FromValue, ResultValue, Value, ValueData, + }, }, exec::Interpreter, }; -use gc::Gc; use rustc_hash::FxHashMap; use std::time::SystemTime; @@ -159,7 +160,7 @@ pub fn assert(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa }); } - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.clear()` @@ -177,7 +178,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue state.groups.clear(); }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.debug(...data)` @@ -192,7 +193,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.error(...data)` @@ -207,7 +208,7 @@ pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state)); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.info(...data)` @@ -222,7 +223,7 @@ pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state)); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.log(...data)` @@ -237,7 +238,7 @@ pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log pub fn log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.trace(...data)` @@ -263,7 +264,7 @@ pub fn trace(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal }); } - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.warn(...data)` @@ -278,7 +279,7 @@ pub fn trace(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn pub fn warn(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state)); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.count(label)` @@ -302,7 +303,7 @@ pub fn count(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal logger(LogMessage::Info(format!("{} {}", msg, c)), state); }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.countReset(label)` @@ -324,7 +325,7 @@ pub fn count_reset(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res logger(LogMessage::Warn(format!("countReset {}", label)), state); }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// Returns current system time in ms. @@ -360,7 +361,7 @@ pub fn time(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu } }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.timeLog(label, ...data)` @@ -392,7 +393,7 @@ pub fn time_log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result } }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.timeEnd(label)` @@ -423,7 +424,7 @@ pub fn time_end(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result } }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.group(...data)` @@ -444,7 +445,7 @@ pub fn group(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal state.groups.push(group_label); }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.groupEnd(label)` @@ -462,7 +463,7 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa state.groups.pop(); }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `console.dir(item, options)` @@ -478,20 +479,18 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_mut(|state: &mut ConsoleState| { logger( - LogMessage::Info(display_obj( - args.get(0).unwrap_or(&Gc::new(ValueData::Undefined)), - true, - )), + LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined()), true)), state, ); }); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// Create a new `console` object -pub fn create_constructor(global: &Value) -> Value { +pub fn create(global: &Value) -> Value { let console = ValueData::new_obj(Some(global)); + make_builtin_fn!(assert, named "assert", of console); make_builtin_fn!(clear, named "clear", of console); make_builtin_fn!(debug, named "debug", of console); @@ -511,6 +510,14 @@ pub fn create_constructor(global: &Value) -> Value { make_builtin_fn!(time_end, named "timeEnd", of console); make_builtin_fn!(dir, named "dir", of console); make_builtin_fn!(dir, named "dirxml", of console); + console.set_internal_state(ConsoleState::default()); + console } + +/// Initialise the `console` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("console", create(global)); +} diff --git a/boa/src/builtins/error.rs b/boa/src/builtins/error.rs index 690993a948..949d28ba15 100644 --- a/boa/src/builtins/error.rs +++ b/boa/src/builtins/error.rs @@ -13,11 +13,10 @@ use crate::{ builtins::{ object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE}, - value::{to_value, ResultValue, Value, ValueData}, + value::{to_value, undefined, ResultValue, Value, ValueData}, }, exec::Interpreter, }; -use gc::Gc; /// Create a new error object. pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { @@ -34,7 +33,7 @@ pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Resu // This value is used by console.log and other routines to match Object type // to its Javascript Identifier (global constructor method name) this.set_kind(ObjectKind::Error); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `Error.prototype.toString()` @@ -54,7 +53,7 @@ pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa } /// Create a new `Error` object. -pub fn _create(global: &Value) -> Value { +pub fn create(global: &Value) -> Value { let prototype = ValueData::new_obj(Some(global)); prototype.set_field_slice("message", to_value("")); prototype.set_field_slice("name", to_value("Error")); @@ -64,5 +63,5 @@ pub fn _create(global: &Value) -> Value { /// Initialise the global object with the `Error` object. pub fn init(global: &Value) { - global.set_field_slice("Error", _create(global)); + global.set_field_slice("Error", create(global)); } diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 3fbae92279..3071c79fad 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -16,14 +16,14 @@ use crate::{ array, object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE}, property::Property, - value::{to_value, ResultValue, Value, ValueData}, + value::{to_value, undefined, ResultValue, Value, ValueData}, }, environment::lexical_environment::{new_function_environment, Environment}, exec::Executor, syntax::ast::node::{FormalParameter, Node}, Interpreter, }; -use gc::{unsafe_empty_trace, Finalize, Gc, Trace}; +use gc::{unsafe_empty_trace, Finalize, Trace}; use std::fmt::{self, Debug}; /// _fn(this, arguments, ctx) -> ResultValue_ - The signature of a built-in function @@ -329,7 +329,7 @@ pub fn create_function_prototype() { pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { let len = arguments_list.len(); let mut obj = Object::default(); - obj.set_internal_slot("ParameterMap", Gc::new(ValueData::Undefined)); + obj.set_internal_slot("ParameterMap", undefined()); // Set length let mut length = Property::default(); length = length.writable(true).value(to_value(len)); @@ -360,7 +360,14 @@ pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> Resu Ok(this.clone()) } -pub fn create_constructor(global: &Value) -> Value { - let proto = ValueData::new_obj(Some(global)); - make_constructor_fn!(make_function, make_function, global, proto) +pub fn create(global: &Value) -> Value { + let prototype = ValueData::new_obj(Some(global)); + + make_constructor_fn!(make_function, make_function, global, prototype) +} + +/// Initialise the `Function` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("Function", create(global)); } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index f82b437ac4..b0dce66d49 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -69,7 +69,7 @@ pub fn stringify(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa } /// Create a new `JSON` object. -pub fn create_constructor(global: &Value) -> Value { +pub fn create(global: &Value) -> Value { let json = ValueData::new_obj(Some(global)); make_builtin_fn!(parse, named "parse", with length 2, of json); @@ -77,3 +77,9 @@ pub fn create_constructor(global: &Value) -> Value { to_value(json) } + +/// Initialise the `JSON` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("JSON", create(global)); +} diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index 6cb2b06ec0..c2ec4b5ca4 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -161,7 +161,7 @@ pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue } else { from_value::(args.get(0).expect("Could not get argument").clone()) .expect("Could not convert argument to f64") - .atan2(args.get(1).expect("Could not get argument").to_num()) + .atan2(args.get(1).expect("Could not get argument").to_number()) })) } @@ -353,7 +353,7 @@ pub fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let mut max = f64::NEG_INFINITY; for arg in args { - let num = arg.to_num(); + let num = arg.to_number(); max = max.max(num); } Ok(to_value(max)) @@ -370,7 +370,7 @@ pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let mut max = f64::INFINITY; for arg in args { - let num = arg.to_num(); + let num = arg.to_number(); max = max.min(num); } Ok(to_value(max)) @@ -550,8 +550,9 @@ pub fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue } /// Create a new `Math` object -pub fn create_constructor(global: &Value) -> Value { +pub fn create(global: &Value) -> Value { let math = ValueData::new_obj(Some(global)); + math.set_field_slice("E", to_value(f64::consts::E)); math.set_field_slice("LN2", to_value(f64::consts::LN_2)); math.set_field_slice("LN10", to_value(f64::consts::LN_10)); @@ -589,5 +590,12 @@ pub fn create_constructor(global: &Value) -> Value { make_builtin_fn!(tan, named "tan", with length 1, of math); make_builtin_fn!(tanh, named "tanh", with length 1, of math); make_builtin_fn!(trunc, named "trunc", with length 1, of math); + math } + +/// Initialise the `Math` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("Math", create(global)); +} diff --git a/boa/src/builtins/math/tests.rs b/boa/src/builtins/math/tests.rs index 24464c50a1..45ad0b13b1 100644 --- a/boa/src/builtins/math/tests.rs +++ b/boa/src/builtins/math/tests.rs @@ -17,8 +17,8 @@ fn abs() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 2.0); - assert_eq!(b.to_num(), 6.655_559_999_999_999_5); + assert_eq!(a.to_number(), 2.0); + assert_eq!(b.to_number(), 6.655_559_999_999_999_5); } #[test] @@ -39,9 +39,9 @@ fn acos() { let c = forward_val(&mut engine, "c").unwrap(); let d = forward(&mut engine, "d"); - assert_eq!(a.to_num(), 0.643_501_108_793_284_3); + assert_eq!(a.to_number(), 0.643_501_108_793_284_3); assert_eq!(b, String::from("NaN")); - assert_eq!(c.to_num(), 0_f64); + assert_eq!(c.to_number(), 0_f64); assert_eq!(d, String::from("NaN")); } @@ -61,7 +61,7 @@ fn acosh() { let b = forward(&mut engine, "b"); let c = forward(&mut engine, "c"); - assert_eq!(a.to_num(), 1.316_957_896_924_816_6); + assert_eq!(a.to_number(), 1.316_957_896_924_816_6); assert_eq!(b, String::from("NaN")); assert_eq!(c, String::from("NaN")); } @@ -80,7 +80,7 @@ fn asin() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward(&mut engine, "b"); - assert_eq!(a.to_num(), 0.643_501_108_793_284_4); + assert_eq!(a.to_number(), 0.643_501_108_793_284_4); assert_eq!(b, String::from("NaN")); } @@ -98,8 +98,8 @@ fn asinh() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 0.881_373_587_019_542_9); - assert_eq!(b.to_num(), 0_f64); + assert_eq!(a.to_number(), 0.881_373_587_019_542_9); + assert_eq!(b.to_number(), 0_f64); } #[test] @@ -118,9 +118,9 @@ fn atan() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), f64::consts::FRAC_PI_4); - assert_eq!(b.to_num(), 0_f64); - assert_eq!(c.to_num(), f64::from(-0)); + assert_eq!(a.to_number(), f64::consts::FRAC_PI_4); + assert_eq!(b.to_number(), 0_f64); + assert_eq!(c.to_number(), f64::from(-0)); } #[test] @@ -137,8 +137,8 @@ fn atan2() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 1.405_647_649_380_269_9); - assert_eq!(b.to_num(), 0.165_148_677_414_626_83); + assert_eq!(a.to_number(), 1.405_647_649_380_269_9); + assert_eq!(b.to_number(), 0.165_148_677_414_626_83); } #[test] @@ -157,9 +157,9 @@ fn cbrt() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 4_f64); - assert_eq!(b.to_num(), -1_f64); - assert_eq!(c.to_num(), 1_f64); + assert_eq!(a.to_number(), 4_f64); + assert_eq!(b.to_number(), -1_f64); + assert_eq!(c.to_number(), 1_f64); } #[test] @@ -178,9 +178,9 @@ fn ceil() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 2_f64); - assert_eq!(b.to_num(), 4_f64); - assert_eq!(c.to_num(), -7_f64); + assert_eq!(a.to_number(), 2_f64); + assert_eq!(b.to_number(), 4_f64); + assert_eq!(c.to_number(), -7_f64); } #[test] @@ -197,8 +197,8 @@ fn cos() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 1_f64); - assert_eq!(b.to_num(), 0.540_302_305_868_139_8); + assert_eq!(a.to_number(), 1_f64); + assert_eq!(b.to_number(), 0.540_302_305_868_139_8); } #[test] @@ -217,9 +217,9 @@ fn cosh() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 1_f64); - assert_eq!(b.to_num(), 1.543_080_634_815_243_7); - assert_eq!(c.to_num(), 1.543_080_634_815_243_7); + assert_eq!(a.to_number(), 1_f64); + assert_eq!(b.to_number(), 1.543_080_634_815_243_7); + assert_eq!(c.to_number(), 1.543_080_634_815_243_7); } #[test] @@ -238,9 +238,9 @@ fn exp() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 1_f64); - assert_eq!(b.to_num(), 0.367_879_441_171_442_33); - assert_eq!(c.to_num(), 7.389_056_098_930_65); + assert_eq!(a.to_number(), 1_f64); + assert_eq!(b.to_number(), 0.367_879_441_171_442_33); + assert_eq!(c.to_number(), 7.389_056_098_930_65); } #[test] @@ -259,9 +259,9 @@ fn floor() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 1_f64); - assert_eq!(b.to_num(), -4_f64); - assert_eq!(c.to_num(), 3_f64); + assert_eq!(a.to_number(), 1_f64); + assert_eq!(b.to_number(), -4_f64); + assert_eq!(c.to_number(), 3_f64); } #[test] @@ -280,8 +280,8 @@ fn log() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward(&mut engine, "c"); - assert_eq!(a.to_num(), 0_f64); - assert_eq!(b.to_num(), f64::consts::LN_10); + assert_eq!(a.to_number(), 0_f64); + assert_eq!(b.to_number(), f64::consts::LN_10); assert_eq!(c, String::from("NaN")); } @@ -301,8 +301,8 @@ fn log10() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward(&mut engine, "c"); - assert_eq!(a.to_num(), f64::consts::LOG10_2); - assert_eq!(b.to_num(), 0_f64); + assert_eq!(a.to_number(), f64::consts::LOG10_2); + assert_eq!(b.to_number(), 0_f64); assert_eq!(c, String::from("NaN")); } @@ -322,8 +322,8 @@ fn log2() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward(&mut engine, "c"); - assert_eq!(a.to_num(), 1.584_962_500_721_156); - assert_eq!(b.to_num(), 0_f64); + assert_eq!(a.to_number(), 1.584_962_500_721_156); + assert_eq!(b.to_number(), 0_f64); assert_eq!(c, String::from("NaN")); } @@ -343,9 +343,9 @@ fn max() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 20_f64); - assert_eq!(b.to_num(), -10_f64); - assert_eq!(c.to_num(), 20_f64); + assert_eq!(a.to_number(), 20_f64); + assert_eq!(b.to_number(), -10_f64); + assert_eq!(c.to_number(), 20_f64); } #[test] @@ -364,9 +364,9 @@ fn min() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 10_f64); - assert_eq!(b.to_num(), -20_f64); - assert_eq!(c.to_num(), -10_f64); + assert_eq!(a.to_number(), 10_f64); + assert_eq!(b.to_number(), -20_f64); + assert_eq!(c.to_number(), -10_f64); } #[test] @@ -387,10 +387,10 @@ fn pow() { let c = forward_val(&mut engine, "c").unwrap(); let d = forward_val(&mut engine, "d").unwrap(); - assert_eq!(a.to_num(), 1_024_f64); - assert_eq!(b.to_num(), 49_f64); - assert_eq!(c.to_num(), 2.0); - assert_eq!(d.to_num(), 0.020_408_163_265_306_12); + assert_eq!(a.to_number(), 1_024_f64); + assert_eq!(b.to_number(), 49_f64); + assert_eq!(c.to_number(), 2.0); + assert_eq!(d.to_number(), 0.020_408_163_265_306_12); } #[test] @@ -407,8 +407,8 @@ fn round() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 21.0); - assert_eq!(b.to_num(), -20.0); + assert_eq!(a.to_number(), 21.0); + assert_eq!(b.to_number(), -20.0); } #[test] @@ -427,9 +427,9 @@ fn sign() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 1_f64); - assert_eq!(b.to_num(), -1_f64); - assert_eq!(c.to_num(), 0_f64); + assert_eq!(a.to_number(), 1_f64); + assert_eq!(b.to_number(), -1_f64); + assert_eq!(c.to_number(), 0_f64); } #[test] @@ -446,8 +446,8 @@ fn sin() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 0_f64); - assert_eq!(b.to_num(), 0.841_470_984_807_896_5); + assert_eq!(a.to_number(), 0_f64); + assert_eq!(b.to_number(), 0.841_470_984_807_896_5); } #[test] @@ -464,8 +464,8 @@ fn sinh() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 0_f64); - assert_eq!(b.to_num(), 1.175_201_193_643_801_4); + assert_eq!(a.to_number(), 0_f64); + assert_eq!(b.to_number(), 1.175_201_193_643_801_4); } #[test] @@ -484,9 +484,9 @@ fn sqrt() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_num(), 0_f64); - assert_eq!(b.to_num(), f64::consts::SQRT_2); - assert_eq!(c.to_num(), 3_f64); + assert_eq!(a.to_number(), 0_f64); + assert_eq!(b.to_number(), f64::consts::SQRT_2); + assert_eq!(c.to_number(), 3_f64); } // TODO: Precision is always off between ci and local. We proably need a better way to compare floats anyways @@ -503,7 +503,7 @@ fn sqrt() { // let a = forward_val(&mut engine, "a").unwrap(); -// assert_eq!(a.to_num(), f64::from(1.964_759_657_248_652_5)); +// assert_eq!(a.to_number(), f64::from(1.964_759_657_248_652_5)); // } #[test] @@ -520,8 +520,8 @@ fn tanh() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 0.761_594_155_955_764_9); - assert_eq!(b.to_num(), 0_f64); + assert_eq!(a.to_number(), 0.761_594_155_955_764_9); + assert_eq!(b.to_number(), 0_f64); } #[test] @@ -538,6 +538,6 @@ fn trunc() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_num(), 13_f64); - assert_eq!(b.to_num(), 0_f64); + assert_eq!(a.to_number(), 13_f64); + assert_eq!(b.to_number(), 0_f64); } diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 0dc00ed572..5b50104fcc 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -95,3 +95,21 @@ pub mod regexp; pub mod string; pub mod symbol; pub mod value; + +use value::Value; + +/// Initializes builtin objects and functions +#[inline] +pub fn init(global: &Value) { + array::init(global); + boolean::init(global); + json::init(global); + math::init(global); + number::init(global); + object::init(global); + function::init(global); + regexp::init(global); + string::init(global); + symbol::init(global); + console::init(global); +} diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index dcc5550641..2439082153 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -88,7 +88,7 @@ pub fn call_number(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential pub fn to_exponential(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - let this_num = to_number(this).to_num(); + let this_num = to_number(this).to_number(); let this_str_num = num_to_exponential(this_num); Ok(to_value(this_str_num)) } @@ -104,10 +104,10 @@ pub fn to_exponential(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed pub fn to_fixed(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - let this_num = to_number(this).to_num(); + let this_num = to_number(this).to_number(); let precision = match args.get(0) { - Some(n) => match n.to_int() { - x if x > 0 => n.to_int() as usize, + Some(n) => match n.to_integer() { + x if x > 0 => n.to_integer() as usize, _ => 0, }, None => 0, @@ -130,7 +130,7 @@ pub fn to_fixed(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> Res /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tolocalestring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString pub fn to_locale_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - let this_num = to_number(this).to_num(); + let this_num = to_number(this).to_number(); let this_str_num = format!("{}", this_num); Ok(to_value(this_str_num)) } @@ -147,10 +147,10 @@ pub fn to_locale_string(this: &mut Value, _args: &[Value], _ctx: &mut Interprete /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision pub fn to_precision(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let this_num = to_number(this); - let _num_str_len = format!("{}", this_num.to_num()).len(); + let _num_str_len = format!("{}", this_num.to_number()).len(); let _precision = match args.get(0) { - Some(n) => match n.to_int() { - x if x > 0 => n.to_int() as usize, + Some(n) => match n.to_integer() { + x if x > 0 => n.to_integer() as usize, _ => 0, }, None => 0, @@ -170,7 +170,7 @@ pub fn to_precision(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - Ok(to_value(format!("{}", to_number(this).to_num()))) + Ok(to_value(format!("{}", to_number(this).to_number()))) } /// `Number.prototype.toString()` @@ -188,16 +188,22 @@ pub fn value_of(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> Re } /// Create a new `Number` object -pub fn create_constructor(global: &Value) -> Value { - let number_prototype = ValueData::new_obj(Some(global)); - number_prototype.set_internal_slot("NumberData", to_value(0)); - - make_builtin_fn!(to_exponential, named "toExponential", with length 1, of number_prototype); - make_builtin_fn!(to_fixed, named "toFixed", with length 1, of number_prototype); - make_builtin_fn!(to_locale_string, named "toLocaleString", of number_prototype); - make_builtin_fn!(to_precision, named "toPrecision", with length 1, of number_prototype); - make_builtin_fn!(to_string, named "toString", with length 1, of number_prototype); - make_builtin_fn!(value_of, named "valueOf", of number_prototype); - - make_constructor_fn!(make_number, call_number, global, number_prototype) +pub fn create(global: &Value) -> Value { + let prototype = ValueData::new_obj(Some(global)); + prototype.set_internal_slot("NumberData", to_value(0)); + + make_builtin_fn!(to_exponential, named "toExponential", with length 1, of prototype); + make_builtin_fn!(to_fixed, named "toFixed", with length 1, of prototype); + make_builtin_fn!(to_locale_string, named "toLocaleString", of prototype); + make_builtin_fn!(to_precision, named "toPrecision", with length 1, of prototype); + make_builtin_fn!(to_string, named "toString", with length 1, of prototype); + make_builtin_fn!(value_of, named "valueOf", of prototype); + + make_constructor_fn!(make_number, call_number, global, prototype) +} + +/// Initialise the `Number` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("Number", create(global)); } diff --git a/boa/src/builtins/number/tests.rs b/boa/src/builtins/number/tests.rs index 4dcdb65f96..c66fe0be34 100644 --- a/boa/src/builtins/number/tests.rs +++ b/boa/src/builtins/number/tests.rs @@ -1,12 +1,11 @@ #![allow(clippy::float_cmp)] -use super::*; use crate::{builtins::value::ValueData, exec::Executor, forward, forward_val, realm::Realm}; #[test] fn check_number_constructor_is_function() { let global = ValueData::new_obj(None); - let number_constructor = create_constructor(&global); + let number_constructor = super::create(&global); assert_eq!(number_constructor.is_function(), true); } @@ -35,14 +34,14 @@ fn call_number() { let invalid_nan = forward_val(&mut engine, "invalid_nan").unwrap(); let from_exp = forward_val(&mut engine, "from_exp").unwrap(); - assert_eq!(default_zero.to_num(), 0_f64); - assert_eq!(int_one.to_num(), 1_f64); - assert_eq!(float_two.to_num(), 2.1); - assert_eq!(str_three.to_num(), 3.2); - assert_eq!(bool_one.to_num(), 1_f64); - assert!(invalid_nan.to_num().is_nan()); - assert_eq!(bool_zero.to_num(), 0_f64); - assert_eq!(from_exp.to_num(), 234_f64); + assert_eq!(default_zero.to_number(), 0_f64); + assert_eq!(int_one.to_number(), 1_f64); + assert_eq!(float_two.to_number(), 2.1); + assert_eq!(str_three.to_number(), 3.2); + assert_eq!(bool_one.to_number(), 1_f64); + assert!(invalid_nan.to_number().is_nan()); + assert_eq!(bool_zero.to_number(), 0_f64); + assert_eq!(from_exp.to_number(), 234_f64); } #[test] @@ -206,9 +205,9 @@ fn value_of() { let exp_val = forward_val(&mut engine, "exp_val").unwrap(); let neg_val = forward_val(&mut engine, "neg_val").unwrap(); - assert_eq!(default_val.to_num(), 0_f64); - assert_eq!(int_val.to_num(), 123_f64); - assert_eq!(float_val.to_num(), 1.234); - assert_eq!(exp_val.to_num(), 12_000_f64); - assert_eq!(neg_val.to_num(), -12_000_f64); + assert_eq!(default_val.to_number(), 0_f64); + assert_eq!(int_val.to_number(), 123_f64); + assert_eq!(float_val.to_number(), 1.234); + assert_eq!(exp_val.to_number(), 12_000_f64); + assert_eq!(neg_val.to_number(), -12_000_f64); } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 5dd73b72db..ace266767e 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -17,11 +17,11 @@ use crate::{ builtins::{ function::Function, property::Property, - value::{from_value, same_value, to_value, ResultValue, Value, ValueData}, + value::{from_value, same_value, to_value, undefined, ResultValue, Value, ValueData}, }, exec::Interpreter, }; -use gc::{unsafe_empty_trace, Finalize, Gc, Trace}; +use gc::{unsafe_empty_trace, Finalize, Gc, GcCell, Trace}; use rustc_hash::FxHashMap; use std::{ borrow::Borrow, @@ -525,18 +525,29 @@ unsafe impl Trace for ObjectKind { } /// Create a new object. -pub fn make_object(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(Gc::new(ValueData::Undefined)) +pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + if let Some(arg) = args.get(0) { + if !arg.is_null_or_undefined() { + return Ok(Gc::new(ValueData::Object(Box::new(GcCell::new( + Object::from(arg).unwrap(), + ))))); + } + } + let global = &ctx.realm.global_obj; + + let object = ValueData::new_obj(Some(global)); + + Ok(object) } /// Get the `prototype` of an object. -pub fn get_proto_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn get_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object"); Ok(obj.get_field_slice(INSTANCE_PROTOTYPE)) } /// Set the `prototype` of an object. -pub fn set_proto_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn set_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone(); obj.set_internal_slot(INSTANCE_PROTOTYPE, proto); @@ -544,14 +555,14 @@ pub fn set_proto_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> Resul } /// Define a property in an object -pub fn define_prop(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn define_property(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object"); let prop = from_value::(args.get(1).expect("Cannot get object").clone()) .expect("Cannot get object"); let desc = from_value::(args.get(2).expect("Cannot get object").clone()) .expect("Cannot get object"); obj.set_prop(prop, desc); - Ok(Gc::new(ValueData::Undefined)) + Ok(undefined()) } /// `Object.prototype.toString()` @@ -579,7 +590,7 @@ pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa /// /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty -pub fn has_own_prop(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn has_own_property(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let prop = if args.is_empty() { None } else { @@ -591,25 +602,24 @@ pub fn has_own_prop(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Re } /// Create a new `Object` object. -pub fn create_constructor(_: &Value) -> Value { - let mut constructor_obj = Object::function(); - // Create the native function - let constructor_fn = crate::builtins::function::Function::create_builtin( - vec![], - crate::builtins::function::FunctionBody::BuiltIn(make_object), - ); - constructor_obj.set_construct(constructor_fn); - let object = to_value(constructor_obj); - // Prototype chain ends here VV - let prototype = to_value(Object::default()); - object.set_field_slice(PROTOTYPE, prototype.clone()); - - make_builtin_fn!(has_own_prop, named "hasOwnProperty", of prototype); +pub fn create(global: &Value) -> Value { + let prototype = ValueData::new_obj(None); + + make_builtin_fn!(has_own_property, named "hasOwnProperty", of prototype); make_builtin_fn!(to_string, named "toString", of prototype); + let object = make_constructor_fn!(make_object, make_object, global, prototype); + object.set_field_slice("length", to_value(1_i32)); - make_builtin_fn!(set_proto_of, named "setPrototypeOf", with length 2, of object); - make_builtin_fn!(get_proto_of, named "getPrototypeOf", with length 1, of object); - make_builtin_fn!(define_prop, named "defineProperty", with length 3, of object); + make_builtin_fn!(set_prototype_of, named "setPrototypeOf", with length 2, of object); + make_builtin_fn!(get_prototype_of, named "getPrototypeOf", with length 1, of object); + make_builtin_fn!(define_property, named "defineProperty", with length 3, of object); + object } + +/// Initialise the `Object` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("Object", create(global)); +} diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 08057887b0..ff98da5d1a 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -18,11 +18,14 @@ use crate::{ builtins::{ object::{InternalState, Object, ObjectInternalMethods, ObjectKind, PROTOTYPE}, property::Property, - value::{from_value, to_value, FromValue, ResultValue, Value, ValueData}, + value::{from_value, to_value, undefined, FromValue, ResultValue, Value, ValueData}, }, exec::Interpreter, }; +#[cfg(test)] +mod tests; + /// The internal representation on a `RegExp` object. #[derive(Debug)] struct RegExp { @@ -67,7 +70,7 @@ fn get_argument(args: &[Value], idx: usize) -> Result { /// Create a new `RegExp` pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(Gc::new(ValueData::Undefined)); + return Err(undefined()); } let mut regex_body = String::new(); let mut regex_flags = String::new(); @@ -91,7 +94,7 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res } } } - _ => return Err(Gc::new(ValueData::Undefined)), + _ => return Err(undefined()), } // if a second argument is given and it's a string, use it as flags match args.get(1) { @@ -162,7 +165,7 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res // This value is used by console.log and other routines to match Object type // to its Javascript Identifier (global constructor method name) this.set_kind(ObjectKind::Ordinary); - this.set_internal_slot("RegExpMatcher", Gc::new(ValueData::Undefined)); + this.set_internal_slot("RegExpMatcher", undefined()); this.set_internal_slot("OriginalSource", to_value(regex_body)); this.set_internal_slot("OriginalFlags", to_value(regex_flags)); @@ -352,7 +355,7 @@ pub fn exec(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu arg_str.get(start..end).expect("Could not get slice"), )); } else { - result.push(Gc::new(ValueData::Undefined)); + result.push(undefined()); } } let result = to_value(result); @@ -435,7 +438,7 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue { .iter() .map(|group| match group { Some(g) => to_value(g.as_str()), - None => Gc::new(ValueData::Undefined), + None => undefined(), }) .collect::>(); @@ -466,25 +469,28 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue { } /// Create a new `RegExp` object. -pub fn create_constructor(global: &Value) -> Value { +pub fn create(global: &Value) -> Value { // Create prototype - let proto = ValueData::new_obj(Some(global)); - proto.set_field_slice("lastIndex", to_value(0)); - - make_builtin_fn!(test, named "test", with length 1, of proto); - make_builtin_fn!(exec, named "exec", with length 1, of proto); - make_builtin_fn!(to_string, named "toString", of proto); - make_builtin_fn!(get_dot_all, named "dotAll", of proto); - make_builtin_fn!(get_flags, named "flags", of proto); - make_builtin_fn!(get_global, named "global", of proto); - make_builtin_fn!(get_ignore_case, named "ignoreCase", of proto); - make_builtin_fn!(get_multiline, named "multiline", of proto); - make_builtin_fn!(get_source, named "source", of proto); - make_builtin_fn!(get_sticky, named "sticky", of proto); - make_builtin_fn!(get_unicode, named "unicode", of proto); - - make_constructor_fn!(make_regexp, make_regexp, global, proto) + let prototype = ValueData::new_obj(Some(global)); + prototype.set_field_slice("lastIndex", to_value(0)); + + make_builtin_fn!(test, named "test", with length 1, of prototype); + make_builtin_fn!(exec, named "exec", with length 1, of prototype); + make_builtin_fn!(to_string, named "toString", of prototype); + make_builtin_fn!(get_dot_all, named "dotAll", of prototype); + make_builtin_fn!(get_flags, named "flags", of prototype); + make_builtin_fn!(get_global, named "global", of prototype); + make_builtin_fn!(get_ignore_case, named "ignoreCase", of prototype); + make_builtin_fn!(get_multiline, named "multiline", of prototype); + make_builtin_fn!(get_source, named "source", of prototype); + make_builtin_fn!(get_sticky, named "sticky", of prototype); + make_builtin_fn!(get_unicode, named "unicode", of prototype); + + make_constructor_fn!(make_regexp, make_regexp, global, prototype) } -#[cfg(test)] -mod tests; +/// Initialise the `RegExp` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("RegExp", create(global)); +} diff --git a/boa/src/builtins/regexp/tests.rs b/boa/src/builtins/regexp/tests.rs index 49ac140a73..63dac7132f 100644 --- a/boa/src/builtins/regexp/tests.rs +++ b/boa/src/builtins/regexp/tests.rs @@ -4,7 +4,7 @@ use crate::forward; use crate::realm::Realm; #[test] -fn test_constructors() { +fn constructors() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -22,14 +22,14 @@ fn test_constructors() { #[test] fn check_regexp_constructor_is_function() { let global = ValueData::new_obj(None); - let regexp_constructor = create_constructor(&global); + let regexp_constructor = create(&global); assert_eq!(regexp_constructor.is_function(), true); } // TODO: uncomment this test when property getters are supported // #[test] -// fn test_flags() { +// fn flags() { // let mut engine = Executor::new(); // let init = r#" // var re_gi = /test/gi; @@ -55,7 +55,7 @@ fn check_regexp_constructor_is_function() { // } #[test] -fn test_last_index() { +fn last_index() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -71,7 +71,7 @@ fn test_last_index() { } #[test] -fn test_exec() { +fn exec() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -91,7 +91,7 @@ fn test_exec() { } #[test] -fn test_to_string() { +fn to_string() { let realm = Realm::create(); let mut engine = Executor::new(realm); diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 7187cfd8f2..3c8855d661 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -17,7 +17,7 @@ use crate::{ object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE}, property::Property, regexp::{make_regexp, match_all as regexp_match_all, r#match as regexp_match}, - value::{from_value, to_value, ResultValue, Value, ValueData}, + value::{from_value, to_value, undefined, ResultValue, Value, ValueData}, }, exec::Interpreter, }; @@ -55,7 +55,7 @@ pub fn make_string(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res pub fn call_string(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let arg = match args.get(0) { Some(v) => v.clone(), - None => Gc::new(ValueData::Undefined), + None => undefined(), }; if arg.is_undefined() { @@ -1016,10 +1016,10 @@ pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res ], ctx, ) - } else if arg == &Gc::new(ValueData::Undefined) { + } else if arg == &undefined() { make_regexp( &mut to_value(Object::default()), - &[Gc::new(ValueData::Undefined), to_value(String::from("g"))], + &[undefined(), to_value(String::from("g"))], ctx, ) } else { @@ -1036,41 +1036,42 @@ pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res regexp_match_all(&mut re, ctx.value_to_rust_string(this)) } -/// Create a new `String` object -pub fn create_constructor(global: &Value) -> Value { +/// Create a new `String` object. +pub fn create(global: &Value) -> Value { // Create prototype - let proto = ValueData::new_obj(Some(global)); - let prop = Property::default().value(to_value(0_i32)); - - proto.set_prop_slice("length", prop); - make_builtin_fn!(char_at, named "charAt", with length 1, of proto); - make_builtin_fn!(char_code_at, named "charCodeAt", with length 1, of proto); - make_builtin_fn!(to_string, named "toString", of proto); - make_builtin_fn!(concat, named "concat", with length 1, of proto); - make_builtin_fn!(repeat, named "repeat", with length 1, of proto); - make_builtin_fn!(slice, named "slice", with length 2, of proto); - make_builtin_fn!(starts_with, named "startsWith", with length 1, of proto); - make_builtin_fn!(ends_with, named "endsWith", with length 1, of proto); - make_builtin_fn!(includes, named "includes", with length 1, of proto); - make_builtin_fn!(index_of, named "indexOf", with length 1, of proto); - make_builtin_fn!(last_index_of, named "lastIndexOf", with length 1, of proto); - make_builtin_fn!(r#match, named "match", with length 1, of proto); - make_builtin_fn!(pad_end, named "padEnd", with length 1, of proto); - make_builtin_fn!(pad_start, named "padStart", with length 1, of proto); - make_builtin_fn!(trim, named "trim", of proto); - make_builtin_fn!(trim_start, named "trimStart", of proto); - make_builtin_fn!(to_lowercase, named "toLowerCase", of proto); - make_builtin_fn!(to_uppercase, named "toUpperCase", of proto); - make_builtin_fn!(substring, named "substring", with length 2, of proto); - make_builtin_fn!(substr, named "substr", with length 2, of proto); - make_builtin_fn!(value_of, named "valueOf", of proto); - make_builtin_fn!(match_all, named "matchAll", with length 1, of proto); - make_builtin_fn!(replace, named "replace", with length 2, of proto); - - make_constructor_fn!(make_string, call_string, global, proto) + let prototype = ValueData::new_obj(Some(global)); + let length = Property::default().value(to_value(0_i32)); + + prototype.set_prop_slice("length", length); + make_builtin_fn!(char_at, named "charAt", with length 1, of prototype); + make_builtin_fn!(char_code_at, named "charCodeAt", with length 1, of prototype); + make_builtin_fn!(to_string, named "toString", of prototype); + make_builtin_fn!(concat, named "concat", with length 1, of prototype); + make_builtin_fn!(repeat, named "repeat", with length 1, of prototype); + make_builtin_fn!(slice, named "slice", with length 2, of prototype); + make_builtin_fn!(starts_with, named "startsWith", with length 1, of prototype); + make_builtin_fn!(ends_with, named "endsWith", with length 1, of prototype); + make_builtin_fn!(includes, named "includes", with length 1, of prototype); + make_builtin_fn!(index_of, named "indexOf", with length 1, of prototype); + make_builtin_fn!(last_index_of, named "lastIndexOf", with length 1, of prototype); + make_builtin_fn!(r#match, named "match", with length 1, of prototype); + make_builtin_fn!(pad_end, named "padEnd", with length 1, of prototype); + make_builtin_fn!(pad_start, named "padStart", with length 1, of prototype); + make_builtin_fn!(trim, named "trim", of prototype); + make_builtin_fn!(trim_start, named "trimStart", of prototype); + make_builtin_fn!(to_lowercase, named "toLowerCase", of prototype); + make_builtin_fn!(to_uppercase, named "toUpperCase", of prototype); + make_builtin_fn!(substring, named "substring", with length 2, of prototype); + make_builtin_fn!(substr, named "substr", with length 2, of prototype); + make_builtin_fn!(value_of, named "valueOf", of prototype); + make_builtin_fn!(match_all, named "matchAll", with length 1, of prototype); + make_builtin_fn!(replace, named "replace", with length 2, of prototype); + + make_constructor_fn!(make_string, call_string, global, prototype) } -/// Initialise the `String` object on the global object +/// Initialise the `String` object on the global object. +#[inline] pub fn init(global: &Value) { - global.set_field_slice("String", create_constructor(global)); + global.set_field_slice("String", create(global)); } diff --git a/boa/src/builtins/string/tests.rs b/boa/src/builtins/string/tests.rs index c2d7c0970e..890db9da8f 100644 --- a/boa/src/builtins/string/tests.rs +++ b/boa/src/builtins/string/tests.rs @@ -6,7 +6,7 @@ use crate::{forward, forward_val}; #[test] fn check_string_constructor_is_function() { let global = ValueData::new_obj(None); - let string_constructor = create_constructor(&global); + let string_constructor = create(&global); assert_eq!(string_constructor.is_function(), true); } diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 1615ca0127..72f0e2d32a 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -24,7 +24,7 @@ use crate::{ internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE, }, - value::{to_value, ResultValue, Value, ValueData}, + value::{to_value, undefined, ResultValue, Value, ValueData}, }, exec::Interpreter, }; @@ -52,7 +52,7 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu // Set description which should either be undefined or a string let desc_string = match args.get(0) { Some(value) => to_value(value.to_string()), - None => Gc::new(ValueData::Undefined), + None => undefined(), }; sym_instance.set_internal_slot("Description", desc_string); @@ -87,19 +87,16 @@ pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa Ok(to_value(full_string)) } -/// The `Symbol()` constructor returns a value of type **symbol**. -/// -/// It is incomplete as a constructor because it does not support the syntax "`new Symbol()`". -/// -/// More information: -/// - [MDN documentation][mdn] -/// - [ECMAScript reference][spec] -/// -/// [spec]: https://tc39.es/ecma262/#sec-symbol-constructor -/// [mdn]: -pub fn create_constructor(global: &Value) -> Value { +/// Create a new `Symbol` object. +pub fn create(global: &Value) -> Value { // Create prototype object - let proto = ValueData::new_obj(Some(global)); - make_builtin_fn!(to_string, named "toString", of proto); - make_constructor_fn!(call_symbol, call_symbol, global, proto) + let prototype = ValueData::new_obj(Some(global)); + make_builtin_fn!(to_string, named "toString", of prototype); + make_constructor_fn!(call_symbol, call_symbol, global, prototype) +} + +/// Initialise the `Symbol` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("Symbol", create(global)); } diff --git a/boa/src/builtins/symbol/tests.rs b/boa/src/builtins/symbol/tests.rs index 4ef9e0b707..ae53fd6932 100644 --- a/boa/src/builtins/symbol/tests.rs +++ b/boa/src/builtins/symbol/tests.rs @@ -6,7 +6,7 @@ use crate::{forward, forward_val}; #[test] fn check_symbol_constructor_is_function() { let global: Gc = ValueData::new_obj(None); - let symbol_constructor = create_constructor(&global); + let symbol_constructor = create(&global); assert_eq!(symbol_constructor.is_function(), true); } diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs new file mode 100644 index 0000000000..cf1b58881f --- /dev/null +++ b/boa/src/builtins/value/conversions.rs @@ -0,0 +1,202 @@ +use super::*; + +/// Conversion to Javascript values from Rust values +pub trait ToValue { + /// Convert this value to a Rust value + fn to_value(&self) -> Value; +} +/// Conversion to Rust values from Javascript values +pub trait FromValue { + /// Convert this value to a Javascript value + fn from_value(value: Value) -> Result + where + Self: Sized; +} + +impl ToValue for Value { + fn to_value(&self) -> Value { + self.clone() + } +} + +impl FromValue for Value { + fn from_value(value: Value) -> Result { + Ok(value) + } +} + +impl ToValue for String { + fn to_value(&self) -> Value { + Gc::new(ValueData::String(self.clone())) + } +} + +impl FromValue for String { + fn from_value(v: Value) -> Result { + Ok(v.to_string()) + } +} + +impl<'s> ToValue for &'s str { + fn to_value(&self) -> Value { + Gc::new(ValueData::String( + String::from_str(*self).expect("Could not convert string to self to String"), + )) + } +} + +impl ToValue for char { + fn to_value(&self) -> Value { + Gc::new(ValueData::String(self.to_string())) + } +} +impl FromValue for char { + fn from_value(v: Value) -> Result { + Ok(v.to_string() + .chars() + .next() + .expect("Could not get next char")) + } +} + +impl ToValue for f64 { + fn to_value(&self) -> Value { + Gc::new(ValueData::Rational(*self)) + } +} +impl FromValue for f64 { + fn from_value(v: Value) -> Result { + Ok(v.to_number()) + } +} + +impl ToValue for i32 { + fn to_value(&self) -> Value { + Gc::new(ValueData::Integer(*self)) + } +} +impl FromValue for i32 { + fn from_value(v: Value) -> Result { + Ok(v.to_integer()) + } +} + +impl ToValue for usize { + fn to_value(&self) -> Value { + Gc::new(ValueData::Integer(*self as i32)) + } +} +impl FromValue for usize { + fn from_value(v: Value) -> Result { + Ok(v.to_integer() as Self) + } +} + +impl ToValue for bool { + fn to_value(&self) -> Value { + Gc::new(ValueData::Boolean(*self)) + } +} +impl FromValue for bool { + fn from_value(v: Value) -> Result { + Ok(v.is_true()) + } +} + +impl<'s, T: ToValue> ToValue for &'s [T] { + fn to_value(&self) -> Value { + let mut arr = Object::default(); + for (i, item) in self.iter().enumerate() { + arr.properties + .insert(i.to_string(), Property::default().value(item.to_value())); + } + to_value(arr) + } +} +impl ToValue for Vec { + fn to_value(&self) -> Value { + let mut arr = Object::default(); + for (i, item) in self.iter().enumerate() { + arr.properties + .insert(i.to_string(), Property::default().value(item.to_value())); + } + to_value(arr) + } +} + +impl FromValue for Vec { + fn from_value(v: Value) -> Result { + let len = v.get_field_slice("length").to_integer(); + let mut vec = Self::with_capacity(len as usize); + for i in 0..len { + vec.push(from_value(v.get_field_slice(&i.to_string()))?) + } + Ok(vec) + } +} + +impl ToValue for Object { + fn to_value(&self) -> Value { + Gc::new(ValueData::Object(Box::new(GcCell::new(self.clone())))) + } +} + +impl FromValue for Object { + fn from_value(v: Value) -> Result { + match *v { + ValueData::Object(ref obj) => Ok(obj.clone().into_inner()), + _ => Err("Value is not a valid object"), + } + } +} + +impl ToValue for JSONValue { + fn to_value(&self) -> Value { + Gc::new(ValueData::from_json(self.clone())) + } +} + +impl FromValue for JSONValue { + fn from_value(v: Value) -> Result { + Ok(v.to_json()) + } +} + +impl ToValue for () { + fn to_value(&self) -> Value { + Gc::new(ValueData::Null) + } +} +impl FromValue for () { + fn from_value(_: Value) -> Result<(), &'static str> { + Ok(()) + } +} + +impl ToValue for Option { + fn to_value(&self) -> Value { + match *self { + Some(ref v) => v.to_value(), + None => Gc::new(ValueData::Null), + } + } +} +impl FromValue for Option { + fn from_value(value: Value) -> Result { + Ok(if value.is_null_or_undefined() { + None + } else { + Some(FromValue::from_value(value)?) + }) + } +} + +/// A utility function that just calls `FromValue::from_value` +pub fn from_value(v: Value) -> Result { + FromValue::from_value(v) +} + +/// A utility function that just calls `ToValue::to_value` +pub fn to_value(v: A) -> Value { + v.to_value() +} diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 52500ed1ff..8cb80414d9 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -24,6 +24,11 @@ use std::{ str::FromStr, }; +pub mod conversions; +pub mod operations; +pub use conversions::*; +pub use operations::*; + /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) #[must_use] pub type ResultValue = Result; @@ -31,6 +36,7 @@ pub type ResultValue = Result; /// A Garbage-collected Javascript value as represented in the interpreter. pub type Value = Gc; +#[inline] pub fn undefined() -> Value { Gc::new(ValueData::Undefined) } @@ -169,7 +175,7 @@ impl ValueData { } /// Returns true if the value is a number - pub fn is_num(&self) -> bool { + pub fn is_number(&self) -> bool { self.is_double() } @@ -204,7 +210,7 @@ impl ValueData { } /// Converts the value into a 64-bit floating point number - pub fn to_num(&self) -> f64 { + pub fn to_number(&self) -> f64 { match *self { Self::Object(_) | Self::Symbol(_) | Self::Undefined => NAN, Self::String(ref str) => match FromStr::from_str(str) { @@ -219,7 +225,7 @@ impl ValueData { } /// Converts the value into a 32-bit integer - pub fn to_int(&self) -> i32 { + pub fn to_integer(&self) -> i32 { match *self { Self::Object(_) | Self::Undefined @@ -628,7 +634,7 @@ impl ValueData { } pub fn as_num_to_power(&self, other: Self) -> Self { - Self::Rational(self.to_num().powf(other.to_num())) + Self::Rational(self.to_number().powf(other.to_number())) } } @@ -853,341 +859,3 @@ impl Display for ValueData { } } } - -impl PartialEq for ValueData { - fn eq(&self, other: &Self) -> bool { - match (self.clone(), other.clone()) { - // TODO: fix this - // _ if self.ptr.to_inner() == &other.ptr.to_inner() => true, - _ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, - (Self::String(_), _) | (_, Self::String(_)) => self.to_string() == other.to_string(), - (Self::Boolean(a), Self::Boolean(b)) if a == b => true, - (Self::Rational(a), Self::Rational(b)) if a == b && !a.is_nan() && !b.is_nan() => true, - (Self::Rational(a), _) if a == other.to_num() => true, - (_, Self::Rational(a)) if a == self.to_num() => true, - (Self::Integer(a), Self::Integer(b)) if a == b => true, - _ => false, - } - } -} - -impl Add for ValueData { - type Output = Self; - fn add(self, other: Self) -> Self { - match (self, other) { - (Self::String(ref s), ref o) => { - Self::String(format!("{}{}", s.clone(), &o.to_string())) - } - (ref s, Self::String(ref o)) => Self::String(format!("{}{}", s.to_string(), o)), - (ref s, ref o) => Self::Rational(s.to_num() + o.to_num()), - } - } -} -impl Sub for ValueData { - type Output = Self; - fn sub(self, other: Self) -> Self { - Self::Rational(self.to_num() - other.to_num()) - } -} -impl Mul for ValueData { - type Output = Self; - fn mul(self, other: Self) -> Self { - Self::Rational(self.to_num() * other.to_num()) - } -} -impl Div for ValueData { - type Output = Self; - fn div(self, other: Self) -> Self { - Self::Rational(self.to_num() / other.to_num()) - } -} -impl Rem for ValueData { - type Output = Self; - fn rem(self, other: Self) -> Self { - Self::Rational(self.to_num() % other.to_num()) - } -} -impl BitAnd for ValueData { - type Output = Self; - fn bitand(self, other: Self) -> Self { - Self::Integer(self.to_int() & other.to_int()) - } -} -impl BitOr for ValueData { - type Output = Self; - fn bitor(self, other: Self) -> Self { - Self::Integer(self.to_int() | other.to_int()) - } -} -impl BitXor for ValueData { - type Output = Self; - fn bitxor(self, other: Self) -> Self { - Self::Integer(self.to_int() ^ other.to_int()) - } -} -impl Shl for ValueData { - type Output = Self; - fn shl(self, other: Self) -> Self { - Self::Integer(self.to_int() << other.to_int()) - } -} -impl Shr for ValueData { - type Output = Self; - fn shr(self, other: Self) -> Self { - Self::Integer(self.to_int() >> other.to_int()) - } -} -impl Not for ValueData { - type Output = Self; - fn not(self) -> Self { - Self::Boolean(!self.is_true()) - } -} - -/// Conversion to Javascript values from Rust values -pub trait ToValue { - /// Convert this value to a Rust value - fn to_value(&self) -> Value; -} -/// Conversion to Rust values from Javascript values -pub trait FromValue { - /// Convert this value to a Javascript value - fn from_value(value: Value) -> Result - where - Self: Sized; -} - -impl ToValue for Value { - fn to_value(&self) -> Value { - self.clone() - } -} - -impl FromValue for Value { - fn from_value(value: Value) -> Result { - Ok(value) - } -} - -impl ToValue for String { - fn to_value(&self) -> Value { - Gc::new(ValueData::String(self.clone())) - } -} - -impl FromValue for String { - fn from_value(v: Value) -> Result { - Ok(v.to_string()) - } -} - -impl<'s> ToValue for &'s str { - fn to_value(&self) -> Value { - Gc::new(ValueData::String( - String::from_str(*self).expect("Could not convert string to self to String"), - )) - } -} - -impl ToValue for char { - fn to_value(&self) -> Value { - Gc::new(ValueData::String(self.to_string())) - } -} -impl FromValue for char { - fn from_value(v: Value) -> Result { - Ok(v.to_string() - .chars() - .next() - .expect("Could not get next char")) - } -} - -impl ToValue for f64 { - fn to_value(&self) -> Value { - Gc::new(ValueData::Rational(*self)) - } -} -impl FromValue for f64 { - fn from_value(v: Value) -> Result { - Ok(v.to_num()) - } -} - -impl ToValue for i32 { - fn to_value(&self) -> Value { - Gc::new(ValueData::Integer(*self)) - } -} -impl FromValue for i32 { - fn from_value(v: Value) -> Result { - Ok(v.to_int()) - } -} - -impl ToValue for usize { - fn to_value(&self) -> Value { - Gc::new(ValueData::Integer(*self as i32)) - } -} -impl FromValue for usize { - fn from_value(v: Value) -> Result { - Ok(v.to_int() as Self) - } -} - -impl ToValue for bool { - fn to_value(&self) -> Value { - Gc::new(ValueData::Boolean(*self)) - } -} -impl FromValue for bool { - fn from_value(v: Value) -> Result { - Ok(v.is_true()) - } -} - -impl<'s, T: ToValue> ToValue for &'s [T] { - fn to_value(&self) -> Value { - let mut arr = Object::default(); - for (i, item) in self.iter().enumerate() { - arr.properties - .insert(i.to_string(), Property::default().value(item.to_value())); - } - to_value(arr) - } -} -impl ToValue for Vec { - fn to_value(&self) -> Value { - let mut arr = Object::default(); - for (i, item) in self.iter().enumerate() { - arr.properties - .insert(i.to_string(), Property::default().value(item.to_value())); - } - to_value(arr) - } -} - -impl FromValue for Vec { - fn from_value(v: Value) -> Result { - let len = v.get_field_slice("length").to_int(); - let mut vec = Self::with_capacity(len as usize); - for i in 0..len { - vec.push(from_value(v.get_field_slice(&i.to_string()))?) - } - Ok(vec) - } -} - -impl ToValue for Object { - fn to_value(&self) -> Value { - Gc::new(ValueData::Object(Box::new(GcCell::new(self.clone())))) - } -} - -impl FromValue for Object { - fn from_value(v: Value) -> Result { - match *v { - ValueData::Object(ref obj) => Ok(obj.clone().into_inner()), - _ => Err("Value is not a valid object"), - } - } -} - -impl ToValue for JSONValue { - fn to_value(&self) -> Value { - Gc::new(ValueData::from_json(self.clone())) - } -} - -impl FromValue for JSONValue { - fn from_value(v: Value) -> Result { - Ok(v.to_json()) - } -} - -impl ToValue for () { - fn to_value(&self) -> Value { - Gc::new(ValueData::Null) - } -} -impl FromValue for () { - fn from_value(_: Value) -> Result<(), &'static str> { - Ok(()) - } -} - -impl ToValue for Option { - fn to_value(&self) -> Value { - match *self { - Some(ref v) => v.to_value(), - None => Gc::new(ValueData::Null), - } - } -} -impl FromValue for Option { - fn from_value(value: Value) -> Result { - Ok(if value.is_null_or_undefined() { - None - } else { - Some(FromValue::from_value(value)?) - }) - } -} - -/// A utility function that just calls `FromValue::from_value` -pub fn from_value(v: Value) -> Result { - FromValue::from_value(v) -} - -/// A utility function that just calls `ToValue::to_value` -pub fn to_value(v: A) -> Value { - v.to_value() -} - -/// The internal comparison abstract operation SameValue(x, y), -/// where x and y are ECMAScript language values, produces true or false. -/// Such a comparison is performed as follows: -/// -/// https://tc39.es/ecma262/#sec-samevalue -/// strict mode currently compares the pointers -pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { - if strict { - // Do both Values point to the same underlying valueData? - let x_ptr = Gc::into_raw(x.clone()); - let y_ptr = Gc::into_raw(y.clone()); - return x_ptr == y_ptr; - } - - if x.get_type() != y.get_type() { - return false; - } - - if x.get_type() == "number" { - let native_x: f64 = from_value(x.clone()).expect("failed to get value"); - let native_y: f64 = from_value(y.clone()).expect("failed to get value"); - return native_x.abs() - native_y.abs() == 0.0; - } - - same_value_non_number(x, y) -} - -pub fn same_value_non_number(x: &Value, y: &Value) -> bool { - debug_assert!(x.get_type() == y.get_type()); - match x.get_type() { - "undefined" => true, - "null" => true, - "string" => { - if x.to_string() == y.to_string() { - return true; - } - false - } - "boolean" => { - from_value::(x.clone()).expect("failed to get value") - == from_value::(y.clone()).expect("failed to get value") - } - "object" => *x == *y, - _ => false, - } -} diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs new file mode 100644 index 0000000000..e465d05f2c --- /dev/null +++ b/boa/src/builtins/value/operations.rs @@ -0,0 +1,138 @@ +use super::*; + +impl PartialEq for ValueData { + fn eq(&self, other: &Self) -> bool { + match (self.clone(), other.clone()) { + // TODO: fix this + // _ if self.ptr.to_inner() == &other.ptr.to_inner() => true, + _ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, + (Self::String(_), _) | (_, Self::String(_)) => self.to_string() == other.to_string(), + (Self::Boolean(a), Self::Boolean(b)) if a == b => true, + (Self::Rational(a), Self::Rational(b)) if a == b && !a.is_nan() && !b.is_nan() => true, + (Self::Rational(a), _) if a == other.to_number() => true, + (_, Self::Rational(a)) if a == self.to_number() => true, + (Self::Integer(a), Self::Integer(b)) if a == b => true, + _ => false, + } + } +} + +impl Add for ValueData { + type Output = Self; + fn add(self, other: Self) -> Self { + match (self, other) { + (Self::String(ref s), ref o) => { + Self::String(format!("{}{}", s.clone(), &o.to_string())) + } + (ref s, Self::String(ref o)) => Self::String(format!("{}{}", s.to_string(), o)), + (ref s, ref o) => Self::Rational(s.to_number() + o.to_number()), + } + } +} +impl Sub for ValueData { + type Output = Self; + fn sub(self, other: Self) -> Self { + Self::Rational(self.to_number() - other.to_number()) + } +} +impl Mul for ValueData { + type Output = Self; + fn mul(self, other: Self) -> Self { + Self::Rational(self.to_number() * other.to_number()) + } +} +impl Div for ValueData { + type Output = Self; + fn div(self, other: Self) -> Self { + Self::Rational(self.to_number() / other.to_number()) + } +} +impl Rem for ValueData { + type Output = Self; + fn rem(self, other: Self) -> Self { + Self::Rational(self.to_number() % other.to_number()) + } +} +impl BitAnd for ValueData { + type Output = Self; + fn bitand(self, other: Self) -> Self { + Self::Integer(self.to_integer() & other.to_integer()) + } +} +impl BitOr for ValueData { + type Output = Self; + fn bitor(self, other: Self) -> Self { + Self::Integer(self.to_integer() | other.to_integer()) + } +} +impl BitXor for ValueData { + type Output = Self; + fn bitxor(self, other: Self) -> Self { + Self::Integer(self.to_integer() ^ other.to_integer()) + } +} +impl Shl for ValueData { + type Output = Self; + fn shl(self, other: Self) -> Self { + Self::Integer(self.to_integer() << other.to_integer()) + } +} +impl Shr for ValueData { + type Output = Self; + fn shr(self, other: Self) -> Self { + Self::Integer(self.to_integer() >> other.to_integer()) + } +} +impl Not for ValueData { + type Output = Self; + fn not(self) -> Self { + Self::Boolean(!self.is_true()) + } +} + +/// The internal comparison abstract operation SameValue(x, y), +/// where x and y are ECMAScript language values, produces true or false. +/// Such a comparison is performed as follows: +/// +/// https://tc39.es/ecma262/#sec-samevalue +/// strict mode currently compares the pointers +pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { + if strict { + // Do both Values point to the same underlying valueData? + let x_ptr = Gc::into_raw(x.clone()); + let y_ptr = Gc::into_raw(y.clone()); + return x_ptr == y_ptr; + } + + if x.get_type() != y.get_type() { + return false; + } + + if x.get_type() == "number" { + let native_x: f64 = from_value(x.clone()).expect("failed to get value"); + let native_y: f64 = from_value(y.clone()).expect("failed to get value"); + return native_x.abs() - native_y.abs() == 0.0; + } + + same_value_non_number(x, y) +} + +pub fn same_value_non_number(x: &Value, y: &Value) -> bool { + debug_assert!(x.get_type() == y.get_type()); + match x.get_type() { + "undefined" => true, + "null" => true, + "string" => { + if x.to_string() == y.to_string() { + return true; + } + false + } + "boolean" => { + from_value::(x.clone()).expect("failed to get value") + == from_value::(y.clone()).expect("failed to get value") + } + "object" => *x == *y, + _ => false, + } +} diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index cc81ba7fcf..c8e928a2b9 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -354,11 +354,11 @@ impl Executor for Interpreter { let v_r_a = self.run(a)?; let v_a = (*v_r_a).clone(); Ok(match *op { - UnaryOp::Minus => to_value(-v_a.to_num()), - UnaryOp::Plus => to_value(v_a.to_num()), + UnaryOp::Minus => to_value(-v_a.to_number()), + UnaryOp::Plus => to_value(v_a.to_number()), UnaryOp::Not => Gc::new(!v_a), UnaryOp::Tilde => { - let num_v_a = v_a.to_num(); + let num_v_a = v_a.to_number(); // NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184 to_value(if num_v_a.is_nan() { -1 @@ -398,10 +398,10 @@ impl Executor for Interpreter { CompOp::StrictEqual => v_a == v_b, CompOp::StrictNotEqual if v_a.is_object() => v_r_a != v_r_b, CompOp::StrictNotEqual => v_a != v_b, - CompOp::GreaterThan => v_a.to_num() > v_b.to_num(), - CompOp::GreaterThanOrEqual => v_a.to_num() >= v_b.to_num(), - CompOp::LessThan => v_a.to_num() < v_b.to_num(), - CompOp::LessThanOrEqual => v_a.to_num() <= v_b.to_num(), + CompOp::GreaterThan => v_a.to_number() > v_b.to_number(), + CompOp::GreaterThanOrEqual => v_a.to_number() >= v_b.to_number(), + CompOp::LessThan => v_a.to_number() < v_b.to_number(), + CompOp::LessThanOrEqual => v_a.to_number() <= v_b.to_number(), CompOp::In => { if !v_b.is_object() { panic!("TypeError: {} is not an Object.", v_b); diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 8c5f57901d..1abe951cef 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -123,7 +123,7 @@ fn array_field_set() { } #[test] -fn test_tilde_operator() { +fn tilde_operator() { let float = r#" let f = -1.2; ~f @@ -161,7 +161,7 @@ fn test_tilde_operator() { } #[test] -fn test_early_return() { +fn early_return() { let early_return = r#" function early_return() { if (true) { @@ -187,7 +187,7 @@ fn test_early_return() { } #[test] -fn test_short_circuit_evaluation() { +fn short_circuit_evaluation() { // OR operation assert_eq!(exec("true || true"), String::from("true")); assert_eq!(exec("true || false"), String::from("true")); @@ -260,7 +260,7 @@ fn assign_operator_precedence() { } #[test] -fn test_do_while_loop() { +fn do_while_loop() { let simple_one = r#" a = 0; do { @@ -296,7 +296,7 @@ fn test_do_while_loop() { #[test] #[ignore] -fn test_do_while_post_inc() { +fn do_while_post_inc() { let with_post_incrementors = r#" var i = 0; do {} while(i++ < 10) i; @@ -351,7 +351,7 @@ fn test_for_loop() { #[test] #[ignore] -fn test_unary_pre() { +fn unary_pre() { let unary_inc = r#" let a = 5; ++a; @@ -375,7 +375,7 @@ fn test_unary_pre() { #[test] #[ignore] -fn test_unary_post() { +fn unary_post() { let unary_inc = r#" let a = 5; a++; @@ -401,7 +401,7 @@ fn test_unary_post() { mod in_operator { use super::*; #[test] - fn test_p_in_o() { + fn propery_in_object() { let p_in_o = r#" var o = {a: 'a'}; var p = 'a'; @@ -411,7 +411,7 @@ mod in_operator { } #[test] - fn test_p_in_property_chain() { + fn property_in_property_chain() { let p_in_o = r#" var o = {}; var p = 'toString'; @@ -421,7 +421,7 @@ mod in_operator { } #[test] - fn test_p_not_in_o() { + fn property_not_in_object() { let p_not_in_o = r#" var o = {a: 'a'}; var p = 'b'; @@ -431,7 +431,7 @@ mod in_operator { } #[test] - fn test_number_in_array() { + fn number_in_array() { // Note: this is valid because the LHS is converted to a prop key with ToPropertyKey // and arrays are just fancy objects like {'0': 'a'} let num_in_array = r#" @@ -444,7 +444,7 @@ mod in_operator { #[test] #[ignore] - fn test_symbol_in_object() { + fn symbol_in_object() { // FIXME: this scenario works in Firefox's console, this is probably an issue // with Symbol comparison. let sym_in_object = r#" @@ -458,7 +458,7 @@ mod in_operator { #[test] #[should_panic(expected = "TypeError: undefined is not an Object.")] - fn test_should_type_error_when_rhs_not_object() { + fn should_type_error_when_rhs_not_object() { let scenario = r#" 'fail' in undefined "#; diff --git a/boa/src/realm.rs b/boa/src/realm.rs index 83536d12d5..bfd8fbcae1 100644 --- a/boa/src/realm.rs +++ b/boa/src/realm.rs @@ -2,12 +2,12 @@ //! all of the ECMAScript code that is loaded within the scope of that global environment, //! and other associated state and resources. //! -//!A realm is represented in this implementation as a Realm struct with the fields specified from the spec +//! A realm is represented in this implementation as a Realm struct with the fields specified from the spec. + use crate::{ builtins::{ - array, boolean, console, function, + self, function::NativeFunctionData, - json, math, number, object, regexp, string, symbol, value::{ToValue, Value, ValueData}, }, environment::{ @@ -55,17 +55,7 @@ impl Realm { fn create_instrinsics(&self) { let global = &self.global_obj; // Create intrinsics, add global objects here - global.set_field_slice("Array", array::create_constructor(global)); - global.set_field_slice("Boolean", boolean::create_constructor(global)); - global.set_field_slice("JSON", json::create_constructor(global)); - global.set_field_slice("Math", math::create_constructor(global)); - global.set_field_slice("Number", number::create_constructor(global)); - global.set_field_slice("Object", object::create_constructor(global)); - global.set_field_slice("Function", function::create_constructor(global)); - global.set_field_slice("RegExp", regexp::create_constructor(global)); - global.set_field_slice("String", string::create_constructor(global)); - global.set_field_slice("Symbol", symbol::create_constructor(global)); - global.set_field_slice("console", console::create_constructor(global)); + builtins::init(global); } /// Utility to add a function to the global object diff --git a/boa/src/syntax/lexer/tests.rs b/boa/src/syntax/lexer/tests.rs index ce750df2a6..a50bc3b5b6 100644 --- a/boa/src/syntax/lexer/tests.rs +++ b/boa/src/syntax/lexer/tests.rs @@ -323,7 +323,7 @@ fn check_positions() { #[test] #[ignore] -fn test_two_divisions_in_expression() { +fn two_divisions_in_expression() { let s = " return a !== 0 || 1 / a === 1 / b;"; let mut lexer = Lexer::new(s); lexer.lex().expect("failed to lex"); @@ -427,13 +427,13 @@ fn hexadecimal_edge_case() { } #[test] -fn test_single_number_without_semicolon() { +fn single_number_without_semicolon() { let mut lexer = Lexer::new("1"); lexer.lex().expect("failed to lex"); } #[test] -fn test_number_followed_by_dot() { +fn number_followed_by_dot() { let mut lexer = Lexer::new("1.."); lexer.lex().expect("failed to lex"); assert_eq!(lexer.tokens[0].kind, TokenKind::numeric_literal(1.0)); @@ -441,7 +441,7 @@ fn test_number_followed_by_dot() { } #[test] -fn test_regex_literal() { +fn regex_literal() { let mut lexer = Lexer::new("/(?:)/"); lexer.lex().expect("failed to lex"); assert_eq!( @@ -451,7 +451,7 @@ fn test_regex_literal() { } #[test] -fn test_regex_literal_flags() { +fn regex_literal_flags() { let mut lexer = Lexer::new(r"/\/[^\/]*\/*/gmi"); lexer.lex().expect("failed to lex"); assert_eq!( @@ -461,7 +461,7 @@ fn test_regex_literal_flags() { } #[test] -fn test_addition_no_spaces() { +fn addition_no_spaces() { let mut lexer = Lexer::new("1+1"); lexer.lex().expect("failed to lex"); assert_eq!(lexer.tokens[0].kind, TokenKind::numeric_literal(1)); @@ -470,7 +470,7 @@ fn test_addition_no_spaces() { } #[test] -fn test_addition_no_spaces_left_side() { +fn addition_no_spaces_left_side() { let mut lexer = Lexer::new("1+ 1"); lexer.lex().expect("failed to lex"); assert_eq!(lexer.tokens[0].kind, TokenKind::numeric_literal(1)); @@ -479,7 +479,7 @@ fn test_addition_no_spaces_left_side() { } #[test] -fn test_addition_no_spaces_right_side() { +fn addition_no_spaces_right_side() { let mut lexer = Lexer::new("1 +1"); lexer.lex().expect("failed to lex"); assert_eq!(lexer.tokens[0].kind, TokenKind::numeric_literal(1)); @@ -488,7 +488,7 @@ fn test_addition_no_spaces_right_side() { } #[test] -fn test_addition_no_spaces_e_number_left_side() { +fn addition_no_spaces_e_number_left_side() { let mut lexer = Lexer::new("1e2+ 1"); lexer.lex().expect("failed to lex"); assert_eq!(lexer.tokens[0].kind, TokenKind::numeric_literal(100.0)); @@ -497,7 +497,7 @@ fn test_addition_no_spaces_e_number_left_side() { } #[test] -fn test_addition_no_spaces_e_number_right_side() { +fn addition_no_spaces_e_number_right_side() { let mut lexer = Lexer::new("1 +1e3"); lexer.lex().expect("failed to lex"); assert_eq!(lexer.tokens[0].kind, TokenKind::numeric_literal(1)); @@ -506,7 +506,7 @@ fn test_addition_no_spaces_e_number_right_side() { } #[test] -fn test_addition_no_spaces_e_number() { +fn addition_no_spaces_e_number() { let mut lexer = Lexer::new("1e3+1e11"); lexer.lex().expect("failed to lex"); assert_eq!(lexer.tokens[0].kind, TokenKind::numeric_literal(1000.0));