Browse Source

Code cleanup (#372)

pull/382/head
HalidOdat 4 years ago committed by GitHub
parent
commit
35f5f0b5b3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 68
      boa/src/builtins/array/mod.rs
  2. 31
      boa/src/builtins/boolean/mod.rs
  3. 2
      boa/src/builtins/boolean/tests.rs
  4. 53
      boa/src/builtins/console/mod.rs
  5. 9
      boa/src/builtins/error.rs
  6. 19
      boa/src/builtins/function/mod.rs
  7. 8
      boa/src/builtins/json/mod.rs
  8. 16
      boa/src/builtins/math/mod.rs
  9. 126
      boa/src/builtins/math/tests.rs
  10. 18
      boa/src/builtins/mod.rs
  11. 48
      boa/src/builtins/number/mod.rs
  12. 29
      boa/src/builtins/number/tests.rs
  13. 62
      boa/src/builtins/object/mod.rs
  14. 56
      boa/src/builtins/regexp/mod.rs
  15. 12
      boa/src/builtins/regexp/tests.rs
  16. 75
      boa/src/builtins/string/mod.rs
  17. 2
      boa/src/builtins/string/tests.rs
  18. 29
      boa/src/builtins/symbol/mod.rs
  19. 2
      boa/src/builtins/symbol/tests.rs
  20. 202
      boa/src/builtins/value/conversions.rs
  21. 352
      boa/src/builtins/value/mod.rs
  22. 138
      boa/src/builtins/value/operations.rs
  23. 14
      boa/src/exec/mod.rs
  24. 26
      boa/src/exec/tests.rs
  25. 18
      boa/src/realm.rs
  26. 22
      boa/src/syntax/lexer/tests.rs

68
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));
}

31
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));
}

2
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);
}

53
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));
}

9
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));
}

19
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));
}

8
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));
}

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

@ -161,7 +161,7 @@ pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
} else {
from_value::<f64>(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));
}

126
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);
}

18
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);
}

48
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));
}

29
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);
}

62
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::<String>(args.get(1).expect("Cannot get object").clone())
.expect("Cannot get object");
let desc = from_value::<Property>(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));
}

56
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<T: FromValue>(args: &[Value], idx: usize) -> Result<T, Value> {
/// 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::<Vec<Value>>();
@ -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));
}

12
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);

75
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));
}

2
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);
}

29
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));
}

2
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> = ValueData::new_obj(None);
let symbol_constructor = create_constructor(&global);
let symbol_constructor = create(&global);
assert_eq!(symbol_constructor.is_function(), true);
}

202
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<Self, &'static str>
where
Self: Sized;
}
impl ToValue for Value {
fn to_value(&self) -> Value {
self.clone()
}
}
impl FromValue for Value {
fn from_value(value: Value) -> Result<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<T: ToValue> ToValue for Vec<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<T: FromValue> FromValue for Vec<T> {
fn from_value(v: Value) -> Result<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<T: ToValue> ToValue for Option<T> {
fn to_value(&self) -> Value {
match *self {
Some(ref v) => v.to_value(),
None => Gc::new(ValueData::Null),
}
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: Value) -> Result<Self, &'static str> {
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<A: FromValue>(v: Value) -> Result<A, &'static str> {
FromValue::from_value(v)
}
/// A utility function that just calls `ToValue::to_value`
pub fn to_value<A: ToValue>(v: A) -> Value {
v.to_value()
}

352
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<Value, Value>;
@ -31,6 +36,7 @@ pub type ResultValue = Result<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter.
pub type Value = Gc<ValueData>;
#[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<Self, &'static str>
where
Self: Sized;
}
impl ToValue for Value {
fn to_value(&self) -> Value {
self.clone()
}
}
impl FromValue for Value {
fn from_value(value: Value) -> Result<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<T: ToValue> ToValue for Vec<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<T: FromValue> FromValue for Vec<T> {
fn from_value(v: Value) -> Result<Self, &'static str> {
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<Self, &'static str> {
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<Self, &'static str> {
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<T: ToValue> ToValue for Option<T> {
fn to_value(&self) -> Value {
match *self {
Some(ref v) => v.to_value(),
None => Gc::new(ValueData::Null),
}
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: Value) -> Result<Self, &'static str> {
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<A: FromValue>(v: Value) -> Result<A, &'static str> {
FromValue::from_value(v)
}
/// A utility function that just calls `ToValue::to_value`
pub fn to_value<A: ToValue>(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::<bool>(x.clone()).expect("failed to get value")
== from_value::<bool>(y.clone()).expect("failed to get value")
}
"object" => *x == *y,
_ => false,
}
}

138
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::<bool>(x.clone()).expect("failed to get value")
== from_value::<bool>(y.clone()).expect("failed to get value")
}
"object" => *x == *y,
_ => false,
}
}

14
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);

26
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
"#;

18
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

22
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));

Loading…
Cancel
Save