Browse Source

`Value` refactor (#498)

- Refactor `String` => `Rc<str>`
 - Refactor `Symbol` => `Rc<Symbol>`
 - Refactor `BigInt` => `RcBigInt`
 - Changed function signature, from `&mut Value` to `&Value`
 - Removed `Interpreter::value_to_rust_number()
 - Abstracted `Gc<GcCell<Object>>` to `GcObject`
 - Removed unnecessary `Box`s in global environment
 - Extracted `extensible` from internal slots
 - Made `to_primitive` throw errors
 - Removed `strict` parameter in `SameValue` function.
 - The `SameValue` function is not dependent on strict mode.
pull/523/head
HalidOdat 4 years ago committed by GitHub
parent
commit
8f8498eac1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 117
      boa/src/builtins/array/mod.rs
  2. 43
      boa/src/builtins/bigint/mod.rs
  3. 14
      boa/src/builtins/boolean/mod.rs
  4. 3
      boa/src/builtins/boolean/tests.rs
  5. 52
      boa/src/builtins/console/mod.rs
  6. 4
      boa/src/builtins/error/mod.rs
  7. 4
      boa/src/builtins/error/range.rs
  8. 4
      boa/src/builtins/error/reference.rs
  9. 4
      boa/src/builtins/error/type.rs
  10. 13
      boa/src/builtins/function/mod.rs
  11. 10
      boa/src/builtins/json/mod.rs
  12. 7
      boa/src/builtins/json/tests.rs
  13. 69
      boa/src/builtins/math/mod.rs
  14. 6
      boa/src/builtins/mod.rs
  15. 66
      boa/src/builtins/number/mod.rs
  16. 73
      boa/src/builtins/object/gcobject.rs
  17. 79
      boa/src/builtins/object/internal_methods.rs
  18. 114
      boa/src/builtins/object/mod.rs
  19. 62
      boa/src/builtins/regexp/mod.rs
  20. 176
      boa/src/builtins/string/mod.rs
  21. 22
      boa/src/builtins/symbol/mod.rs
  22. 34
      boa/src/builtins/value/conversions.rs
  23. 25
      boa/src/builtins/value/display.rs
  24. 136
      boa/src/builtins/value/equality.rs
  25. 21
      boa/src/builtins/value/hash.rs
  26. 141
      boa/src/builtins/value/mod.rs
  27. 82
      boa/src/builtins/value/operations.rs
  28. 42
      boa/src/builtins/value/rcbigint.rs
  29. 87
      boa/src/builtins/value/rcstring.rs
  30. 39
      boa/src/builtins/value/rcsymbol.rs
  31. 2
      boa/src/builtins/value/tests.rs
  32. 4
      boa/src/builtins/value/val_type.rs
  33. 4
      boa/src/environment/global_environment_record.rs
  34. 10
      boa/src/environment/lexical_environment.rs
  35. 16
      boa/src/exec/expression/mod.rs
  36. 238
      boa/src/exec/mod.rs
  37. 13
      boa/src/exec/operator/mod.rs
  38. 23
      boa/src/realm.rs

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

@ -17,7 +17,7 @@ use crate::{
builtins::{ builtins::{
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property, property::Property,
value::{same_value_zero, ResultValue, Value, ValueData}, value::{same_value_zero, ResultValue, Value},
}, },
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
@ -109,11 +109,7 @@ impl Array {
} }
/// Create a new array /// Create a new array
pub(crate) fn make_array( pub(crate) fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// Make a new Object which will internally represent the Array (mapping // Make a new Object which will internally represent the Array (mapping
// between indices and values): this creates an Object with no prototype // between indices and values): this creates an Object with no prototype
@ -169,7 +165,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.isarray /// [spec]: https://tc39.es/ecma262/#sec-array.isarray
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
pub(crate) fn is_array( pub(crate) fn is_array(
_this: &mut Value, _this: &Value,
args: &[Value], args: &[Value],
_interpreter: &mut Interpreter, _interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -191,7 +187,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat
pub(crate) fn concat(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn concat(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
// If concat is called with no arguments, it returns the original array // If concat is called with no arguments, it returns the original array
return Ok(this.clone()); return Ok(this.clone());
@ -228,7 +224,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push
pub(crate) fn push(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn push(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let new_array = Self::add_to_array_object(this, args)?; let new_array = Self::add_to_array_object(this, args)?;
Ok(new_array.get_field("length")) Ok(new_array.get_field("length"))
} }
@ -243,7 +239,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop
pub(crate) fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let curr_length = i32::from(&this.get_field("length")); let curr_length = i32::from(&this.get_field("length"));
if curr_length < 1 { if curr_length < 1 {
return Ok(Value::undefined()); return Ok(Value::undefined());
@ -266,7 +262,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
pub(crate) fn for_each( pub(crate) fn for_each(
this: &mut Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -275,7 +271,7 @@ impl Array {
} }
let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); let callback_arg = args.get(0).expect("Could not get `callbackFn` argument.");
let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length = i32::from(&this.get_field("length")); let length = i32::from(&this.get_field("length"));
@ -283,7 +279,7 @@ impl Array {
let element = this.get_field(i.to_string()); let element = this.get_field(i.to_string());
let arguments = [element, Value::from(i), this.clone()]; let arguments = [element, Value::from(i), this.clone()];
interpreter.call(callback_arg, &mut this_arg, &arguments)?; interpreter.call(callback_arg, &this_arg, &arguments)?;
} }
Ok(Value::undefined()) Ok(Value::undefined())
@ -301,17 +297,18 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join
pub(crate) fn join(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn join(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let separator = if args.is_empty() { let separator = if args.is_empty() {
String::from(",") String::from(",")
} else { } else {
ctx.to_string(args.get(0).expect("Could not get argument"))? ctx.to_string(args.get(0).expect("Could not get argument"))?
.to_string()
}; };
let mut elem_strs: Vec<String> = Vec::new(); let mut elem_strs = Vec::new();
let length = i32::from(&this.get_field("length")); let length = i32::from(&this.get_field("length"));
for n in 0..length { for n in 0..length {
let elem_str: String = ctx.to_string(&this.get_field(n.to_string()))?; let elem_str = ctx.to_string(&this.get_field(n.to_string()))?.to_string();
elem_strs.push(elem_str); elem_strs.push(elem_str);
} }
@ -331,11 +328,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string( pub(crate) fn to_string(this: &Value, _args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
_args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let method_name = "join"; let method_name = "join";
let mut arguments = vec![Value::from(",")]; let mut arguments = vec![Value::from(",")];
// 2. // 2.
@ -354,7 +347,7 @@ impl Array {
// 4. // 4.
let join = ctx.call(&method, this, &arguments)?; let join = ctx.call(&method, this, &arguments)?;
let string = if let ValueData::String(ref s) = join.data() { let string = if let Value::String(ref s) = join {
Value::from(s.as_str()) Value::from(s.as_str())
} else { } else {
Value::from("") Value::from("")
@ -375,7 +368,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
#[allow(clippy::else_if_without_else)] #[allow(clippy::else_if_without_else)]
pub(crate) fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len = i32::from(&this.get_field("length")); let len = i32::from(&this.get_field("length"));
let middle: i32 = len.wrapping_div(2); let middle: i32 = len.wrapping_div(2);
@ -413,7 +406,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
pub(crate) fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn shift(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len = i32::from(&this.get_field("length")); let len = i32::from(&this.get_field("length"));
if len == 0 { if len == 0 {
@ -455,7 +448,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
pub(crate) fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len = i32::from(&this.get_field("length")); let len = i32::from(&this.get_field("length"));
let arg_c: i32 = args.len() as i32; let arg_c: i32 = args.len() as i32;
@ -500,7 +493,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
pub(crate) fn every( pub(crate) fn every(
this: &mut Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -510,7 +503,7 @@ impl Array {
)); ));
} }
let callback = &args[0]; let callback = &args[0];
let mut this_arg = if args.len() > 1 { let this_arg = if args.len() > 1 {
args[1].clone() args[1].clone()
} else { } else {
Value::undefined() Value::undefined()
@ -521,9 +514,7 @@ impl Array {
while i < len { while i < len {
let element = this.get_field(i.to_string()); let element = this.get_field(i.to_string());
let arguments = [element, Value::from(i), this.clone()]; let arguments = [element, Value::from(i), this.clone()];
let result = interpreter let result = interpreter.call(callback, &this_arg, &arguments)?.is_true();
.call(callback, &mut this_arg, &arguments)?
.is_true();
if !result { if !result {
return Ok(Value::from(false)); return Ok(Value::from(false));
} }
@ -544,11 +535,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
pub(crate) fn map( pub(crate) fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing argument 0 when calling function Array.prototype.map", "missing argument 0 when calling function Array.prototype.map",
@ -556,7 +543,7 @@ impl Array {
} }
let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); let callback = args.get(0).cloned().unwrap_or_else(Value::undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length = i32::from(&this.get_field("length")); let length = i32::from(&this.get_field("length"));
@ -568,7 +555,7 @@ impl Array {
let args = [element, Value::from(idx), new.clone()]; let args = [element, Value::from(idx), new.clone()];
interpreter interpreter
.call(&callback, &mut this_val, &args) .call(&callback, &this_val, &args)
.unwrap_or_else(|_| Value::undefined()) .unwrap_or_else(|_| Value::undefined())
}) })
.collect(); .collect();
@ -595,7 +582,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
pub(crate) fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
// If no arguments, return -1. Not described in spec, but is what chrome does. // If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
return Ok(Value::from(-1)); return Ok(Value::from(-1));
@ -648,11 +635,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf
pub(crate) fn last_index_of( pub(crate) fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
_: &mut Interpreter,
) -> ResultValue {
// If no arguments, return -1. Not described in spec, but is what chrome does. // If no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
return Ok(Value::from(-1)); return Ok(Value::from(-1));
@ -699,27 +682,19 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
pub(crate) fn find( pub(crate) fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing callback when calling function Array.prototype.find", "missing callback when calling function Array.prototype.find",
)); ));
} }
let callback = &args[0]; let callback = &args[0];
let mut this_arg = if args.len() > 1 { let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined);
args[1].clone()
} else {
Value::undefined()
};
let len = i32::from(&this.get_field("length")); let len = i32::from(&this.get_field("length"));
for i in 0..len { for i in 0..len {
let element = this.get_field(i.to_string()); let element = this.get_field(i.to_string());
let arguments = [element.clone(), Value::from(i), this.clone()]; let arguments = [element.clone(), Value::from(i), this.clone()];
let result = interpreter.call(callback, &mut this_arg, &arguments)?; let result = interpreter.call(callback, &this_arg, &arguments)?;
if result.is_true() { if result.is_true() {
return Ok(element); return Ok(element);
} }
@ -740,7 +715,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.findindex /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.findindex
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
pub(crate) fn find_index( pub(crate) fn find_index(
this: &mut Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -752,7 +727,7 @@ impl Array {
let predicate_arg = args.get(0).expect("Could not get `predicate` argument."); let predicate_arg = args.get(0).expect("Could not get `predicate` argument.");
let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length = i32::from(&this.get_field("length")); let length = i32::from(&this.get_field("length"));
@ -760,7 +735,7 @@ impl Array {
let element = this.get_field(i.to_string()); let element = this.get_field(i.to_string());
let arguments = [element, Value::from(i), this.clone()]; let arguments = [element, Value::from(i), this.clone()];
let result = interpreter.call(predicate_arg, &mut this_arg, &arguments)?; let result = interpreter.call(predicate_arg, &this_arg, &arguments)?;
if result.is_true() { if result.is_true() {
return Ok(Value::rational(f64::from(i))); return Ok(Value::rational(f64::from(i)));
@ -781,7 +756,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
pub(crate) fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = i32::from(&this.get_field("length")); let len: i32 = i32::from(&this.get_field("length"));
let default_value = Value::undefined(); let default_value = Value::undefined();
let value = args.get(0).unwrap_or(&default_value); let value = args.get(0).unwrap_or(&default_value);
@ -820,11 +795,7 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
pub(crate) fn includes_value( pub(crate) fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
_: &mut Interpreter,
) -> ResultValue {
let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined); let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined);
let length = i32::from(&this.get_field("length")); let length = i32::from(&this.get_field("length"));
@ -855,7 +826,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
pub(crate) fn slice( pub(crate) fn slice(
this: &mut Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -904,7 +875,7 @@ impl Array {
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
pub(crate) fn filter( pub(crate) fn filter(
this: &mut Value, this: &Value,
args: &[Value], args: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -915,7 +886,7 @@ impl Array {
} }
let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); let callback = args.get(0).cloned().unwrap_or_else(Value::undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length = i32::from(&this.get_field("length")); let length = i32::from(&this.get_field("length"));
@ -928,7 +899,7 @@ impl Array {
let args = [element.clone(), Value::from(idx), new.clone()]; let args = [element.clone(), Value::from(idx), new.clone()];
let callback_result = interpreter let callback_result = interpreter
.call(&callback, &mut this_val, &args) .call(&callback, &this_val, &args)
.unwrap_or_else(|_| Value::undefined()); .unwrap_or_else(|_| Value::undefined());
if callback_result.is_true() { if callback_result.is_true() {
@ -957,18 +928,14 @@ impl Array {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
pub(crate) fn some( pub(crate) fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
interpreter: &mut Interpreter,
) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(Value::from( return Err(Value::from(
"missing callback when calling function Array.prototype.some", "missing callback when calling function Array.prototype.some",
)); ));
} }
let callback = &args[0]; let callback = &args[0];
let mut this_arg = if args.len() > 1 { let this_arg = if args.len() > 1 {
args[1].clone() args[1].clone()
} else { } else {
Value::undefined() Value::undefined()
@ -979,9 +946,7 @@ impl Array {
while i < len { while i < len {
let element = this.get_field(i.to_string()); let element = this.get_field(i.to_string());
let arguments = [element, Value::from(i), this.clone()]; let arguments = [element, Value::from(i), this.clone()];
let result = interpreter let result = interpreter.call(callback, &this_arg, &arguments)?.is_true();
.call(callback, &mut this_arg, &arguments)?
.is_true();
if result { if result {
return Ok(Value::from(true)); return Ok(Value::from(true));
} }

43
boa/src/builtins/bigint/mod.rs

@ -16,7 +16,7 @@ use crate::{
builtins::{ builtins::{
function::{make_builtin_fn, make_constructor_fn}, function::{make_builtin_fn, make_constructor_fn},
object::ObjectData, object::ObjectData,
value::{ResultValue, Value, ValueData}, value::{RcBigInt, ResultValue, Value},
}, },
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
@ -61,15 +61,15 @@ impl BigInt {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue /// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue
#[inline] #[inline]
fn this_bigint_value(value: &Value, ctx: &mut Interpreter) -> Result<Self, Value> { fn this_bigint_value(value: &Value, ctx: &mut Interpreter) -> Result<RcBigInt, Value> {
match value.data() { match value {
// 1. If Type(value) is BigInt, return value. // 1. If Type(value) is BigInt, return value.
ValueData::BigInt(ref bigint) => return Ok(bigint.clone()), Value::BigInt(ref bigint) => return Ok(bigint.clone()),
// 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then // 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then
// a. Assert: Type(value.[[BigIntData]]) is BigInt. // a. Assert: Type(value.[[BigIntData]]) is BigInt.
// b. Return value.[[BigIntData]]. // b. Return value.[[BigIntData]].
ValueData::Object(ref object) => { Value::Object(ref object) => {
if let ObjectData::BigInt(ref bigint) = object.borrow().data { if let ObjectData::BigInt(ref bigint) = object.borrow().data {
return Ok(bigint.clone()); return Ok(bigint.clone());
} }
@ -91,10 +91,10 @@ impl BigInt {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-bigint-objects /// [spec]: https://tc39.es/ecma262/#sec-bigint-objects
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt
pub(crate) fn make_bigint(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) { let data = match args.get(0) {
Some(ref value) => ctx.to_bigint(value)?, Some(ref value) => ctx.to_bigint(value)?,
None => Self::from(0), None => RcBigInt::from(Self::from(0)),
}; };
Ok(Value::from(data)) Ok(Value::from(data))
} }
@ -110,11 +110,7 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string( pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let radix = if !args.is_empty() { let radix = if !args.is_empty() {
args[0].to_integer() args[0].to_integer()
} else { } else {
@ -139,11 +135,7 @@ impl BigInt {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf
pub(crate) fn value_of( pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
_args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
Ok(Value::from(Self::this_bigint_value(this, ctx)?)) Ok(Value::from(Self::this_bigint_value(this, ctx)?))
} }
@ -154,11 +146,7 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn /// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn as_int_n( pub(crate) fn as_int_n(_this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
_this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let (modulo, bits) = Self::calculate_as_uint_n(args, ctx)?; let (modulo, bits) = Self::calculate_as_uint_n(args, ctx)?;
if bits > 0 && modulo >= BigInt::from(2).pow(&BigInt::from(bits as i64 - 1)) { if bits > 0 && modulo >= BigInt::from(2).pow(&BigInt::from(bits as i64 - 1)) {
@ -177,11 +165,7 @@ impl BigInt {
/// [spec]: https://tc39.es/ecma262/#sec-bigint.asuintn /// [spec]: https://tc39.es/ecma262/#sec-bigint.asuintn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn as_uint_n( pub(crate) fn as_uint_n(_this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
_this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let (modulo, _) = Self::calculate_as_uint_n(args, ctx)?; let (modulo, _) = Self::calculate_as_uint_n(args, ctx)?;
Ok(Value::from(modulo)) Ok(Value::from(modulo))
@ -206,7 +190,10 @@ impl BigInt {
let bigint = ctx.to_bigint(bigint_arg)?; let bigint = ctx.to_bigint(bigint_arg)?;
Ok(( Ok((
bigint.mod_floor(&BigInt::from(2).pow(&BigInt::from(bits as i64))), bigint
.as_inner()
.clone()
.mod_floor(&BigInt::from(2).pow(&BigInt::from(bits as i64))),
bits, bits,
)) ))
} }

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

@ -16,7 +16,7 @@ use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::{ builtins::{
object::ObjectData, object::ObjectData,
value::{ResultValue, Value, ValueData}, value::{ResultValue, Value},
}, },
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
@ -40,9 +40,9 @@ impl Boolean {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
fn this_boolean_value(value: &Value, ctx: &mut Interpreter) -> Result<bool, Value> { fn this_boolean_value(value: &Value, ctx: &mut Interpreter) -> Result<bool, Value> {
match value.data() { match value {
ValueData::Boolean(boolean) => return Ok(*boolean), Value::Boolean(boolean) => return Ok(*boolean),
ValueData::Object(ref object) => { Value::Object(ref object) => {
let object = object.borrow(); let object = object.borrow();
if let Some(boolean) = object.as_boolean() { if let Some(boolean) = object.as_boolean() {
return Ok(boolean); return Ok(boolean);
@ -58,7 +58,7 @@ impl Boolean {
/// ///
/// `[[Call]]` Creates a new boolean primitive /// `[[Call]]` Creates a new boolean primitive
pub(crate) fn construct_boolean( pub(crate) fn construct_boolean(
this: &mut Value, this: &Value,
args: &[Value], args: &[Value],
_: &mut Interpreter, _: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -78,7 +78,7 @@ impl Boolean {
/// [spec]: https://tc39.es/ecma262/#sec-boolean-object /// [spec]: https://tc39.es/ecma262/#sec-boolean-object
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let boolean = Self::this_boolean_value(this, ctx)?; let boolean = Self::this_boolean_value(this, ctx)?;
Ok(Value::from(boolean.to_string())) Ok(Value::from(boolean.to_string()))
} }
@ -92,7 +92,7 @@ impl Boolean {
/// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof /// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf
#[inline] #[inline]
pub(crate) fn value_of(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn value_of(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
Ok(Value::from(Self::this_boolean_value(this, ctx)?)) Ok(Value::from(Self::this_boolean_value(this, ctx)?))
} }

3
boa/src/builtins/boolean/tests.rs

@ -71,7 +71,6 @@ fn instances_have_correct_proto_set() {
assert!(same_value( assert!(same_value(
&bool_instance.get_internal_slot("__proto__"), &bool_instance.get_internal_slot("__proto__"),
&bool_prototype, &bool_prototype
true
)); ));
} }

52
boa/src/builtins/console/mod.rs

@ -20,7 +20,7 @@ use crate::{
builtins::{ builtins::{
function::make_builtin_fn, function::make_builtin_fn,
object::InternalState, object::InternalState,
value::{display_obj, ResultValue, Value}, value::{display_obj, RcString, ResultValue, Value},
}, },
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
@ -31,8 +31,8 @@ use std::time::SystemTime;
/// This is the internal console object state. /// This is the internal console object state.
#[derive(Debug, Default)] #[derive(Debug, Default)]
pub struct ConsoleState { pub struct ConsoleState {
count_map: FxHashMap<String, u32>, count_map: FxHashMap<RcString, u32>,
timer_map: FxHashMap<String, u128>, timer_map: FxHashMap<RcString, u128>,
groups: Vec<String>, groups: Vec<String>,
} }
@ -74,7 +74,7 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result<String, Value>
let target = ctx.to_string(&data.get(0).cloned().unwrap_or_default())?; let target = ctx.to_string(&data.get(0).cloned().unwrap_or_default())?;
match data.len() { match data.len() {
0 => Ok(String::new()), 0 => Ok(String::new()),
1 => Ok(target), 1 => Ok(target.to_string()),
_ => { _ => {
let mut formatted = String::new(); let mut formatted = String::new();
let mut arg_index = 1; let mut arg_index = 1;
@ -141,7 +141,7 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result<String, Value>
/// ///
/// [spec]: https://console.spec.whatwg.org/#assert /// [spec]: https://console.spec.whatwg.org/#assert
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert
pub fn assert(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn assert(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default(); let assertion = get_arg_at_index::<bool>(args, 0).unwrap_or_default();
if !assertion { if !assertion {
@ -175,7 +175,7 @@ pub fn assert(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
/// ///
/// [spec]: https://console.spec.whatwg.org/#clear /// [spec]: https://console.spec.whatwg.org/#clear
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear
pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn clear(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.clear(); state.groups.clear();
}); });
@ -193,7 +193,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// ///
/// [spec]: https://console.spec.whatwg.org/#debug /// [spec]: https://console.spec.whatwg.org/#debug
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug
pub fn debug(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn debug(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| {
logger(LogMessage::Log(formatter(args, ctx)?), state); logger(LogMessage::Log(formatter(args, ctx)?), state);
Ok(()) Ok(())
@ -211,7 +211,7 @@ pub fn debug(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
/// ///
/// [spec]: https://console.spec.whatwg.org/#error /// [spec]: https://console.spec.whatwg.org/#error
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error
pub fn error(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| {
logger(LogMessage::Error(formatter(args, ctx)?), state); logger(LogMessage::Error(formatter(args, ctx)?), state);
Ok(()) Ok(())
@ -229,7 +229,7 @@ pub fn error(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
/// ///
/// [spec]: https://console.spec.whatwg.org/#info /// [spec]: https://console.spec.whatwg.org/#info
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info
pub fn info(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn info(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| {
logger(LogMessage::Info(formatter(args, ctx)?), state); logger(LogMessage::Info(formatter(args, ctx)?), state);
Ok(()) Ok(())
@ -247,7 +247,7 @@ pub fn info(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
/// ///
/// [spec]: https://console.spec.whatwg.org/#log /// [spec]: https://console.spec.whatwg.org/#log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log
pub fn log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn log(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| {
logger(LogMessage::Log(formatter(args, ctx)?), state); logger(LogMessage::Log(formatter(args, ctx)?), state);
Ok(()) Ok(())
@ -265,7 +265,7 @@ pub fn log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
/// ///
/// [spec]: https://console.spec.whatwg.org/#trace /// [spec]: https://console.spec.whatwg.org/#trace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace
pub fn trace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn trace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
if !args.is_empty() { if !args.is_empty() {
this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| {
logger(LogMessage::Log(formatter(args, ctx)?), state); logger(LogMessage::Log(formatter(args, ctx)?), state);
@ -294,7 +294,7 @@ pub fn trace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
/// ///
/// [spec]: https://console.spec.whatwg.org/#warn /// [spec]: https://console.spec.whatwg.org/#warn
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn
pub fn warn(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn warn(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| {
logger(LogMessage::Warn(formatter(args, ctx)?), state); logger(LogMessage::Warn(formatter(args, ctx)?), state);
Ok(()) Ok(())
@ -312,10 +312,10 @@ pub fn warn(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
/// ///
/// [spec]: https://console.spec.whatwg.org/#count /// [spec]: https://console.spec.whatwg.org/#count
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count
pub fn count(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn count(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let label = match args.get(0) { let label = match args.get(0) {
Some(value) => ctx.to_string(value)?, Some(value) => ctx.to_string(value)?,
None => "default".to_owned(), None => "default".into(),
}; };
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -339,10 +339,10 @@ pub fn count(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
/// ///
/// [spec]: https://console.spec.whatwg.org/#countreset /// [spec]: https://console.spec.whatwg.org/#countreset
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset
pub fn count_reset(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn count_reset(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let label = match args.get(0) { let label = match args.get(0) {
Some(value) => ctx.to_string(value)?, Some(value) => ctx.to_string(value)?,
None => "default".to_owned(), None => "default".into(),
}; };
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -372,10 +372,10 @@ fn system_time_in_ms() -> u128 {
/// ///
/// [spec]: https://console.spec.whatwg.org/#time /// [spec]: https://console.spec.whatwg.org/#time
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time
pub fn time(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn time(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let label = match args.get(0) { let label = match args.get(0) {
Some(value) => ctx.to_string(value)?, Some(value) => ctx.to_string(value)?,
None => "default".to_owned(), None => "default".into(),
}; };
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -403,10 +403,10 @@ pub fn time(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
/// ///
/// [spec]: https://console.spec.whatwg.org/#timelog /// [spec]: https://console.spec.whatwg.org/#timelog
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog
pub fn time_log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn time_log(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let label = match args.get(0) { let label = match args.get(0) {
Some(value) => ctx.to_string(value)?, Some(value) => ctx.to_string(value)?,
None => "default".to_owned(), None => "default".into(),
}; };
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -438,14 +438,14 @@ pub fn time_log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
/// ///
/// [spec]: https://console.spec.whatwg.org/#timeend /// [spec]: https://console.spec.whatwg.org/#timeend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd
pub fn time_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn time_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let label = match args.get(0) { let label = match args.get(0) {
Some(value) => ctx.to_string(value)?, Some(value) => ctx.to_string(value)?,
None => "default".to_owned(), None => "default".into(),
}; };
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
if let Some(t) = state.timer_map.remove(&label) { if let Some(t) = state.timer_map.remove(label.as_str()) {
let time = system_time_in_ms(); let time = system_time_in_ms();
logger( logger(
LogMessage::Info(format!("{}: {} ms - timer removed", label, time - t)), LogMessage::Info(format!("{}: {} ms - timer removed", label, time - t)),
@ -472,7 +472,7 @@ pub fn time_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
/// ///
/// [spec]: https://console.spec.whatwg.org/#group /// [spec]: https://console.spec.whatwg.org/#group
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group
pub fn group(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn group(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let group_label = formatter(args, ctx)?; let group_label = formatter(args, ctx)?;
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
@ -493,7 +493,7 @@ pub fn group(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
/// ///
/// [spec]: https://console.spec.whatwg.org/#groupend /// [spec]: https://console.spec.whatwg.org/#groupend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd
pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn group_end(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
state.groups.pop(); state.groups.pop();
}); });
@ -511,7 +511,7 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa
/// ///
/// [spec]: https://console.spec.whatwg.org/#dir /// [spec]: https://console.spec.whatwg.org/#dir
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir
pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn dir(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
let undefined = Value::undefined(); let undefined = Value::undefined();
logger( logger(

4
boa/src/builtins/error/mod.rs

@ -43,7 +43,7 @@ impl Error {
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() { if !args.is_empty() {
this.set_field( this.set_field(
"message", "message",
@ -71,7 +71,7 @@ impl Error {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name"); let name = this.get_field("name");
let message = this.get_field("message"); let message = this.get_field("message");
Ok(Value::from(format!("{}: {}", name, message))) Ok(Value::from(format!("{}: {}", name, message)))

4
boa/src/builtins/error/range.rs

@ -32,7 +32,7 @@ impl RangeError {
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() { if !args.is_empty() {
this.set_field( this.set_field(
"message", "message",
@ -60,7 +60,7 @@ impl RangeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name"); let name = this.get_field("name");
let message = this.get_field("message"); let message = this.get_field("message");
Ok(Value::from(format!("{}: {}", name, message))) Ok(Value::from(format!("{}: {}", name, message)))

4
boa/src/builtins/error/reference.rs

@ -31,7 +31,7 @@ impl ReferenceError {
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() { if !args.is_empty() {
this.set_field( this.set_field(
"message", "message",
@ -59,7 +59,7 @@ impl ReferenceError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name"); let name = this.get_field("name");
let message = this.get_field("message"); let message = this.get_field("message");
Ok(Value::from(format!("{}: {}", name, message))) Ok(Value::from(format!("{}: {}", name, message)))

4
boa/src/builtins/error/type.rs

@ -38,7 +38,7 @@ impl TypeError {
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
/// Create a new error object. /// Create a new error object.
pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if !args.is_empty() { if !args.is_empty() {
this.set_field( this.set_field(
"message", "message",
@ -67,7 +67,7 @@ impl TypeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name"); let name = this.get_field("name");
let message = this.get_field("message"); let message = this.get_field("message");
Ok(Value::from(format!("{}: {}", name, message))) Ok(Value::from(format!("{}: {}", name, message)))

13
boa/src/builtins/function/mod.rs

@ -16,7 +16,7 @@ use crate::{
array::Array, array::Array,
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property, property::Property,
value::{ResultValue, Value}, value::{RcString, ResultValue, Value},
}, },
environment::function_environment_record::BindingStatus, environment::function_environment_record::BindingStatus,
environment::lexical_environment::{new_function_environment, Environment}, environment::lexical_environment::{new_function_environment, Environment},
@ -28,7 +28,7 @@ use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt::{self, Debug}; use std::fmt::{self, Debug};
/// _fn(this, arguments, ctx) -> ResultValue_ - The signature of a built-in function /// _fn(this, arguments, ctx) -> ResultValue_ - The signature of a built-in function
pub type NativeFunctionData = fn(&mut Value, &[Value], &mut Interpreter) -> ResultValue; pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue;
/// Sets the ConstructorKind /// Sets the ConstructorKind
#[derive(Debug, Copy, Clone)] #[derive(Debug, Copy, Clone)]
@ -178,7 +178,7 @@ impl Function {
pub fn call( pub fn call(
&self, &self,
function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object) function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
this: &mut Value, this: &Value,
args_list: &[Value], args_list: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -236,7 +236,7 @@ impl Function {
pub fn construct( pub fn construct(
&self, &self,
function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object) function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object)
this: &mut Value, this: &Value,
args_list: &[Value], args_list: &[Value],
interpreter: &mut Interpreter, interpreter: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -376,7 +376,8 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
.writable(true) .writable(true)
.configurable(true); .configurable(true);
obj.properties_mut().insert(index.to_string(), prop); obj.properties_mut()
.insert(RcString::from(index.to_string()), prop);
index += 1; index += 1;
} }
@ -386,7 +387,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
/// Create new function `[[Construct]]` /// Create new function `[[Construct]]`
/// ///
// This gets called when a new Function() is created. // This gets called when a new Function() is created.
pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn make_function(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.set_data(ObjectData::Function(Function::builtin( this.set_data(ObjectData::Function(Function::builtin(
Vec::new(), Vec::new(),
|_, _, _| Ok(Value::undefined()), |_, _, _| Ok(Value::undefined()),

10
boa/src/builtins/json/mod.rs

@ -44,7 +44,7 @@ impl Json {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-json.parse /// [spec]: https://tc39.es/ecma262/#sec-json.parse
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse
pub(crate) fn parse(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn parse(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
match serde_json::from_str::<JSONValue>( match serde_json::from_str::<JSONValue>(
&ctx.to_string(args.get(0).expect("cannot get argument for JSON.parse"))?, &ctx.to_string(args.get(0).expect("cannot get argument for JSON.parse"))?,
) { ) {
@ -106,7 +106,7 @@ impl Json {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-json.stringify /// [spec]: https://tc39.es/ecma262/#sec-json.stringify
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify
pub(crate) fn stringify(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn stringify(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let object = match args.get(0) { let object = match args.get(0) {
Some(obj) if obj.is_symbol() || obj.is_function() || obj.is_undefined() => { Some(obj) if obj.is_symbol() || obj.is_function() || obj.is_undefined() => {
return Ok(Value::undefined()) return Ok(Value::undefined())
@ -132,13 +132,13 @@ impl Json {
.iter() .iter()
.filter_map(|(k, v)| v.value.as_ref().map(|value| (k, value))) .filter_map(|(k, v)| v.value.as_ref().map(|value| (k, value)))
{ {
let mut this_arg = object.clone(); let this_arg = object.clone();
object_to_return.set_property( object_to_return.set_property(
key.to_owned(), key.to_owned(),
Property::default().value(ctx.call( Property::default().value(ctx.call(
replacer, replacer,
&mut this_arg, &this_arg,
&[Value::string(key), val.clone()], &[Value::from(key.clone()), val.clone()],
)?), )?),
); );
} }

7
boa/src/builtins/json/tests.rs

@ -302,13 +302,10 @@ fn json_parse_sets_prototypes() {
.get_field("Array") .get_field("Array")
.get_field(PROTOTYPE); .get_field(PROTOTYPE);
assert_eq!( assert_eq!(
same_value(&object_prototype, &global_object_prototype, true), same_value(&object_prototype, &global_object_prototype),
true
);
assert_eq!(
same_value(&array_prototype, &global_array_prototype, true),
true true
); );
assert_eq!(same_value(&array_prototype, &global_array_prototype), true);
} }
#[test] #[test]

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

@ -24,6 +24,7 @@ use std::f64;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
/// Javascript `Math` object.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Math; pub(crate) struct Math;
@ -39,7 +40,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.abs /// [spec]: https://tc39.es/ecma262/#sec-math.abs
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
pub(crate) fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn abs(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).abs()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).abs()).into())
} }
@ -51,7 +52,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.acos /// [spec]: https://tc39.es/ecma262/#sec-math.acos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
pub(crate) fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn acos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).acos()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).acos()).into())
} }
@ -63,7 +64,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.acosh /// [spec]: https://tc39.es/ecma262/#sec-math.acosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
pub(crate) fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn acosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args Ok(args
.get(0) .get(0)
.map_or(f64::NAN, |x| f64::from(x).acosh()) .map_or(f64::NAN, |x| f64::from(x).acosh())
@ -78,7 +79,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.asin /// [spec]: https://tc39.es/ecma262/#sec-math.asin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
pub(crate) fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn asin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).asin()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).asin()).into())
} }
@ -90,7 +91,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.asinh /// [spec]: https://tc39.es/ecma262/#sec-math.asinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
pub(crate) fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn asinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args Ok(args
.get(0) .get(0)
.map_or(f64::NAN, |x| f64::from(x).asinh()) .map_or(f64::NAN, |x| f64::from(x).asinh())
@ -105,7 +106,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.atan /// [spec]: https://tc39.es/ecma262/#sec-math.atan
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
pub(crate) fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn atan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).atan()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).atan()).into())
} }
@ -117,7 +118,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.atanh /// [spec]: https://tc39.es/ecma262/#sec-math.atanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
pub(crate) fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn atanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args Ok(args
.get(0) .get(0)
.map_or(f64::NAN, |x| f64::from(x).atanh()) .map_or(f64::NAN, |x| f64::from(x).atanh())
@ -132,7 +133,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.atan2 /// [spec]: https://tc39.es/ecma262/#sec-math.atan2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
pub(crate) fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn atan2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(if args.is_empty() { Ok(Value::from(if args.is_empty() {
f64::NAN f64::NAN
} else { } else {
@ -149,7 +150,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.cbrt /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
pub(crate) fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn cbrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cbrt()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cbrt()).into())
} }
@ -161,7 +162,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.ceil /// [spec]: https://tc39.es/ecma262/#sec-math.ceil
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
pub(crate) fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn ceil(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).ceil()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).ceil()).into())
} }
@ -173,7 +174,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.cos /// [spec]: https://tc39.es/ecma262/#sec-math.cos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
pub(crate) fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn cos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cos()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cos()).into())
} }
@ -185,7 +186,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.cosh /// [spec]: https://tc39.es/ecma262/#sec-math.cosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
pub(crate) fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn cosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cosh()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cosh()).into())
} }
@ -197,7 +198,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.exp /// [spec]: https://tc39.es/ecma262/#sec-math.exp
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
pub(crate) fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn exp(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).exp()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).exp()).into())
} }
@ -209,7 +210,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.floor /// [spec]: https://tc39.es/ecma262/#sec-math.floor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
pub(crate) fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn floor(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args Ok(args
.get(0) .get(0)
.map_or(f64::NAN, |x| f64::from(x).floor()) .map_or(f64::NAN, |x| f64::from(x).floor())
@ -224,7 +225,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.log /// [spec]: https://tc39.es/ecma262/#sec-math.log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
pub(crate) fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(if args.is_empty() { Ok(Value::from(if args.is_empty() {
f64::NAN f64::NAN
} else { } else {
@ -246,7 +247,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.log10 /// [spec]: https://tc39.es/ecma262/#sec-math.log10
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
pub(crate) fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn log10(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(if args.is_empty() { Ok(Value::from(if args.is_empty() {
f64::NAN f64::NAN
} else { } else {
@ -268,7 +269,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.log2 /// [spec]: https://tc39.es/ecma262/#sec-math.log2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
pub(crate) fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn log2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(if args.is_empty() { Ok(Value::from(if args.is_empty() {
f64::NAN f64::NAN
} else { } else {
@ -290,7 +291,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.max /// [spec]: https://tc39.es/ecma262/#sec-math.max
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max
pub(crate) fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn max(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::NEG_INFINITY; let mut max = f64::NEG_INFINITY;
for arg in args { for arg in args {
let num = f64::from(arg); let num = f64::from(arg);
@ -307,7 +308,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.min /// [spec]: https://tc39.es/ecma262/#sec-math.min
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min
pub(crate) fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn min(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::INFINITY; let mut max = f64::INFINITY;
for arg in args { for arg in args {
let num = f64::from(arg); let num = f64::from(arg);
@ -324,7 +325,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.pow /// [spec]: https://tc39.es/ecma262/#sec-math.pow
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
pub(crate) fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn pow(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(if args.len() >= 2 { Ok(Value::from(if args.len() >= 2 {
let num = f64::from(args.get(0).expect("Could not get argument")); let num = f64::from(args.get(0).expect("Could not get argument"));
let power = f64::from(args.get(1).expect("Could not get argument")); let power = f64::from(args.get(1).expect("Could not get argument"));
@ -342,7 +343,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.random /// [spec]: https://tc39.es/ecma262/#sec-math.random
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
pub(crate) fn random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn random(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(rand::random::<f64>())) Ok(Value::from(rand::random::<f64>()))
} }
@ -354,7 +355,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.round /// [spec]: https://tc39.es/ecma262/#sec-math.round
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
pub(crate) fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn round(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args Ok(args
.get(0) .get(0)
.map_or(f64::NAN, |x| f64::from(x).round()) .map_or(f64::NAN, |x| f64::from(x).round())
@ -369,7 +370,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.sign /// [spec]: https://tc39.es/ecma262/#sec-math.sign
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
pub(crate) fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn sign(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(if args.is_empty() { Ok(Value::from(if args.is_empty() {
f64::NAN f64::NAN
} else { } else {
@ -391,7 +392,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.sin /// [spec]: https://tc39.es/ecma262/#sec-math.sin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
pub(crate) fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn sin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sin()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sin()).into())
} }
@ -403,7 +404,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.sinh /// [spec]: https://tc39.es/ecma262/#sec-math.sinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
pub(crate) fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn sinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sinh()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sinh()).into())
} }
@ -415,11 +416,19 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.sqrt /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
pub(crate) fn sqrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn sqrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sqrt()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sqrt()).into())
} }
/// Get the tangent of a number
pub(crate) fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// Get the tangent of a number.
///
/// More information:
/// - [ECMAScript reference][spec]
/// - [MDN documentation][mdn]
///
/// [spec]: https://tc39.es/ecma262/#sec-math.tan
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan
pub(crate) fn tan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).tan()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).tan()).into())
} }
@ -431,7 +440,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.tanh /// [spec]: https://tc39.es/ecma262/#sec-math.tanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
pub(crate) fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn tanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).tanh()).into()) Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).tanh()).into())
} }
@ -443,7 +452,7 @@ impl Math {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-math.trunc /// [spec]: https://tc39.es/ecma262/#sec-math.trunc
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
pub(crate) fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub(crate) fn trunc(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(args Ok(args
.get(0) .get(0)
.map_or(f64::NAN, |x| f64::from(x).trunc()) .map_or(f64::NAN, |x| f64::from(x).trunc())

6
boa/src/builtins/mod.rs

@ -35,7 +35,7 @@ pub(crate) use self::{
string::String, string::String,
symbol::Symbol, symbol::Symbol,
undefined::Undefined, undefined::Undefined,
value::{ResultValue, Value, ValueData}, value::{ResultValue, Value},
}; };
/// Initializes builtin objects and functions /// Initializes builtin objects and functions
@ -67,8 +67,8 @@ pub fn init(global: &Value) {
Undefined::init, Undefined::init,
]; ];
match global.data() { match global {
ValueData::Object(ref global_object) => { Value::Object(ref global_object) => {
for init in &globals { for init in &globals {
let (name, value) = init(global); let (name, value) = init(global);
global_object.borrow_mut().insert_field(name, value); global_object.borrow_mut().insert_field(name, value);

66
boa/src/builtins/number/mod.rs

@ -21,7 +21,7 @@ use super::{
object::ObjectData, object::ObjectData,
}; };
use crate::{ use crate::{
builtins::value::{ResultValue, Value, ValueData}, builtins::value::{ResultValue, Value},
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
}; };
@ -57,10 +57,10 @@ impl Number {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue /// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue
fn this_number_value(value: &Value, ctx: &mut Interpreter) -> Result<f64, Value> { fn this_number_value(value: &Value, ctx: &mut Interpreter) -> Result<f64, Value> {
match *value.data() { match *value {
ValueData::Integer(integer) => return Ok(f64::from(integer)), Value::Integer(integer) => return Ok(f64::from(integer)),
ValueData::Rational(rational) => return Ok(rational), Value::Rational(rational) => return Ok(rational),
ValueData::Object(ref object) => { Value::Object(ref object) => {
if let Some(number) = object.borrow().as_number() { if let Some(number) = object.borrow().as_number() {
return Ok(number); return Ok(number);
} }
@ -83,11 +83,7 @@ impl Number {
/// `[[Construct]]` - Creates a Number instance /// `[[Construct]]` - Creates a Number instance
/// ///
/// `[[Call]]` - Creates a number primitive /// `[[Call]]` - Creates a number primitive
pub(crate) fn make_number( pub(crate) fn make_number(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let data = match args.get(0) { let data = match args.get(0) {
Some(ref value) => ctx.to_numeric_number(value)?, Some(ref value) => ctx.to_numeric_number(value)?,
None => 0.0, None => 0.0,
@ -109,7 +105,7 @@ impl Number {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_exponential( pub(crate) fn to_exponential(
this: &mut Value, this: &Value,
_args: &[Value], _args: &[Value],
ctx: &mut Interpreter, ctx: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -129,7 +125,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_fixed(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn to_fixed(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_num = Self::this_number_value(this, ctx)?; let this_num = Self::this_number_value(this, ctx)?;
let precision = match args.get(0) { let precision = match args.get(0) {
Some(n) => match n.to_integer() { Some(n) => match n.to_integer() {
@ -157,7 +153,7 @@ impl Number {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_locale_string( pub(crate) fn to_locale_string(
this: &mut Value, this: &Value,
_args: &[Value], _args: &[Value],
ctx: &mut Interpreter, ctx: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -177,11 +173,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_precision( pub(crate) fn to_precision(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let this_num = Self::this_number_value(this, ctx)?; let this_num = Self::this_number_value(this, ctx)?;
let _num_str_len = format!("{}", this_num).len(); let _num_str_len = format!("{}", this_num).len();
let _precision = match args.get(0) { let _precision = match args.get(0) {
@ -345,11 +337,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string( pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// 1. Let x be ? thisNumberValue(this value). // 1. Let x be ? thisNumberValue(this value).
let x = Self::this_number_value(this, ctx)?; let x = Self::this_number_value(this, ctx)?;
@ -402,11 +390,7 @@ impl Number {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf
pub(crate) fn value_of( pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
_args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
Ok(Value::from(Self::this_number_value(this, ctx)?)) Ok(Value::from(Self::this_number_value(this, ctx)?))
} }
@ -424,14 +408,10 @@ impl Number {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix /// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
pub(crate) fn parse_int( pub(crate) fn parse_int(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
_this: &mut Value,
args: &[Value],
_ctx: &mut Interpreter,
) -> ResultValue {
if let (Some(val), r) = (args.get(0), args.get(1)) { if let (Some(val), r) = (args.get(0), args.get(1)) {
let mut radix = if let Some(rx) = r { let mut radix = if let Some(rx) = r {
if let ValueData::Integer(i) = rx.data() { if let Value::Integer(i) = rx {
*i as u32 *i as u32
} else { } else {
// Handling a second argument that isn't an integer but was provided so cannot be defaulted. // Handling a second argument that isn't an integer but was provided so cannot be defaulted.
@ -442,8 +422,8 @@ impl Number {
0 0
}; };
match val.data() { match val {
ValueData::String(s) => { Value::String(s) => {
// Attempt to infer radix from given string. // Attempt to infer radix from given string.
if radix == 0 { if radix == 0 {
@ -466,8 +446,8 @@ impl Number {
Ok(Value::from(f64::NAN)) Ok(Value::from(f64::NAN))
} }
} }
ValueData::Integer(i) => Ok(Value::integer(*i)), Value::Integer(i) => Ok(Value::integer(*i)),
ValueData::Rational(f) => Ok(Value::integer(*f as i32)), Value::Rational(f) => Ok(Value::integer(*f as i32)),
_ => { _ => {
// Wrong argument type to parseInt. // Wrong argument type to parseInt.
Ok(Value::from(f64::NAN)) Ok(Value::from(f64::NAN))
@ -495,13 +475,13 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string /// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
pub(crate) fn parse_float( pub(crate) fn parse_float(
_this: &mut Value, _this: &Value,
args: &[Value], args: &[Value],
_ctx: &mut Interpreter, _ctx: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
if let Some(val) = args.get(0) { if let Some(val) = args.get(0) {
match val.data() { match val {
ValueData::String(s) => { Value::String(s) => {
if let Ok(i) = s.parse::<i32>() { if let Ok(i) = s.parse::<i32>() {
// Attempt to parse an integer first so that it can be stored as an integer // Attempt to parse an integer first so that it can be stored as an integer
// to improve performance // to improve performance
@ -513,8 +493,8 @@ impl Number {
Ok(Value::from(f64::NAN)) Ok(Value::from(f64::NAN))
} }
} }
ValueData::Integer(i) => Ok(Value::integer(*i)), Value::Integer(i) => Ok(Value::integer(*i)),
ValueData::Rational(f) => Ok(Value::rational(*f)), Value::Rational(f) => Ok(Value::rational(*f)),
_ => { _ => {
// Wrong argument type to parseFloat. // Wrong argument type to parseFloat.
Ok(Value::from(f64::NAN)) Ok(Value::from(f64::NAN))

73
boa/src/builtins/object/gcobject.rs

@ -0,0 +1,73 @@
//! This module implements the `GcObject` structure.
//!
//! The `GcObject` is a garbage collected Object.
use super::Object;
use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace};
use std::fmt::{self, Display};
/// Garbage collected `Object`.
#[derive(Debug, Trace, Finalize, Clone)]
pub struct GcObject(Gc<GcCell<Object>>);
impl GcObject {
#[inline]
pub(crate) fn new(object: Object) -> Self {
Self(Gc::new(GcCell::new(object)))
}
#[inline]
pub fn borrow(&self) -> GcCellRef<'_, Object> {
self.try_borrow().expect("Object already mutably borrowed")
}
#[inline]
pub fn borrow_mut(&self) -> GcCellRefMut<'_, Object> {
self.try_borrow_mut().expect("Object already borrowed")
}
#[inline]
pub fn try_borrow(&self) -> Result<GcCellRef<'_, Object>, BorrowError> {
self.0.try_borrow().map_err(|_| BorrowError)
}
#[inline]
pub fn try_borrow_mut(&self) -> Result<GcCellRefMut<'_, Object>, BorrowMutError> {
self.0.try_borrow_mut().map_err(|_| BorrowMutError)
}
/// Checks if the garbage collected memory is the same.
#[inline]
pub fn equals(lhs: &Self, rhs: &Self) -> bool {
std::ptr::eq(lhs.as_ref(), rhs.as_ref())
}
}
impl AsRef<GcCell<Object>> for GcObject {
#[inline]
fn as_ref(&self) -> &GcCell<Object> {
&*self.0
}
}
/// An error returned by [`GcObject::try_borrow`](struct.GcObject.html#method.try_borrow).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BorrowError;
impl Display for BorrowError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt("Object already mutably borrowed", f)
}
}
/// An error returned by [`GcObject::try_borrow_mut`](struct.GcObject.html#method.try_borrow_mut).
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct BorrowMutError;
impl Display for BorrowMutError {
#[inline]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt("Object already borrowed", f)
}
}

79
boa/src/builtins/object/internal_methods.rs

@ -8,11 +8,9 @@
use crate::builtins::{ use crate::builtins::{
object::{Object, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{Object, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property, property::Property,
value::{same_value, Value, ValueData}, value::{same_value, RcString, Value},
}; };
use crate::BoaProfiler; use crate::BoaProfiler;
use std::borrow::Borrow;
use std::ops::Deref;
impl Object { impl Object {
/// Check if object has property. /// Check if object has property.
@ -29,8 +27,8 @@ impl Object {
if !parent.is_null() { if !parent.is_null() {
// the parent value variant should be an object // the parent value variant should be an object
// In the unlikely event it isn't return false // In the unlikely event it isn't return false
return match *parent { return match parent {
ValueData::Object(ref obj) => (*obj).deref().borrow().has_property(val), Value::Object(ref obj) => obj.borrow().has_property(val),
_ => false, _ => false,
}; };
} }
@ -48,11 +46,7 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
#[inline] #[inline]
pub fn is_extensible(&self) -> bool { pub fn is_extensible(&self) -> bool {
let val = self.get_internal_slot("extensible"); self.extensible
match *val.deref().borrow() {
ValueData::Boolean(b) => b,
_ => false,
}
} }
/// Disable extensibility. /// Disable extensibility.
@ -63,7 +57,7 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
#[inline] #[inline]
pub fn prevent_extensions(&mut self) -> bool { pub fn prevent_extensions(&mut self) -> bool {
self.set_internal_slot("extensible", Value::from(false)); self.extensible = false;
true true
} }
@ -241,7 +235,6 @@ impl Object {
&& !same_value( && !same_value(
&desc.value.clone().unwrap(), &desc.value.clone().unwrap(),
&current.value.clone().unwrap(), &current.value.clone().unwrap(),
false,
) )
{ {
return false; return false;
@ -253,21 +246,13 @@ impl Object {
} else { } else {
if !current.configurable.unwrap() { if !current.configurable.unwrap() {
if desc.set.is_some() if desc.set.is_some()
&& !same_value( && !same_value(&desc.set.clone().unwrap(), &current.set.clone().unwrap())
&desc.set.clone().unwrap(),
&current.set.clone().unwrap(),
false,
)
{ {
return false; return false;
} }
if desc.get.is_some() if desc.get.is_some()
&& !same_value( && !same_value(&desc.get.clone().unwrap(), &current.get.clone().unwrap())
&desc.get.clone().unwrap(),
&current.get.clone().unwrap(),
false,
)
{ {
return false; return false;
} }
@ -293,8 +278,8 @@ impl Object {
debug_assert!(Property::is_property_key(prop)); debug_assert!(Property::is_property_key(prop));
// Prop could either be a String or Symbol // Prop could either be a String or Symbol
match *(*prop) { match *prop {
ValueData::String(ref st) => { Value::String(ref st) => {
self.properties() self.properties()
.get(st) .get(st)
.map_or_else(Property::default, |v| { .map_or_else(Property::default, |v| {
@ -312,23 +297,24 @@ impl Object {
d d
}) })
} }
ValueData::Symbol(ref symbol) => self Value::Symbol(ref symbol) => {
.symbol_properties() self.symbol_properties()
.get(&symbol.hash()) .get(&symbol.hash())
.map_or_else(Property::default, |v| { .map_or_else(Property::default, |v| {
let mut d = Property::default(); let mut d = Property::default();
if v.is_data_descriptor() { if v.is_data_descriptor() {
d.value = v.value.clone(); d.value = v.value.clone();
d.writable = v.writable; d.writable = v.writable;
} else { } else {
debug_assert!(v.is_accessor_descriptor()); debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone(); d.get = v.get.clone();
d.set = v.set.clone(); d.set = v.set.clone();
} }
d.enumerable = v.enumerable; d.enumerable = v.enumerable;
d.configurable = v.configurable; d.configurable = v.configurable;
d d
}), })
}
_ => Property::default(), _ => Property::default(),
} }
} }
@ -347,11 +333,10 @@ impl Object {
pub fn set_prototype_of(&mut self, val: Value) -> bool { pub fn set_prototype_of(&mut self, val: Value) -> bool {
debug_assert!(val.is_object() || val.is_null()); debug_assert!(val.is_object() || val.is_null());
let current = self.get_internal_slot(PROTOTYPE); let current = self.get_internal_slot(PROTOTYPE);
if same_value(&current, &val, false) { if same_value(&current, &val) {
return true; return true;
} }
let extensible = self.get_internal_slot("extensible"); if !self.is_extensible() {
if extensible.is_null() {
return false; return false;
} }
let mut p = val.clone(); let mut p = val.clone();
@ -359,7 +344,7 @@ impl Object {
while !done { while !done {
if p.is_null() { if p.is_null() {
done = true done = true
} else if same_value(&Value::from(self.clone()), &p, false) { } else if same_value(&Value::from(self.clone()), &p) {
return false; return false;
} else { } else {
p = p.get_internal_slot(PROTOTYPE); p = p.get_internal_slot(PROTOTYPE);
@ -402,7 +387,7 @@ impl Object {
#[inline] #[inline]
pub(crate) fn insert_property<N>(&mut self, name: N, p: Property) pub(crate) fn insert_property<N>(&mut self, name: N, p: Property)
where where
N: Into<String>, N: Into<RcString>,
{ {
self.properties.insert(name.into(), p); self.properties.insert(name.into(), p);
} }
@ -420,7 +405,7 @@ impl Object {
#[inline] #[inline]
pub(crate) fn insert_field<N>(&mut self, name: N, value: Value) -> Option<Property> pub(crate) fn insert_field<N>(&mut self, name: N, value: Value) -> Option<Property>
where where
N: Into<String>, N: Into<RcString>,
{ {
self.properties.insert( self.properties.insert(
name.into(), name.into(),

114
boa/src/builtins/object/mod.rs

@ -17,26 +17,26 @@ use crate::{
builtins::{ builtins::{
function::Function, function::Function,
property::Property, property::Property,
value::{ResultValue, Value, ValueData}, value::{RcBigInt, RcString, RcSymbol, ResultValue, Value},
BigInt, Symbol, BigInt,
}, },
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
}; };
use gc::{Finalize, Trace}; use gc::{Finalize, Trace};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::{ use std::fmt::{Debug, Display, Error, Formatter};
fmt::{Debug, Display, Error, Formatter},
ops::Deref,
};
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
use crate::builtins::value::same_value; use crate::builtins::value::same_value;
pub use internal_state::{InternalState, InternalStateCell}; pub use internal_state::{InternalState, InternalStateCell};
pub mod gcobject;
pub mod internal_methods; pub mod internal_methods;
mod internal_state; mod internal_state;
pub use gcobject::GcObject;
#[cfg(test)] #[cfg(test)]
mod tests; mod tests;
@ -54,23 +54,25 @@ pub struct Object {
/// Internal Slots /// Internal Slots
internal_slots: FxHashMap<String, Value>, internal_slots: FxHashMap<String, Value>,
/// Properties /// Properties
properties: FxHashMap<String, Property>, properties: FxHashMap<RcString, Property>,
/// Symbol Properties /// Symbol Properties
symbol_properties: FxHashMap<u32, Property>, symbol_properties: FxHashMap<u32, Property>,
/// Some rust object that stores internal state /// Some rust object that stores internal state
state: Option<InternalStateCell>, state: Option<InternalStateCell>,
/// Whether it can have new properties added to it.
extensible: bool,
} }
/// Defines the different types of objects. /// Defines the different types of objects.
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub enum ObjectData { pub enum ObjectData {
Array, Array,
BigInt(BigInt), BigInt(RcBigInt),
Boolean(bool), Boolean(bool),
Function(Function), Function(Function),
String(String), String(RcString),
Number(f64), Number(f64),
Symbol(Symbol), Symbol(RcSymbol),
Error, Error,
Ordinary, Ordinary,
} }
@ -99,16 +101,14 @@ impl Default for Object {
/// Return a new ObjectData struct, with `kind` set to Ordinary /// Return a new ObjectData struct, with `kind` set to Ordinary
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {
let mut object = Self { Self {
data: ObjectData::Ordinary, data: ObjectData::Ordinary,
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
state: None, state: None,
}; extensible: true,
}
object.set_internal_slot("extensible", Value::from(true));
object
} }
} }
@ -122,16 +122,14 @@ impl Object {
pub fn function(function: Function) -> Self { pub fn function(function: Function) -> Self {
let _timer = BoaProfiler::global().start_event("Object::Function", "object"); let _timer = BoaProfiler::global().start_event("Object::Function", "object");
let mut object = Self { Self {
data: ObjectData::Function(function), data: ObjectData::Function(function),
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
state: None, state: None,
}; extensible: true,
}
object.set_internal_slot("extensible", Value::from(true));
object
} }
/// ObjectCreate is used to specify the runtime creation of new ordinary objects. /// ObjectCreate is used to specify the runtime creation of new ordinary objects.
@ -145,8 +143,6 @@ impl Object {
let mut obj = Self::default(); let mut obj = Self::default();
obj.internal_slots obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto); .insert(INSTANCE_PROTOTYPE.to_string(), proto);
obj.internal_slots
.insert("extensible".to_string(), Value::from(true));
obj obj
} }
@ -158,6 +154,7 @@ impl Object {
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
state: None, state: None,
extensible: true,
} }
} }
@ -169,28 +166,34 @@ impl Object {
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
state: None, state: None,
extensible: true,
} }
} }
/// Return a new `String` object whose `[[StringData]]` internal slot is set to argument. /// Return a new `String` object whose `[[StringData]]` internal slot is set to argument.
pub fn string(value: String) -> Self { pub fn string<S>(value: S) -> Self
where
S: Into<RcString>,
{
Self { Self {
data: ObjectData::String(value), data: ObjectData::String(value.into()),
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
state: None, state: None,
extensible: true,
} }
} }
/// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument. /// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument.
pub fn bigint(value: BigInt) -> Self { pub fn bigint(value: RcBigInt) -> Self {
Self { Self {
data: ObjectData::BigInt(value), data: ObjectData::BigInt(value),
internal_slots: FxHashMap::default(), internal_slots: FxHashMap::default(),
properties: FxHashMap::default(), properties: FxHashMap::default(),
symbol_properties: FxHashMap::default(), symbol_properties: FxHashMap::default(),
state: None, state: None,
extensible: true,
} }
} }
@ -201,13 +204,13 @@ impl Object {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-toobject /// [spec]: https://tc39.es/ecma262/#sec-toobject
pub fn from(value: &Value) -> Result<Self, ()> { pub fn from(value: &Value) -> Result<Self, ()> {
match *value.data() { match *value {
ValueData::Boolean(a) => Ok(Self::boolean(a)), Value::Boolean(a) => Ok(Self::boolean(a)),
ValueData::Rational(a) => Ok(Self::number(a)), Value::Rational(a) => Ok(Self::number(a)),
ValueData::Integer(a) => Ok(Self::number(f64::from(a))), Value::Integer(a) => Ok(Self::number(f64::from(a))),
ValueData::String(ref a) => Ok(Self::string(a.clone())), Value::String(ref a) => Ok(Self::string(a.clone())),
ValueData::BigInt(ref bigint) => Ok(Self::bigint(bigint.clone())), Value::BigInt(ref bigint) => Ok(Self::bigint(bigint.clone())),
ValueData::Object(ref obj) => Ok((*obj).deref().borrow().clone()), Value::Object(ref obj) => Ok(obj.borrow().clone()),
_ => Err(()), _ => Err(()),
} }
} }
@ -255,9 +258,9 @@ impl Object {
} }
#[inline] #[inline]
pub fn as_string(&self) -> Option<&str> { pub fn as_string(&self) -> Option<RcString> {
match self.data { match self.data {
ObjectData::String(ref string) => Some(string.as_str()), ObjectData::String(ref string) => Some(string.clone()),
_ => None, _ => None,
} }
} }
@ -283,9 +286,9 @@ impl Object {
} }
#[inline] #[inline]
pub fn as_symbol(&self) -> Option<&Symbol> { pub fn as_symbol(&self) -> Option<RcSymbol> {
match self.data { match self.data {
ObjectData::Symbol(ref symbol) => Some(symbol), ObjectData::Symbol(ref symbol) => Some(symbol.clone()),
_ => None, _ => None,
} }
} }
@ -363,12 +366,12 @@ impl Object {
} }
#[inline] #[inline]
pub fn properties(&self) -> &FxHashMap<String, Property> { pub fn properties(&self) -> &FxHashMap<RcString, Property> {
&self.properties &self.properties
} }
#[inline] #[inline]
pub fn properties_mut(&mut self) -> &mut FxHashMap<String, Property> { pub fn properties_mut(&mut self) -> &mut FxHashMap<RcString, Property> {
&mut self.properties &mut self.properties
} }
@ -394,7 +397,7 @@ impl Object {
} }
/// Create a new object. /// Create a new object.
pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn make_object(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
if let Some(arg) = args.get(0) { if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() { if !arg.is_null_or_undefined() {
return Ok(Value::object(Object::from(arg).unwrap())); return Ok(Value::object(Object::from(arg).unwrap()));
@ -408,23 +411,21 @@ pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
} }
/// Uses the SameValue algorithm to check equality of objects /// Uses the SameValue algorithm to check equality of objects
pub fn is(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn is(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let x = args.get(0).cloned().unwrap_or_else(Value::undefined); let x = args.get(0).cloned().unwrap_or_else(Value::undefined);
let y = args.get(1).cloned().unwrap_or_else(Value::undefined); let y = args.get(1).cloned().unwrap_or_else(Value::undefined);
let result = same_value(&x, &y, false); Ok(same_value(&x, &y).into())
Ok(Value::boolean(result))
} }
/// Get the `prototype` of an object. /// Get the `prototype` of an object.
pub fn get_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object"); let obj = args.get(0).expect("Cannot get object");
Ok(obj.get_field(INSTANCE_PROTOTYPE)) Ok(obj.get_field(INSTANCE_PROTOTYPE))
} }
/// Set the `prototype` of an object. /// Set the `prototype` of an object.
pub fn set_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object").clone(); let obj = args.get(0).expect("Cannot get object").clone();
let proto = args.get(1).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone();
obj.set_internal_slot(INSTANCE_PROTOTYPE, proto); obj.set_internal_slot(INSTANCE_PROTOTYPE, proto);
@ -432,7 +433,7 @@ pub fn set_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> R
} }
/// Define a property in an object /// Define a property in an object
pub fn define_property(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn define_property(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object"); let obj = args.get(0).expect("Cannot get object");
let prop = ctx.to_string(args.get(1).expect("Cannot get object"))?; let prop = ctx.to_string(args.get(1).expect("Cannot get object"))?;
let desc = Property::from(args.get(2).expect("Cannot get object")); let desc = Property::from(args.get(2).expect("Cannot get object"));
@ -450,7 +451,7 @@ pub fn define_property(_: &mut Value, args: &[Value], ctx: &mut Interpreter) ->
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(Value::from(this.to_string())) Ok(Value::from(this.to_string()))
} }
@ -465,7 +466,7 @@ pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty
pub fn has_own_property(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let prop = if args.is_empty() { let prop = if args.is_empty() {
None None
} else { } else {
@ -475,7 +476,7 @@ pub fn has_own_property(this: &mut Value, args: &[Value], ctx: &mut Interpreter)
.as_object() .as_object()
.as_deref() .as_deref()
.expect("Cannot get THIS object") .expect("Cannot get THIS object")
.get_own_property(&Value::string(&prop.expect("cannot get prop"))); .get_own_property(&Value::string(prop.expect("cannot get prop")));
if own_property.is_none() { if own_property.is_none() {
Ok(Value::from(false)) Ok(Value::from(false))
} else { } else {
@ -483,25 +484,22 @@ pub fn has_own_property(this: &mut Value, args: &[Value], ctx: &mut Interpreter)
} }
} }
pub fn property_is_enumerable( pub fn property_is_enumerable(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let key = match args.get(0) { let key = match args.get(0) {
None => return Ok(Value::from(false)), None => return Ok(Value::from(false)),
Some(key) => key, Some(key) => key,
}; };
let property_key = ctx.to_property_key(&mut key.clone())?; let property_key = ctx.to_property_key(key)?;
let own_property = ctx.to_object(this).map(|obj| { let own_property = ctx.to_object(this).map(|obj| {
obj.as_object() obj.as_object()
.expect("Unable to deref object") .expect("Unable to deref object")
.get_own_property(&property_key) .get_own_property(&property_key)
}); });
own_property.map_or(Ok(Value::from(false)), |own_prop| {
Ok(Value::from(own_prop.enumerable.unwrap_or(false))) Ok(own_property.map_or(Value::from(false), |own_prop| {
}) Value::from(own_prop.enumerable.unwrap_or(false))
}))
} }
/// Create a new `Object` object. /// Create a new `Object` object.

62
boa/src/builtins/regexp/mod.rs

@ -9,8 +9,6 @@
//! [spec]: https://tc39.es/ecma262/#sec-regexp-constructor //! [spec]: https://tc39.es/ecma262/#sec-regexp-constructor
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp
use std::ops::Deref;
use regex::Regex; use regex::Regex;
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
@ -18,7 +16,7 @@ use crate::{
builtins::{ builtins::{
object::{InternalState, ObjectData}, object::{InternalState, ObjectData},
property::Property, property::Property,
value::{ResultValue, Value, ValueData}, value::{RcString, ResultValue, Value},
}, },
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
@ -68,45 +66,33 @@ impl RegExp {
pub(crate) const LENGTH: usize = 2; pub(crate) const LENGTH: usize = 2;
/// Create a new `RegExp` /// Create a new `RegExp`
pub(crate) fn make_regexp( pub(crate) fn make_regexp(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value, let arg = args.get(0).ok_or_else(Value::undefined)?;
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
if args.is_empty() {
return Err(Value::undefined());
}
let mut regex_body = String::new(); let mut regex_body = String::new();
let mut regex_flags = String::new(); let mut regex_flags = String::new();
#[allow(clippy::indexing_slicing)] // length has been checked match arg {
match args[0].deref() { Value::String(ref body) => {
ValueData::String(ref body) => {
// first argument is a string -> use it as regex pattern // first argument is a string -> use it as regex pattern
regex_body = body.into(); regex_body = body.to_string();
} }
ValueData::Object(ref obj) => { Value::Object(ref obj) => {
let obj = obj.borrow(); let obj = obj.borrow();
let slots = obj.internal_slots(); let slots = obj.internal_slots();
if slots.get("RegExpMatcher").is_some() { if slots.get("RegExpMatcher").is_some() {
// first argument is another `RegExp` object, so copy its pattern and flags // first argument is another `RegExp` object, so copy its pattern and flags
if let Some(body) = slots.get("OriginalSource") { if let Some(body) = slots.get("OriginalSource") {
regex_body = ctx.to_string(body)?; regex_body = ctx.to_string(body)?.to_string();
} }
if let Some(flags) = slots.get("OriginalFlags") { if let Some(flags) = slots.get("OriginalFlags") {
regex_flags = ctx.to_string(flags)?; regex_flags = ctx.to_string(flags)?.to_string();
} }
} }
} }
_ => return Err(Value::undefined()), _ => return Err(Value::undefined()),
} }
// if a second argument is given and it's a string, use it as flags // if a second argument is given and it's a string, use it as flags
match args.get(1) { if let Some(Value::String(flags)) = args.get(1) {
None => {} regex_flags = flags.to_string();
Some(flags) => {
if let ValueData::String(flags) = flags.deref() {
regex_flags = flags.into();
}
}
} }
// parse flags // parse flags
@ -186,7 +172,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll
fn get_dot_all(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_dot_all(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all))) this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all)))
} }
@ -201,7 +187,7 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags
/// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2 /// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2
fn get_flags(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_flags(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone()))) this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone())))
} }
@ -215,7 +201,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global
fn get_global(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_global(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global))) this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global)))
} }
@ -229,7 +215,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase
fn get_ignore_case(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_ignore_case(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case))) this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case)))
} }
@ -243,7 +229,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline
fn get_multiline(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_multiline(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline))) this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline)))
} }
@ -258,7 +244,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source
fn get_source(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_source(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(this.get_internal_slot("OriginalSource")) Ok(this.get_internal_slot("OriginalSource"))
} }
@ -272,7 +258,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky
fn get_sticky(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_sticky(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky))) this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky)))
} }
@ -287,7 +273,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode
fn get_unicode(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { fn get_unicode(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode))) this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode)))
} }
@ -303,7 +289,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test
pub(crate) fn test(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn test(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?;
let mut last_index = usize::from(&this.get_field("lastIndex")); let mut last_index = usize::from(&this.get_field("lastIndex"));
let result = this.with_internal_state_ref(|regex: &RegExp| { let result = this.with_internal_state_ref(|regex: &RegExp| {
@ -336,7 +322,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec
pub(crate) fn exec(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn exec(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?;
let mut last_index = usize::from(&this.get_field("lastIndex")); let mut last_index = usize::from(&this.get_field("lastIndex"));
let result = this.with_internal_state_ref(|regex: &RegExp| { let result = this.with_internal_state_ref(|regex: &RegExp| {
@ -386,7 +372,7 @@ impl RegExp {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match
pub(crate) fn r#match(this: &mut Value, arg: String, ctx: &mut Interpreter) -> ResultValue { pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Interpreter) -> ResultValue {
let (matcher, flags) = this let (matcher, flags) = this
.with_internal_state_ref(|regex: &RegExp| (regex.matcher.clone(), regex.flags.clone())); .with_internal_state_ref(|regex: &RegExp| (regex.matcher.clone(), regex.flags.clone()));
if flags.contains('g') { if flags.contains('g') {
@ -414,7 +400,7 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let body = ctx.to_string(&this.get_internal_slot("OriginalSource"))?; let body = ctx.to_string(&this.get_internal_slot("OriginalSource"))?;
let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone()); let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone());
Ok(Value::from(format!("/{}/{}", body, flags))) Ok(Value::from(format!("/{}/{}", body, flags)))
@ -431,7 +417,7 @@ impl RegExp {
/// [spec]: https://tc39.es/ecma262/#sec-regexp-prototype-matchall /// [spec]: https://tc39.es/ecma262/#sec-regexp-prototype-matchall
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll
// TODO: it's returning an array, it should return an iterator // TODO: it's returning an array, it should return an iterator
pub(crate) fn match_all(this: &mut Value, arg_str: String) -> ResultValue { pub(crate) fn match_all(this: &Value, arg_str: String) -> ResultValue {
let matches: Vec<Value> = this.with_internal_state_ref(|regex: &RegExp| { let matches: Vec<Value> = this.with_internal_state_ref(|regex: &RegExp| {
let mut matches = Vec::new(); let mut matches = Vec::new();

176
boa/src/builtins/string/mod.rs

@ -17,7 +17,7 @@ use crate::{
builtins::{ builtins::{
object::{Object, ObjectData}, object::{Object, ObjectData},
property::Property, property::Property,
value::{ResultValue, Value, ValueData}, value::{RcString, ResultValue, Value},
RegExp, RegExp,
}, },
exec::Interpreter, exec::Interpreter,
@ -28,7 +28,6 @@ use std::string::String as StdString;
use std::{ use std::{
cmp::{max, min}, cmp::{max, min},
f64::NAN, f64::NAN,
ops::Deref,
}; };
/// JavaScript `String` implementation. /// JavaScript `String` implementation.
@ -42,13 +41,13 @@ impl String {
/// The amount of arguments this function object takes. /// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1; pub(crate) const LENGTH: usize = 1;
fn this_string_value(this: &Value, ctx: &mut Interpreter) -> Result<StdString, Value> { fn this_string_value(this: &Value, ctx: &mut Interpreter) -> Result<RcString, Value> {
match this.data() { match this {
ValueData::String(ref string) => return Ok(string.clone()), Value::String(ref string) => return Ok(string.clone()),
ValueData::Object(ref object) => { Value::Object(ref object) => {
let object = object.borrow(); let object = object.borrow();
if let Some(string) = object.as_string() { if let Some(string) = object.as_string() {
return Ok(string.to_owned()); return Ok(string);
} }
} }
_ => {} _ => {}
@ -61,16 +60,12 @@ impl String {
/// ///
/// [[Call]] - Returns a new native `string` /// [[Call]] - Returns a new native `string`
/// <https://tc39.es/ecma262/#sec-string-constructor-string-value> /// <https://tc39.es/ecma262/#sec-string-constructor-string-value>
pub(crate) fn make_string( pub(crate) fn make_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe // This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe
// to its Javascript Identifier (global constructor method name) // to its Javascript Identifier (global constructor method name)
let string = match args.get(0) { let string = match args.get(0) {
Some(ref value) => ctx.to_string(value)?, Some(ref value) => ctx.to_string(value)?,
None => StdString::new(), None => RcString::default(),
}; };
let length = string.chars().count(); let length = string.chars().count();
@ -85,7 +80,7 @@ impl String {
/// Get the string value to a primitive string /// Get the string value to a primitive string
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
#[inline] #[inline]
pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
// Get String from String Object and send it back as a new value // Get String from String Object and send it back as a new value
Ok(Value::from(Self::this_string_value(this, ctx)?)) Ok(Value::from(Self::this_string_value(this, ctx)?))
} }
@ -106,7 +101,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt
pub(crate) fn char_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn char_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -148,11 +143,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
pub(crate) fn char_code_at( pub(crate) fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -192,11 +183,11 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat
pub(crate) fn concat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let object = ctx.require_object_coercible(this)?; let object = ctx.require_object_coercible(this)?;
let mut string = ctx.to_string(object)?; let mut string = ctx.to_string(object)?.to_string();
for arg in args { for arg in args {
string.push_str(&ctx.to_string(arg)?); string.push_str(&ctx.to_string(arg)?);
@ -216,7 +207,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
pub(crate) fn repeat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -239,7 +230,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice
pub(crate) fn slice(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -286,11 +277,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
pub(crate) fn starts_with( pub(crate) fn starts_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -319,7 +306,7 @@ impl String {
} else { } else {
// Only use the part of the string from "start" // Only use the part of the string from "start"
let this_string: StdString = primitive_val.chars().skip(start as usize).collect(); let this_string: StdString = primitive_val.chars().skip(start as usize).collect();
Ok(Value::from(this_string.starts_with(&search_string))) Ok(Value::from(this_string.starts_with(search_string.as_str())))
} }
} }
@ -333,11 +320,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
pub(crate) fn ends_with( pub(crate) fn ends_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -367,7 +350,7 @@ impl String {
} else { } else {
// Only use the part of the string up to "end" // Only use the part of the string up to "end"
let this_string: StdString = primitive_val.chars().take(end as usize).collect(); let this_string: StdString = primitive_val.chars().take(end as usize).collect();
Ok(Value::from(this_string.ends_with(&search_string))) Ok(Value::from(this_string.ends_with(search_string.as_str())))
} }
} }
@ -381,7 +364,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
pub(crate) fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn includes(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -406,14 +389,14 @@ impl String {
// Take the string from "this" and use only the part of it after "start" // Take the string from "this" and use only the part of it after "start"
let this_string: StdString = primitive_val.chars().skip(start as usize).collect(); let this_string: StdString = primitive_val.chars().skip(start as usize).collect();
Ok(Value::from(this_string.contains(&search_string))) Ok(Value::from(this_string.contains(search_string.as_str())))
} }
/// Return either the string itself or the string of the regex equivalent /// Return either the string itself or the string of the regex equivalent
fn get_regex_string(value: &Value) -> StdString { fn get_regex_string(value: &Value) -> StdString {
match value.deref() { match value {
ValueData::String(ref body) => body.into(), Value::String(ref body) => body.to_string(),
ValueData::Object(ref obj) => { Value::Object(ref obj) => {
let obj = obj.borrow(); let obj = obj.borrow();
if obj.internal_slots().get("RegExpMatcher").is_some() { if obj.internal_slots().get("RegExpMatcher").is_some() {
@ -443,7 +426,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace
pub(crate) fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// TODO: Support Symbol replacer // TODO: Support Symbol replacer
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
if args.is_empty() { if args.is_empty() {
@ -460,8 +443,8 @@ impl String {
let replace_value = if args.len() > 1 { let replace_value = if args.len() > 1 {
// replace_object could be a string or function or not exist at all // replace_object could be a string or function or not exist at all
let replace_object: &Value = args.get(1).expect("second argument expected"); let replace_object: &Value = args.get(1).expect("second argument expected");
match replace_object.deref() { match replace_object {
ValueData::String(val) => { Value::String(val) => {
// https://tc39.es/ecma262/#table-45 // https://tc39.es/ecma262/#table-45
let mut result = val.to_string(); let mut result = val.to_string();
let re = Regex::new(r"\$(\d)").unwrap(); let re = Regex::new(r"\$(\d)").unwrap();
@ -500,7 +483,7 @@ impl String {
result result
} }
ValueData::Object(_) => { Value::Object(_) => {
// This will return the matched substring first, then captured parenthesized groups later // This will return the matched substring first, then captured parenthesized groups later
let mut results: Vec<Value> = caps let mut results: Vec<Value> = caps
.iter() .iter()
@ -518,7 +501,7 @@ impl String {
let result = ctx.call(&replace_object, this, &results).unwrap(); let result = ctx.call(&replace_object, this, &results).unwrap();
ctx.to_string(&result)? ctx.to_string(&result)?.to_string()
} }
_ => "undefined".to_string(), _ => "undefined".to_string(),
} }
@ -545,7 +528,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf
pub(crate) fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -573,7 +556,7 @@ impl String {
// checking "starts with" the search string // checking "starts with" the search string
for index in start..length { for index in start..length {
let this_string: StdString = primitive_val.chars().skip(index as usize).collect(); let this_string: StdString = primitive_val.chars().skip(index as usize).collect();
if this_string.starts_with(&search_string) { if this_string.starts_with(search_string.as_str()) {
// Explicitly return early with the index value // Explicitly return early with the index value
return Ok(Value::from(index)); return Ok(Value::from(index));
} }
@ -595,7 +578,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
pub(crate) fn last_index_of( pub(crate) fn last_index_of(
this: &mut Value, this: &Value,
args: &[Value], args: &[Value],
ctx: &mut Interpreter, ctx: &mut Interpreter,
) -> ResultValue { ) -> ResultValue {
@ -627,7 +610,7 @@ impl String {
let mut highest_index = -1; let mut highest_index = -1;
for index in start..length { for index in start..length {
let this_string: StdString = primitive_val.chars().skip(index as usize).collect(); let this_string: StdString = primitive_val.chars().skip(index as usize).collect();
if this_string.starts_with(&search_string) { if this_string.starts_with(search_string.as_str()) {
highest_index = index; highest_index = index;
} }
} }
@ -647,10 +630,9 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
pub(crate) fn r#match(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn r#match(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let mut re = let re = RegExp::make_regexp(&Value::from(Object::default()), &[args[0].clone()], ctx)?;
RegExp::make_regexp(&mut Value::from(Object::default()), &[args[0].clone()], ctx)?; RegExp::r#match(&re, ctx.to_string(this)?, ctx)
RegExp::r#match(&mut re, ctx.to_string(this)?, ctx)
} }
/// Abstract method `StringPad`. /// Abstract method `StringPad`.
@ -658,9 +640,9 @@ impl String {
/// Performs the actual string padding for padStart/End. /// Performs the actual string padding for padStart/End.
/// <https://tc39.es/ecma262/#sec-stringpad/> /// <https://tc39.es/ecma262/#sec-stringpad/>
fn string_pad( fn string_pad(
primitive: StdString, primitive: RcString,
max_length: i32, max_length: i32,
fill_string: Option<StdString>, fill_string: Option<RcString>,
at_start: bool, at_start: bool,
) -> ResultValue { ) -> ResultValue {
let primitive_length = primitive.len() as i32; let primitive_length = primitive.len() as i32;
@ -669,20 +651,13 @@ impl String {
return Ok(Value::from(primitive)); return Ok(Value::from(primitive));
} }
let filler = match fill_string { let filter = fill_string.as_deref().unwrap_or(" ");
Some(filler) => filler,
None => " ".to_owned(),
};
if filler == "" {
return Ok(Value::from(primitive));
}
let fill_len = max_length.wrapping_sub(primitive_length); let fill_len = max_length.wrapping_sub(primitive_length);
let mut fill_str = StdString::new(); let mut fill_str = StdString::new();
while fill_str.len() < fill_len as usize { while fill_str.len() < fill_len as usize {
fill_str.push_str(&filler); fill_str.push_str(filter);
} }
// Cut to size max_length // Cut to size max_length
let concat_fill_str: StdString = fill_str.chars().take(fill_len as usize).collect(); let concat_fill_str: StdString = fill_str.chars().take(fill_len as usize).collect();
@ -706,8 +681,8 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padend /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd
pub(crate) fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn pad_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val = ctx.to_string(this)?; let primitive = ctx.to_string(this)?;
if args.is_empty() { if args.is_empty() {
return Err(Value::from("padEnd requires maxLength argument")); return Err(Value::from("padEnd requires maxLength argument"));
} }
@ -716,13 +691,9 @@ impl String {
.expect("failed to get argument for String method"), .expect("failed to get argument for String method"),
); );
let fill_string = if args.len() != 1 { let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?;
Some(ctx.to_string(args.get(1).expect("Could not get argument"))?)
} else {
None
};
Self::string_pad(primitive_val, max_length, fill_string, false) Self::string_pad(primitive, max_length, fill_string, false)
} }
/// `String.prototype.padStart( targetLength [, padString] )` /// `String.prototype.padStart( targetLength [, padString] )`
@ -737,12 +708,8 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
pub(crate) fn pad_start( pub(crate) fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value, let primitive = ctx.to_string(this)?;
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let primitive_val = ctx.to_string(this)?;
if args.is_empty() { if args.is_empty() {
return Err(Value::from("padStart requires maxLength argument")); return Err(Value::from("padStart requires maxLength argument"));
} }
@ -751,12 +718,9 @@ impl String {
.expect("failed to get argument for String method"), .expect("failed to get argument for String method"),
); );
let fill_string = match args.len() { let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?;
1 => None,
_ => Some(ctx.to_string(args.get(1).expect("Could not get argument"))?),
};
Self::string_pad(primitive_val, max_length, fill_string, true) Self::string_pad(primitive, max_length, fill_string, true)
} }
/// Helper function to check if a `char` is trimmable. /// Helper function to check if a `char` is trimmable.
@ -790,7 +754,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
pub(crate) fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn trim(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str = ctx.to_string(this)?; let this_str = ctx.to_string(this)?;
Ok(Value::from( Ok(Value::from(
this_str.trim_matches(Self::is_trimmable_whitespace), this_str.trim_matches(Self::is_trimmable_whitespace),
@ -809,7 +773,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart
pub(crate) fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn trim_start(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str = ctx.to_string(this)?; let this_str = ctx.to_string(this)?;
Ok(Value::from( Ok(Value::from(
this_str.trim_start_matches(Self::is_trimmable_whitespace), this_str.trim_start_matches(Self::is_trimmable_whitespace),
@ -828,7 +792,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd
pub(crate) fn trim_end(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn trim_end(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str = ctx.to_string(this)?; let this_str = ctx.to_string(this)?;
Ok(Value::from( Ok(Value::from(
this_str.trim_end_matches(Self::is_trimmable_whitespace), this_str.trim_end_matches(Self::is_trimmable_whitespace),
@ -846,11 +810,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_lowercase( pub(crate) fn to_lowercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
_: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let this_str = ctx.to_string(this)?; let this_str = ctx.to_string(this)?;
@ -872,11 +832,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_uppercase( pub(crate) fn to_uppercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
_: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let this_str = ctx.to_string(this)?; let this_str = ctx.to_string(this)?;
@ -895,11 +851,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring
pub(crate) fn substring( pub(crate) fn substring(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -947,7 +899,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr
/// <https://tc39.es/ecma262/#sec-string.prototype.substr> /// <https://tc39.es/ecma262/#sec-string.prototype.substr>
pub(crate) fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn substr(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// First we get it the actual string a private field stored on the object only the engine has access to. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?; let primitive_val = ctx.to_string(this)?;
@ -1002,7 +954,7 @@ impl String {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf
pub(crate) fn value_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn value_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// Use the to_string method because it is specified to do the same thing in this case // Use the to_string method because it is specified to do the same thing in this case
Self::to_string(this, args, ctx) Self::to_string(this, args, ctx)
} }
@ -1020,22 +972,18 @@ impl String {
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
/// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges /// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges
// TODO: update this method to return iterator // TODO: update this method to return iterator
pub(crate) fn match_all( pub(crate) fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
this: &mut Value, let re: Value = match args.get(0) {
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let mut re: Value = match args.get(0) {
Some(arg) => { Some(arg) => {
if arg.is_null() { if arg.is_null() {
RegExp::make_regexp( RegExp::make_regexp(
&mut Value::from(Object::default()), &Value::from(Object::default()),
&[Value::from(ctx.to_string(arg)?), Value::from("g")], &[Value::from(ctx.to_string(arg)?), Value::from("g")],
ctx, ctx,
) )
} else if arg.is_undefined() { } else if arg.is_undefined() {
RegExp::make_regexp( RegExp::make_regexp(
&mut Value::from(Object::default()), &Value::from(Object::default()),
&[Value::undefined(), Value::from("g")], &[Value::undefined(), Value::from("g")],
ctx, ctx,
) )
@ -1044,13 +992,13 @@ impl String {
} }
} }
None => RegExp::make_regexp( None => RegExp::make_regexp(
&mut Value::from(Object::default()), &Value::from(Object::default()),
&[Value::from(""), Value::from("g")], &[Value::from(""), Value::from("g")],
ctx, ctx,
), ),
}?; }?;
RegExp::match_all(&mut re, ctx.to_string(this)?) RegExp::match_all(&re, ctx.to_string(this)?.to_string())
} }
/// Create a new `String` object. /// Create a new `String` object.

22
boa/src/builtins/symbol/mod.rs

@ -20,14 +20,14 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn}; use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{ use crate::{
builtins::value::{ResultValue, Value, ValueData}, builtins::value::{RcString, RcSymbol, ResultValue, Value},
exec::Interpreter, exec::Interpreter,
BoaProfiler, BoaProfiler,
}; };
use gc::{Finalize, Trace}; use gc::{Finalize, Trace};
#[derive(Debug, Finalize, Trace, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] #[derive(Debug, Finalize, Trace, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Symbol(Option<Box<str>>, u32); pub struct Symbol(Option<RcString>, u32);
impl Symbol { impl Symbol {
/// The name of the object. /// The name of the object.
@ -46,13 +46,13 @@ impl Symbol {
self.1 self.1
} }
fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result<Self, Value> { fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result<RcSymbol, Value> {
match value.data() { match value {
ValueData::Symbol(ref symbol) => return Ok(symbol.clone()), Value::Symbol(ref symbol) => return Ok(symbol.clone()),
ValueData::Object(ref object) => { Value::Object(ref object) => {
let object = object.borrow(); let object = object.borrow();
if let Some(symbol) = object.as_symbol() { if let Some(symbol) = object.as_symbol() {
return Ok(symbol.clone()); return Ok(symbol);
} }
} }
_ => {} _ => {}
@ -72,11 +72,9 @@ impl Symbol {
/// ///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-description /// [spec]: https://tc39.es/ecma262/#sec-symbol-description
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol
pub(crate) fn call(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn call(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let description = match args.get(0) { let description = match args.get(0) {
Some(ref value) if !value.is_undefined() => { Some(ref value) if !value.is_undefined() => Some(ctx.to_string(value)?),
Some(ctx.to_string(value)?.into_boxed_str())
}
_ => None, _ => None,
}; };
@ -94,7 +92,7 @@ impl Symbol {
/// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let symbol = Self::this_symbol_value(this, ctx)?; let symbol = Self::this_symbol_value(this, ctx)?;
let description = symbol.description().unwrap_or(""); let description = symbol.description().unwrap_or("");
Ok(Value::from(format!("Symbol({})", description))) Ok(Value::from(format!("Symbol({})", description)))

34
boa/src/builtins/value/conversions.rs

@ -16,7 +16,7 @@ impl From<String> for Value {
impl From<Box<str>> for Value { impl From<Box<str>> for Value {
fn from(value: Box<str>) -> Self { fn from(value: Box<str>) -> Self {
Self::string(value) Self::string(String::from(value))
} }
} }
@ -38,6 +38,12 @@ impl From<char> for Value {
} }
} }
impl From<RcString> for Value {
fn from(value: RcString) -> Self {
Value::String(value)
}
}
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct TryFromCharError; pub struct TryFromCharError;
@ -89,6 +95,12 @@ impl From<BigInt> for Value {
} }
} }
impl From<RcBigInt> for Value {
fn from(value: RcBigInt) -> Self {
Value::BigInt(value)
}
}
impl From<usize> for Value { impl From<usize> for Value {
fn from(value: usize) -> Value { fn from(value: usize) -> Value {
Value::integer(value as i32) Value::integer(value as i32)
@ -120,7 +132,7 @@ where
let mut array = Object::default(); let mut array = Object::default();
for (i, item) in value.iter().enumerate() { for (i, item) in value.iter().enumerate() {
array.properties_mut().insert( array.properties_mut().insert(
i.to_string(), RcString::from(i.to_string()),
Property::default().value(item.clone().into()), Property::default().value(item.clone().into()),
); );
} }
@ -135,9 +147,10 @@ where
fn from(value: Vec<T>) -> Self { fn from(value: Vec<T>) -> Self {
let mut array = Object::default(); let mut array = Object::default();
for (i, item) in value.into_iter().enumerate() { for (i, item) in value.into_iter().enumerate() {
array array.properties_mut().insert(
.properties_mut() RcString::from(i.to_string()),
.insert(i.to_string(), Property::default().value(item.into())); Property::default().value(item.into()),
);
} }
Value::from(array) Value::from(array)
} }
@ -159,17 +172,6 @@ impl Display for TryFromObjectError {
} }
} }
impl TryFrom<&Value> for Object {
type Error = TryFromObjectError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
match value.data() {
ValueData::Object(ref object) => Ok(object.clone().into_inner()),
_ => Err(TryFromObjectError),
}
}
}
impl From<()> for Value { impl From<()> for Value {
fn from(_: ()) -> Self { fn from(_: ()) -> Self {
Value::null() Value::null()

25
boa/src/builtins/value/display.rs

@ -1,11 +1,5 @@
use super::*; use super::*;
impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.data(), f)
}
}
/// A helper macro for printing objects /// A helper macro for printing objects
/// Can be used to print both properties and internal slots /// Can be used to print both properties and internal slots
/// All of the overloads take: /// All of the overloads take:
@ -64,10 +58,10 @@ macro_rules! print_obj_value {
}; };
} }
pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String { pub(crate) fn log_string_from(x: &Value, print_internals: bool) -> String {
match x { match x {
// We don't want to print private (compiler) or prototype properties // We don't want to print private (compiler) or prototype properties
ValueData::Object(ref v) => { Value::Object(ref v) => {
// Can use the private "type" field of an Object to match on // Can use the private "type" field of an Object to match on
// which type of Object it represents for special printing // which type of Object it represents for special printing
match v.borrow().data { match v.borrow().data {
@ -95,7 +89,7 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String {
log_string_from( log_string_from(
&v.borrow() &v.borrow()
.properties() .properties()
.get(&i.to_string()) .get(i.to_string().as_str())
.unwrap() .unwrap()
.value .value
.clone() .clone()
@ -111,16 +105,13 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String {
_ => display_obj(&x, print_internals), _ => display_obj(&x, print_internals),
} }
} }
ValueData::Symbol(ref symbol) => match symbol.description() { Value::Symbol(ref symbol) => symbol.to_string(),
Some(ref desc) => format!("Symbol({})", desc),
None => String::from("Symbol()"),
},
_ => format!("{}", x), _ => format!("{}", x),
} }
} }
/// A helper function for specifically printing object values /// A helper function for specifically printing object values
pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String {
// A simple helper for getting the address of a value // A simple helper for getting the address of a value
// TODO: Find a more general place for this, as it can be used in other situations as well // TODO: Find a more general place for this, as it can be used in other situations as well
fn address_of<T>(t: &T) -> usize { fn address_of<T>(t: &T) -> usize {
@ -133,12 +124,12 @@ pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String {
let mut encounters = HashSet::new(); let mut encounters = HashSet::new();
fn display_obj_internal( fn display_obj_internal(
data: &ValueData, data: &Value,
encounters: &mut HashSet<usize>, encounters: &mut HashSet<usize>,
indent: usize, indent: usize,
print_internals: bool, print_internals: bool,
) -> String { ) -> String {
if let ValueData::Object(ref v) = *data { if let Value::Object(ref v) = *data {
// The in-memory address of the current object // The in-memory address of the current object
let addr = address_of(v.borrow().deref()); let addr = address_of(v.borrow().deref());
@ -175,7 +166,7 @@ pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String {
display_obj_internal(v, &mut encounters, 4, print_internals) display_obj_internal(v, &mut encounters, 4, print_internals)
} }
impl Display for ValueData { impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self { match self {
Self::Null => write!(f, "null"), Self::Null => write!(f, "null"),

136
boa/src/builtins/value/equality.rs

@ -14,20 +14,20 @@ impl Value {
return false; return false;
} }
match (self.data(), other.data()) { match (self, other) {
// 2. If Type(x) is Number or BigInt, then // 2. If Type(x) is Number or BigInt, then
// a. Return ! Type(x)::equal(x, y). // a. Return ! Type(x)::equal(x, y).
(ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::equal(x, y), (Self::BigInt(x), Self::BigInt(y)) => BigInt::equal(x, y),
(ValueData::Rational(x), ValueData::Rational(y)) => Number::equal(*x, *y), (Self::Rational(x), Self::Rational(y)) => Number::equal(*x, *y),
(ValueData::Rational(x), ValueData::Integer(y)) => Number::equal(*x, f64::from(*y)), (Self::Rational(x), Self::Integer(y)) => Number::equal(*x, f64::from(*y)),
(ValueData::Integer(x), ValueData::Rational(y)) => Number::equal(f64::from(*x), *y), (Self::Integer(x), Self::Rational(y)) => Number::equal(f64::from(*x), *y),
(ValueData::Integer(x), ValueData::Integer(y)) => x == y, (Self::Integer(x), Self::Integer(y)) => x == y,
//Null has to be handled specially because "typeof null" returns object and if we managed //Null has to be handled specially because "typeof null" returns object and if we managed
//this without a special case we would compare self and other as if they were actually //this without a special case we would compare self and other as if they were actually
//objects which unfortunately fails //objects which unfortunately fails
//Specification Link: https://tc39.es/ecma262/#sec-typeof-operator //Specification Link: https://tc39.es/ecma262/#sec-typeof-operator
(ValueData::Null, ValueData::Null) => true, (Self::Null, Self::Null) => true,
// 3. Return ! SameValueNonNumeric(x, y). // 3. Return ! SameValueNonNumeric(x, y).
(_, _) => same_value_non_numeric(self, other), (_, _) => same_value_non_numeric(self, other),
@ -39,28 +39,28 @@ impl Value {
/// This method is executed when doing abstract equality comparisons with the `==` operator. /// This method is executed when doing abstract equality comparisons with the `==` operator.
/// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison> /// For more information, check <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
#[allow(clippy::float_cmp)] #[allow(clippy::float_cmp)]
pub fn equals(&mut self, other: &mut Self, interpreter: &mut Interpreter) -> bool { pub fn equals(&self, other: &Self, interpreter: &mut Interpreter) -> Result<bool, Value> {
// 1. If Type(x) is the same as Type(y), then // 1. If Type(x) is the same as Type(y), then
// a. Return the result of performing Strict Equality Comparison x === y. // a. Return the result of performing Strict Equality Comparison x === y.
if self.get_type() == other.get_type() { if self.get_type() == other.get_type() {
return self.strict_equals(other); return Ok(self.strict_equals(other));
} }
match (self.data(), other.data()) { Ok(match (self, other) {
// 2. If x is null and y is undefined, return true. // 2. If x is null and y is undefined, return true.
// 3. If x is undefined and y is null, return true. // 3. If x is undefined and y is null, return true.
_ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, (Self::Null, Self::Undefined) | (Self::Undefined, Self::Null) => true,
// 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y). // 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y).
// 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y. // 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y.
// //
// https://github.com/rust-lang/rust/issues/54883 // https://github.com/rust-lang/rust/issues/54883
(ValueData::Integer(_), ValueData::String(_)) (Self::Integer(_), Self::String(_))
| (ValueData::Rational(_), ValueData::String(_)) | (Self::Rational(_), Self::String(_))
| (ValueData::String(_), ValueData::Integer(_)) | (Self::String(_), Self::Integer(_))
| (ValueData::String(_), ValueData::Rational(_)) | (Self::String(_), Self::Rational(_))
| (ValueData::Rational(_), ValueData::Boolean(_)) | (Self::Rational(_), Self::Boolean(_))
| (ValueData::Integer(_), ValueData::Boolean(_)) => { | (Self::Integer(_), Self::Boolean(_)) => {
let a: &Value = self.borrow(); let a: &Value = self.borrow();
let b: &Value = other.borrow(); let b: &Value = other.borrow();
Number::equal(f64::from(a), f64::from(b)) Number::equal(f64::from(a), f64::from(b))
@ -70,52 +70,52 @@ impl Value {
// a. Let n be ! StringToBigInt(y). // a. Let n be ! StringToBigInt(y).
// b. If n is NaN, return false. // b. If n is NaN, return false.
// c. Return the result of the comparison x == n. // c. Return the result of the comparison x == n.
(ValueData::BigInt(ref a), ValueData::String(ref b)) => match string_to_bigint(b) { (Self::BigInt(ref a), Self::String(ref b)) => match string_to_bigint(b) {
Some(ref b) => a == b, Some(ref b) => a.as_inner() == b,
None => false, None => false,
}, },
// 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x.
(ValueData::String(ref a), ValueData::BigInt(ref b)) => match string_to_bigint(a) { (Self::String(ref a), Self::BigInt(ref b)) => match string_to_bigint(a) {
Some(ref a) => a == b, Some(ref a) => a == b.as_inner(),
None => false, None => false,
}, },
// 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y.
(ValueData::Boolean(_), _) => { (Self::Boolean(_), _) => {
other.equals(&mut Value::from(self.to_integer()), interpreter) return other.equals(&Value::from(self.to_integer()), interpreter)
} }
// 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y). // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y).
(_, ValueData::Boolean(_)) => { (_, Self::Boolean(_)) => {
self.equals(&mut Value::from(other.to_integer()), interpreter) return self.equals(&Value::from(other.to_integer()), interpreter)
} }
// 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result
// of the comparison x == ? ToPrimitive(y). // of the comparison x == ? ToPrimitive(y).
(ValueData::Object(_), _) => { (Self::Object(_), _) => {
let mut primitive = interpreter.to_primitive(self, PreferredType::Default); let primitive = interpreter.to_primitive(self, PreferredType::Default)?;
primitive.equals(other, interpreter) return primitive.equals(other, interpreter);
} }
// 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result
// of the comparison ? ToPrimitive(x) == y. // of the comparison ? ToPrimitive(x) == y.
(_, ValueData::Object(_)) => { (_, Self::Object(_)) => {
let mut primitive = interpreter.to_primitive(other, PreferredType::Default); let primitive = interpreter.to_primitive(other, PreferredType::Default)?;
primitive.equals(self, interpreter) return primitive.equals(self, interpreter);
} }
// 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then
// a. If x or y are any of NaN, +∞, or -∞, return false. // a. If x or y are any of NaN, +∞, or -∞, return false.
// b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false. // b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false.
(ValueData::BigInt(ref a), ValueData::Rational(ref b)) => a == b, (Self::BigInt(ref a), Self::Rational(ref b)) => a.as_inner() == b,
(ValueData::Rational(ref a), ValueData::BigInt(ref b)) => a == b, (Self::Rational(ref a), Self::BigInt(ref b)) => a == b.as_inner(),
(ValueData::BigInt(ref a), ValueData::Integer(ref b)) => a == b, (Self::BigInt(ref a), Self::Integer(ref b)) => a.as_inner() == b,
(ValueData::Integer(ref a), ValueData::BigInt(ref b)) => a == b, (Self::Integer(ref a), Self::BigInt(ref b)) => a == b.as_inner(),
// 13. Return false. // 13. Return false.
_ => false, _ => false,
} })
} }
} }
@ -137,60 +137,54 @@ pub fn string_to_bigint(string: &str) -> Option<BigInt> {
/// The internal comparison abstract operation SameValue(x, y), /// The internal comparison abstract operation SameValue(x, y),
/// where x and y are ECMAScript language values, produces true or false. /// 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 /// More information:
/// strict mode currently compares the pointers /// - [ECMAScript][spec]
pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { ///
if strict { /// [spec]: https://tc39.es/ecma262/#sec-samevalue
// Do both Values point to the same underlying valueData? pub fn same_value(x: &Value, y: &Value) -> bool {
return std::ptr::eq(x.data(), y.data());
}
// 1. If Type(x) is different from Type(y), return false. // 1. If Type(x) is different from Type(y), return false.
if x.get_type() != y.get_type() { if x.get_type() != y.get_type() {
return false; return false;
} }
match (x.data(), y.data()) { match (x, y) {
// 2. If Type(x) is Number or BigInt, then // 2. If Type(x) is Number or BigInt, then
// a. Return ! Type(x)::SameValue(x, y). // a. Return ! Type(x)::SameValue(x, y).
(ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::same_value(x, y), (Value::BigInt(x), Value::BigInt(y)) => BigInt::same_value(x, y),
(ValueData::Rational(x), ValueData::Rational(y)) => Number::same_value(*x, *y), (Value::Rational(x), Value::Rational(y)) => Number::same_value(*x, *y),
(ValueData::Rational(x), ValueData::Integer(y)) => Number::same_value(*x, f64::from(*y)), (Value::Rational(x), Value::Integer(y)) => Number::same_value(*x, f64::from(*y)),
(ValueData::Integer(x), ValueData::Rational(y)) => Number::same_value(f64::from(*x), *y), (Value::Integer(x), Value::Rational(y)) => Number::same_value(f64::from(*x), *y),
(ValueData::Integer(x), ValueData::Integer(y)) => x == y, (Value::Integer(x), Value::Integer(y)) => x == y,
// 3. Return ! SameValueNonNumeric(x, y). // 3. Return ! SameValueNonNumeric(x, y).
(_, _) => same_value_non_numeric(x, y), (_, _) => same_value_non_numeric(x, y),
} }
} }
/// The internal comparison abstract operation SameValueZero(x, y), /// The internal comparison abstract operation `SameValueZero(x, y)`,
/// where x and y are ECMAScript language values, produces true or false. /// where `x` and `y` are ECMAScript language values, produces `true` or `false`.
/// SameValueZero differs from SameValue only in its treatment of +0 and -0. ///
/// `SameValueZero` differs from SameValue only in its treatment of `+0` and `-0`.
/// ///
/// Such a comparison is performed as follows: /// More information:
/// - [ECMAScript][spec]
/// ///
/// <https://tc39.es/ecma262/#sec-samevaluezero> /// [spec]: https://tc39.es/ecma262/#sec-samevaluezero
pub fn same_value_zero(x: &Value, y: &Value) -> bool { pub fn same_value_zero(x: &Value, y: &Value) -> bool {
if x.get_type() != y.get_type() { if x.get_type() != y.get_type() {
return false; return false;
} }
match (x.data(), y.data()) { match (x, y) {
// 2. If Type(x) is Number or BigInt, then // 2. If Type(x) is Number or BigInt, then
// a. Return ! Type(x)::SameValueZero(x, y). // a. Return ! Type(x)::SameValueZero(x, y).
(ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::same_value_zero(x, y), (Value::BigInt(x), Value::BigInt(y)) => BigInt::same_value_zero(x, y),
(ValueData::Rational(x), ValueData::Rational(y)) => Number::same_value_zero(*x, *y), (Value::Rational(x), Value::Rational(y)) => Number::same_value_zero(*x, *y),
(ValueData::Rational(x), ValueData::Integer(y)) => { (Value::Rational(x), Value::Integer(y)) => Number::same_value_zero(*x, f64::from(*y)),
Number::same_value_zero(*x, f64::from(*y)) (Value::Integer(x), Value::Rational(y)) => Number::same_value_zero(f64::from(*x), *y),
} (Value::Integer(x), Value::Integer(y)) => x == y,
(ValueData::Integer(x), ValueData::Rational(y)) => {
Number::same_value_zero(f64::from(*x), *y)
}
(ValueData::Integer(x), ValueData::Integer(y)) => x == y,
// 3. Return ! SameValueNonNumeric(x, y). // 3. Return ! SameValueNonNumeric(x, y).
(_, _) => same_value_non_numeric(x, y), (_, _) => same_value_non_numeric(x, y),
@ -199,11 +193,11 @@ pub fn same_value_zero(x: &Value, y: &Value) -> bool {
fn same_value_non_numeric(x: &Value, y: &Value) -> bool { fn same_value_non_numeric(x: &Value, y: &Value) -> bool {
debug_assert!(x.get_type() == y.get_type()); debug_assert!(x.get_type() == y.get_type());
match x.get_type() { match (x, y) {
Type::Null | Type::Undefined => true, (Value::Null, Value::Null) | (Value::Undefined, Value::Undefined) => true,
Type::String => x.to_string() == y.to_string(), (Value::String(ref x), Value::String(ref y)) => x == y,
Type::Boolean => bool::from(x) == bool::from(y), (Value::Boolean(x), Value::Boolean(y)) => x == y,
Type::Object => std::ptr::eq(x.data(), y.data()), (Value::Object(ref x), Value::Object(ref y)) => GcObject::equals(x, y),
_ => false, _ => false,
} }
} }

21
boa/src/builtins/value/hash.rs

@ -38,17 +38,16 @@ impl Hash for RationalHashable {
impl Hash for Value { impl Hash for Value {
fn hash<H: Hasher>(&self, state: &mut H) { fn hash<H: Hasher>(&self, state: &mut H) {
let data = self.data(); match self {
match data { Self::Undefined => UndefinedHashable.hash(state),
ValueData::Undefined => UndefinedHashable.hash(state), Self::Null => NullHashable.hash(state),
ValueData::Null => NullHashable.hash(state), Self::String(ref string) => string.hash(state),
ValueData::String(ref string) => string.hash(state), Self::Boolean(boolean) => boolean.hash(state),
ValueData::Boolean(boolean) => boolean.hash(state), Self::Integer(integer) => integer.hash(state),
ValueData::Integer(integer) => integer.hash(state), Self::BigInt(ref bigint) => bigint.hash(state),
ValueData::BigInt(ref bigint) => bigint.hash(state), Self::Rational(rational) => RationalHashable(*rational).hash(state),
ValueData::Rational(rational) => RationalHashable(*rational).hash(state), Self::Symbol(ref symbol) => Hash::hash(symbol, state),
ValueData::Symbol(ref symbol) => Hash::hash(symbol, state), Self::Object(ref object) => std::ptr::hash(object.as_ref(), state),
ValueData::Object(_) => std::ptr::hash(data, state),
} }
} }
} }

141
boa/src/builtins/value/mod.rs

@ -11,13 +11,16 @@ pub use crate::builtins::value::val_type::Type;
use crate::builtins::{ use crate::builtins::{
function::Function, function::Function,
object::{InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{
GcObject, InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE,
PROTOTYPE,
},
property::Property, property::Property,
BigInt, Symbol, BigInt, Symbol,
}; };
use crate::exec::Interpreter; use crate::exec::Interpreter;
use crate::BoaProfiler; use crate::BoaProfiler;
use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace}; use gc::{Finalize, GcCellRef, GcCellRefMut, Trace};
use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue};
use std::{ use std::{
any::Any, any::Any,
@ -34,32 +37,57 @@ pub mod display;
pub mod equality; pub mod equality;
pub mod hash; pub mod hash;
pub mod operations; pub mod operations;
pub mod rcbigint;
pub mod rcstring;
pub mod rcsymbol;
pub use conversions::*; pub use conversions::*;
pub(crate) use display::display_obj; pub(crate) use display::display_obj;
pub use equality::*; pub use equality::*;
pub use hash::*; pub use hash::*;
pub use operations::*; pub use operations::*;
pub use rcbigint::RcBigInt;
pub use rcstring::RcString;
pub use rcsymbol::RcSymbol;
/// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`)
#[must_use] #[must_use]
pub type ResultValue = Result<Value, Value>; pub type ResultValue = Result<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter. /// A Javascript value
#[derive(Debug, Clone, Trace, Finalize, Default)] #[derive(Trace, Finalize, Debug, Clone)]
pub struct Value(Gc<ValueData>); pub enum Value {
/// `null` - A null value, for when a value doesn't exist.
Null,
/// `undefined` - An undefined value, for when a field or index doesn't exist.
Undefined,
/// `boolean` - A `true` / `false` value, for if a certain criteria is met.
Boolean(bool),
/// `String` - A UTF-8 string, such as `"Hello, world"`.
String(RcString),
/// `Number` - A 64-bit floating point number, such as `3.1415`
Rational(f64),
/// `Number` - A 32-bit integer, such as `42`.
Integer(i32),
/// `BigInt` - holds any arbitrary large signed integer.
BigInt(RcBigInt),
/// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values.
Object(GcObject),
/// `Symbol` - A Symbol Primitive type.
Symbol(RcSymbol),
}
impl Value { impl Value {
/// Creates a new `undefined` value. /// Creates a new `undefined` value.
#[inline] #[inline]
pub fn undefined() -> Self { pub fn undefined() -> Self {
Self(Gc::new(ValueData::Undefined)) Self::Undefined
} }
/// Creates a new `null` value. /// Creates a new `null` value.
#[inline] #[inline]
pub fn null() -> Self { pub fn null() -> Self {
Self(Gc::new(ValueData::Null)) Self::Null
} }
/// Creates a new number with `NaN` value. /// Creates a new number with `NaN` value.
@ -72,9 +100,9 @@ impl Value {
#[inline] #[inline]
pub fn string<S>(value: S) -> Self pub fn string<S>(value: S) -> Self
where where
S: Into<String>, S: Into<RcString>,
{ {
Self(Gc::new(ValueData::String(value.into()))) Self::String(value.into())
} }
/// Creates a new number value. /// Creates a new number value.
@ -83,7 +111,7 @@ impl Value {
where where
N: Into<f64>, N: Into<f64>,
{ {
Self(Gc::new(ValueData::Rational(value.into()))) Self::Rational(value.into())
} }
/// Creates a new number value. /// Creates a new number value.
@ -92,7 +120,7 @@ impl Value {
where where
I: Into<i32>, I: Into<i32>,
{ {
Self(Gc::new(ValueData::Integer(value.into()))) Self::Integer(value.into())
} }
/// Creates a new number value. /// Creates a new number value.
@ -106,38 +134,35 @@ impl Value {
/// Creates a new bigint value. /// Creates a new bigint value.
#[inline] #[inline]
pub fn bigint(value: BigInt) -> Self { pub fn bigint<B>(value: B) -> Self
Self(Gc::new(ValueData::BigInt(value))) where
B: Into<RcBigInt>,
{
Self::BigInt(value.into())
} }
/// Creates a new boolean value. /// Creates a new boolean value.
#[inline] #[inline]
pub fn boolean(value: bool) -> Self { pub fn boolean(value: bool) -> Self {
Self(Gc::new(ValueData::Boolean(value))) Self::Boolean(value)
} }
/// Creates a new object value. /// Creates a new object value.
#[inline] #[inline]
pub fn object(object: Object) -> Self { pub fn object(object: Object) -> Self {
Self(Gc::new(ValueData::Object(Box::new(GcCell::new(object))))) Self::Object(GcObject::new(object))
} }
/// Creates a new symbol value. /// Creates a new symbol value.
#[inline] #[inline]
pub fn symbol(symbol: Symbol) -> Self { pub(crate) fn symbol(symbol: Symbol) -> Self {
Self(Gc::new(ValueData::Symbol(symbol))) Self::Symbol(RcSymbol::from(symbol))
}
/// Gets the underlying `ValueData` structure.
#[inline]
pub fn data(&self) -> &ValueData {
&*self.0
} }
/// Helper function to convert the `Value` to a number and compute its power. /// Helper function to convert the `Value` to a number and compute its power.
pub fn as_num_to_power(&self, other: Self) -> Self { pub fn as_num_to_power(&self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => Self::bigint(a.clone().pow(b)), (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(a.as_inner().clone().pow(b)),
(a, b) => Self::rational(a.to_number().powf(b.to_number())), (a, b) => Self::rational(a.to_number().powf(b.to_number())),
} }
} }
@ -145,6 +170,7 @@ impl Value {
/// Returns a new empty object /// Returns a new empty object
pub fn new_object(global: Option<&Value>) -> Self { pub fn new_object(global: Option<&Value>) -> Self {
let _timer = BoaProfiler::global().start_event("new_object", "value"); let _timer = BoaProfiler::global().start_event("new_object", "value");
if let Some(global) = global { if let Some(global) = global {
let object_prototype = global.get_field("Object").get_field(PROTOTYPE); let object_prototype = global.get_field("Object").get_field(PROTOTYPE);
@ -225,10 +251,10 @@ impl Value {
/// Conversts the `Value` to `JSON`. /// Conversts the `Value` to `JSON`.
pub fn to_json(&self, interpreter: &mut Interpreter) -> Result<JSONValue, Value> { pub fn to_json(&self, interpreter: &mut Interpreter) -> Result<JSONValue, Value> {
match *self.data() { match *self {
ValueData::Null => Ok(JSONValue::Null), Self::Null => Ok(JSONValue::Null),
ValueData::Boolean(b) => Ok(JSONValue::Bool(b)), Self::Boolean(b) => Ok(JSONValue::Bool(b)),
ValueData::Object(ref obj) => { Self::Object(ref obj) => {
if obj.borrow().is_array() { if obj.borrow().is_array() {
let mut arr: Vec<JSONValue> = Vec::new(); let mut arr: Vec<JSONValue> = Vec::new();
for k in obj.borrow().properties().keys() { for k in obj.borrow().properties().keys() {
@ -248,59 +274,26 @@ impl Value {
let key = k.clone(); let key = k.clone();
let value = self.get_field(k.to_string()); let value = self.get_field(k.to_string());
if !value.is_undefined() && !value.is_function() { if !value.is_undefined() && !value.is_function() {
new_obj.insert(key, value.to_json(interpreter)?); new_obj.insert(key.to_string(), value.to_json(interpreter)?);
} }
} }
Ok(JSONValue::Object(new_obj)) Ok(JSONValue::Object(new_obj))
} }
} }
ValueData::String(ref str) => Ok(JSONValue::String(str.clone())), Self::String(ref str) => Ok(JSONValue::String(str.to_string())),
ValueData::Rational(num) => Ok(JSONNumber::from_f64(num) Self::Rational(num) => Ok(JSONNumber::from_f64(num)
.map(JSONValue::Number) .map(JSONValue::Number)
.unwrap_or(JSONValue::Null)), .unwrap_or(JSONValue::Null)),
ValueData::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))), Self::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))),
ValueData::BigInt(_) => { Self::BigInt(_) => {
Err(interpreter.construct_type_error("BigInt value can't be serialized in JSON")) Err(interpreter.construct_type_error("BigInt value can't be serialized in JSON"))
} }
ValueData::Symbol(_) | ValueData::Undefined => { Self::Symbol(_) | Self::Undefined => {
unreachable!("Symbols and Undefined JSON Values depend on parent type"); unreachable!("Symbols and Undefined JSON Values depend on parent type");
} }
} }
} }
}
impl Deref for Value {
type Target = ValueData;
fn deref(&self) -> &Self::Target {
self.data()
}
}
/// A Javascript value
#[derive(Trace, Finalize, Debug, Clone)]
pub enum ValueData {
/// `null` - A null value, for when a value doesn't exist.
Null,
/// `undefined` - An undefined value, for when a field or index doesn't exist.
Undefined,
/// `boolean` - A `true` / `false` value, for if a certain criteria is met.
Boolean(bool),
/// `String` - A UTF-8 string, such as `"Hello, world"`.
String(String),
/// `Number` - A 64-bit floating point number, such as `3.1415`
Rational(f64),
/// `Number` - A 32-bit integer, such as `42`.
Integer(i32),
/// `BigInt` - holds any arbitrary large signed integer.
BigInt(BigInt),
/// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values.
Object(Box<GcCell<Object>>),
/// `Symbol` - A Symbol Primitive type.
Symbol(Symbol),
}
impl ValueData {
/// This will tell us if we can exten an object or not, not properly implemented yet /// This will tell us if we can exten an object or not, not properly implemented yet
/// ///
/// For now always returns true. /// For now always returns true.
@ -430,7 +423,7 @@ impl ValueData {
Self::Rational(n) if n != 0.0 && !n.is_nan() => true, Self::Rational(n) if n != 0.0 && !n.is_nan() => true,
Self::Integer(n) if n != 0 => true, Self::Integer(n) if n != 0 => true,
Self::Boolean(v) => v, Self::Boolean(v) => v,
Self::BigInt(ref n) if *n != 0 => true, Self::BigInt(ref n) if *n.as_inner() != 0 => true,
_ => false, _ => false,
} }
} }
@ -488,7 +481,7 @@ impl ValueData {
Self::String(ref s) if !s.is_empty() => true, Self::String(ref s) if !s.is_empty() => true,
Self::Rational(n) if n != 0.0 && !n.is_nan() => true, Self::Rational(n) if n != 0.0 && !n.is_nan() => true,
Self::Integer(n) if n != 0 => true, Self::Integer(n) if n != 0 => true,
Self::BigInt(ref n) if *n != 0 => true, Self::BigInt(ref n) if *n.as_inner() != 0 => true,
Self::Boolean(v) => v, Self::Boolean(v) => v,
_ => false, _ => false,
} }
@ -568,12 +561,12 @@ impl ValueData {
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
/// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]] /// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]]
/// TODO: this function should use the get Value if its set /// TODO: this function should use the get Value if its set
pub fn get_field<F>(&self, field: F) -> Value pub fn get_field<F>(&self, field: F) -> Self
where where
F: Into<Value>, F: Into<Value>,
{ {
let _timer = BoaProfiler::global().start_event("Value::get_field", "value"); let _timer = BoaProfiler::global().start_event("Value::get_field", "value");
match *field.into() { match field.into() {
// Our field will either be a String or a Symbol // Our field will either be a String or a Symbol
Self::String(ref s) => { Self::String(ref s) => {
match self.get_property(s) { match self.get_property(s) {
@ -727,7 +720,7 @@ impl ValueData {
/// Set the property in the value. /// Set the property in the value.
pub fn set_property<S>(&self, field: S, property: Property) -> Property pub fn set_property<S>(&self, field: S, property: Property) -> Property
where where
S: Into<String>, S: Into<RcString>,
{ {
if let Some(mut object) = self.as_object_mut() { if let Some(mut object) = self.as_object_mut() {
object object
@ -758,7 +751,7 @@ impl ValueData {
} }
} }
impl Default for ValueData { impl Default for Value {
fn default() -> Self { fn default() -> Self {
Self::Undefined Self::Undefined
} }

82
boa/src/builtins/value/operations.rs

@ -3,14 +3,14 @@ use super::*;
impl Add for Value { impl Add for Value {
type Output = Self; type Output = Self;
fn add(self, other: Self) -> Self { fn add(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::String(ref s), ref o) => { (Self::String(ref s), ref o) => {
Self::string(format!("{}{}", s.clone(), &o.to_string())) Self::string(format!("{}{}", s.clone(), &o.to_string()))
} }
(ValueData::BigInt(ref n1), ValueData::BigInt(ref n2)) => { (Self::BigInt(ref n1), Self::BigInt(ref n2)) => {
Self::bigint(n1.clone() + n2.clone()) Self::bigint(n1.as_inner().clone() + n2.as_inner().clone())
} }
(ref s, ValueData::String(ref o)) => Self::string(format!("{}{}", s.to_string(), o)), (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()), (ref s, ref o) => Self::rational(s.to_number() + o.to_number()),
} }
} }
@ -18,9 +18,9 @@ impl Add for Value {
impl Sub for Value { impl Sub for Value {
type Output = Self; type Output = Self;
fn sub(self, other: Self) -> Self { fn sub(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() - b.clone()) Self::bigint(a.as_inner().clone() - b.as_inner().clone())
} }
(a, b) => Self::rational(a.to_number() - b.to_number()), (a, b) => Self::rational(a.to_number() - b.to_number()),
} }
@ -29,9 +29,9 @@ impl Sub for Value {
impl Mul for Value { impl Mul for Value {
type Output = Self; type Output = Self;
fn mul(self, other: Self) -> Self { fn mul(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() * b.clone()) Self::bigint(a.as_inner().clone() * b.as_inner().clone())
} }
(a, b) => Self::rational(a.to_number() * b.to_number()), (a, b) => Self::rational(a.to_number() * b.to_number()),
} }
@ -40,9 +40,9 @@ impl Mul for Value {
impl Div for Value { impl Div for Value {
type Output = Self; type Output = Self;
fn div(self, other: Self) -> Self { fn div(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() / b.clone()) Self::bigint(a.as_inner().clone() / b.as_inner().clone())
} }
(a, b) => Self::rational(a.to_number() / b.to_number()), (a, b) => Self::rational(a.to_number() / b.to_number()),
} }
@ -51,9 +51,9 @@ impl Div for Value {
impl Rem for Value { impl Rem for Value {
type Output = Self; type Output = Self;
fn rem(self, other: Self) -> Self { fn rem(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() % b.clone()) Self::bigint(a.as_inner().clone() % b.as_inner().clone())
} }
(a, b) => Self::rational(a.to_number() % b.to_number()), (a, b) => Self::rational(a.to_number() % b.to_number()),
} }
@ -62,9 +62,9 @@ impl Rem for Value {
impl BitAnd for Value { impl BitAnd for Value {
type Output = Self; type Output = Self;
fn bitand(self, other: Self) -> Self { fn bitand(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() & b.clone()) Self::bigint(a.as_inner().clone() & b.as_inner().clone())
} }
(a, b) => Self::integer(a.to_integer() & b.to_integer()), (a, b) => Self::integer(a.to_integer() & b.to_integer()),
} }
@ -73,9 +73,9 @@ impl BitAnd for Value {
impl BitOr for Value { impl BitOr for Value {
type Output = Self; type Output = Self;
fn bitor(self, other: Self) -> Self { fn bitor(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() | b.clone()) Self::bigint(a.as_inner().clone() | b.as_inner().clone())
} }
(a, b) => Self::integer(a.to_integer() | b.to_integer()), (a, b) => Self::integer(a.to_integer() | b.to_integer()),
} }
@ -84,9 +84,9 @@ impl BitOr for Value {
impl BitXor for Value { impl BitXor for Value {
type Output = Self; type Output = Self;
fn bitxor(self, other: Self) -> Self { fn bitxor(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() ^ b.clone()) Self::bigint(a.as_inner().clone() ^ b.as_inner().clone())
} }
(a, b) => Self::integer(a.to_integer() ^ b.to_integer()), (a, b) => Self::integer(a.to_integer() ^ b.to_integer()),
} }
@ -96,9 +96,9 @@ impl BitXor for Value {
impl Shl for Value { impl Shl for Value {
type Output = Self; type Output = Self;
fn shl(self, other: Self) -> Self { fn shl(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() << b.clone()) Self::bigint(a.as_inner().clone() << b.as_inner().clone())
} }
(a, b) => Self::integer(a.to_integer() << b.to_integer()), (a, b) => Self::integer(a.to_integer() << b.to_integer()),
} }
@ -107,9 +107,9 @@ impl Shl for Value {
impl Shr for Value { impl Shr for Value {
type Output = Self; type Output = Self;
fn shr(self, other: Self) -> Self { fn shr(self, other: Self) -> Self {
match (self.data(), other.data()) { match (self, other) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { (Self::BigInt(ref a), Self::BigInt(ref b)) => {
Self::bigint(a.clone() >> b.clone()) Self::bigint(a.as_inner().clone() >> b.as_inner().clone())
} }
(a, b) => Self::integer(a.to_integer() >> b.to_integer()), (a, b) => Self::integer(a.to_integer() >> b.to_integer()),
} }
@ -126,19 +126,17 @@ impl Neg for Value {
type Output = Self; type Output = Self;
fn neg(self) -> Self::Output { fn neg(self) -> Self::Output {
match self.data() { match self {
ValueData::Object(_) | ValueData::Symbol(_) | ValueData::Undefined => { Self::Object(_) | Self::Symbol(_) | Self::Undefined => Self::rational(NAN),
Self::rational(NAN) Self::String(ref str) => Self::rational(match f64::from_str(str) {
}
ValueData::String(ref str) => Self::rational(match f64::from_str(str) {
Ok(num) => -num, Ok(num) => -num,
Err(_) => NAN, Err(_) => NAN,
}), }),
ValueData::Rational(num) => Self::rational(-num), Self::Rational(num) => Self::rational(-num),
ValueData::Integer(num) => Self::rational(-f64::from(*num)), Self::Integer(num) => Self::rational(-f64::from(num)),
ValueData::Boolean(true) => Self::integer(1), Self::Boolean(true) => Self::integer(1),
ValueData::Boolean(false) | ValueData::Null => Self::integer(0), Self::Boolean(false) | Self::Null => Self::integer(0),
ValueData::BigInt(ref num) => Self::bigint(-num.clone()), Self::BigInt(ref num) => Self::bigint(-num.as_inner().clone()),
} }
} }
} }

42
boa/src/builtins/value/rcbigint.rs

@ -0,0 +1,42 @@
use crate::builtins::BigInt;
use std::fmt::{self, Display};
use std::ops::Deref;
use std::rc::Rc;
use gc::{unsafe_empty_trace, Finalize, Trace};
#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RcBigInt(Rc<BigInt>);
unsafe impl Trace for RcBigInt {
unsafe_empty_trace!();
}
impl RcBigInt {
pub(crate) fn as_inner(&self) -> &BigInt {
&self.0
}
}
impl Display for RcBigInt {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl Deref for RcBigInt {
type Target = BigInt;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<BigInt> for RcBigInt {
#[inline]
fn from(bigint: BigInt) -> Self {
Self(Rc::from(bigint))
}
}

87
boa/src/builtins/value/rcstring.rs

@ -0,0 +1,87 @@
use std::borrow::Borrow;
use std::fmt::{self, Display};
use std::ops::Deref;
use std::rc::Rc;
use gc::{unsafe_empty_trace, Finalize, Trace};
#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RcString(Rc<str>);
unsafe impl Trace for RcString {
unsafe_empty_trace!();
}
impl RcString {
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl Default for RcString {
#[inline]
fn default() -> Self {
Self(Rc::from(String::new()))
}
}
impl Display for RcString {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
impl PartialEq<str> for RcString {
fn eq(&self, other: &str) -> bool {
self.as_str() == other
}
}
impl PartialEq<RcString> for str {
fn eq(&self, other: &RcString) -> bool {
self == other.as_str()
}
}
impl PartialEq<&str> for RcString {
fn eq(&self, other: &&str) -> bool {
self.as_str() == *other
}
}
impl PartialEq<RcString> for &str {
fn eq(&self, other: &RcString) -> bool {
*self == other.as_str()
}
}
impl Deref for RcString {
type Target = str;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Borrow<str> for RcString {
#[inline]
fn borrow(&self) -> &str {
self.0.borrow()
}
}
impl From<String> for RcString {
#[inline]
fn from(string: String) -> Self {
Self(Rc::from(string))
}
}
impl From<&str> for RcString {
#[inline]
fn from(string: &str) -> Self {
Self(Rc::from(string))
}
}

39
boa/src/builtins/value/rcsymbol.rs

@ -0,0 +1,39 @@
use crate::builtins::Symbol;
use std::fmt::{self, Display};
use std::ops::Deref;
use std::rc::Rc;
use gc::{unsafe_empty_trace, Finalize, Trace};
#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct RcSymbol(Rc<Symbol>);
unsafe impl Trace for RcSymbol {
unsafe_empty_trace!();
}
impl Display for RcSymbol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0.description() {
Some(desc) => write!(f, "Symbol({})", desc),
None => write!(f, "Symbol()"),
}
}
}
impl Deref for RcSymbol {
type Target = Symbol;
#[inline]
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl From<Symbol> for RcSymbol {
#[inline]
fn from(symbol: Symbol) -> Self {
Self(Rc::from(symbol))
}
}

2
boa/src/builtins/value/tests.rs

@ -20,7 +20,7 @@ fn check_string_to_value() {
#[test] #[test]
fn check_undefined() { fn check_undefined() {
let u = ValueData::Undefined; let u = Value::Undefined;
assert_eq!(u.get_type(), Type::Undefined); assert_eq!(u.get_type(), Type::Undefined);
assert_eq!(u.to_string(), "undefined"); assert_eq!(u.to_string(), "undefined");
} }

4
boa/src/builtins/value/val_type.rs

@ -1,4 +1,4 @@
use crate::builtins::value::ValueData; use crate::builtins::value::Value;
use std::ops::Deref; use std::ops::Deref;
/// Possible types of val as defined at https://tc39.es/ecma262/#sec-typeof-operator. /// Possible types of val as defined at https://tc39.es/ecma262/#sec-typeof-operator.
@ -32,7 +32,7 @@ impl Type {
} }
} }
impl ValueData { impl Value {
/// Get the type of the value. /// Get the type of the value.
/// ///
/// This is similar to typeof as described at https://tc39.es/ecma262/#sec-typeof-operator but instead of /// This is similar to typeof as described at https://tc39.es/ecma262/#sec-typeof-operator but instead of

4
boa/src/environment/global_environment_record.rs

@ -21,9 +21,9 @@ use rustc_hash::FxHashSet;
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct GlobalEnvironmentRecord { pub struct GlobalEnvironmentRecord {
pub object_record: Box<ObjectEnvironmentRecord>, pub object_record: ObjectEnvironmentRecord,
pub global_this_binding: Value, pub global_this_binding: Value,
pub declarative_record: Box<DeclarativeEnvironmentRecord>, pub declarative_record: DeclarativeEnvironmentRecord,
pub var_names: FxHashSet<String>, pub var_names: FxHashSet<String>,
} }

10
boa/src/environment/lexical_environment.rs

@ -260,7 +260,7 @@ pub fn new_object_environment(object: Value, environment: Option<Environment>) -
} }
pub fn new_global_environment(global: Value, this_value: Value) -> Environment { pub fn new_global_environment(global: Value, this_value: Value) -> Environment {
let obj_rec = Box::new(ObjectEnvironmentRecord { let obj_rec = ObjectEnvironmentRecord {
bindings: global, bindings: global,
outer_env: None, outer_env: None,
/// Object Environment Records created for with statements (13.11) /// Object Environment Records created for with statements (13.11)
@ -269,12 +269,12 @@ pub fn new_global_environment(global: Value, this_value: Value) -> Environment {
/// with each object Environment Record. By default, the value of withEnvironment is false /// with each object Environment Record. By default, the value of withEnvironment is false
/// for any object Environment Record. /// for any object Environment Record.
with_environment: false, with_environment: false,
}); };
let dcl_rec = Box::new(DeclarativeEnvironmentRecord { let dcl_rec = DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(), env_rec: FxHashMap::default(),
outer_env: None, outer_env: None,
}); };
Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord { Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord {
object_record: obj_rec, object_record: obj_rec,
@ -311,7 +311,7 @@ mod tests {
{ {
const bar = "bar"; const bar = "bar";
} }
try{ try{
bar; bar;
} catch (err) { } catch (err) {

16
boa/src/exec/expression/mod.rs

@ -4,7 +4,7 @@ use super::{Executable, Interpreter, InterpreterState};
use crate::{ use crate::{
builtins::{ builtins::{
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
value::{ResultValue, Type, Value, ValueData}, value::{ResultValue, Type, Value},
}, },
syntax::ast::node::{Call, New, Node}, syntax::ast::node::{Call, New, Node},
BoaProfiler, BoaProfiler,
@ -13,7 +13,7 @@ use crate::{
impl Executable for Call { impl Executable for Call {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue { fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
let _timer = BoaProfiler::global().start_event("Call", "exec"); let _timer = BoaProfiler::global().start_event("Call", "exec");
let (mut this, func) = match self.expr() { let (this, func) = match self.expr() {
Node::GetConstField(ref get_const_field) => { Node::GetConstField(ref get_const_field) => {
let mut obj = get_const_field.obj().run(interpreter)?; let mut obj = get_const_field.obj().run(interpreter)?;
if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol { if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol {
@ -45,7 +45,7 @@ impl Executable for Call {
} }
// execute the function call itself // execute the function call itself
let fnct_result = interpreter.call(&func, &mut this, &v_args); let fnct_result = interpreter.call(&func, &this, &v_args);
// unset the early return flag // unset the early return flag
interpreter.set_current_state(InterpreterState::Executing); interpreter.set_current_state(InterpreterState::Executing);
@ -66,15 +66,15 @@ impl Executable for New {
for arg in self.args() { for arg in self.args() {
v_args.push(arg.run(interpreter)?); v_args.push(arg.run(interpreter)?);
} }
let mut this = Value::new_object(None); let this = Value::new_object(None);
// Create a blank object, then set its __proto__ property to the [Constructor].prototype // Create a blank object, then set its __proto__ property to the [Constructor].prototype
this.set_internal_slot(INSTANCE_PROTOTYPE, func_object.get_field(PROTOTYPE)); this.set_internal_slot(INSTANCE_PROTOTYPE, func_object.get_field(PROTOTYPE));
match func_object.data() { match func_object {
ValueData::Object(ref obj) => { Value::Object(ref obj) => {
let obj = (**obj).borrow(); let obj = obj.borrow();
if let ObjectData::Function(ref func) = obj.data { if let ObjectData::Function(ref func) = obj.data {
return func.construct(func_object.clone(), &mut this, &v_args, interpreter); return func.construct(func_object.clone(), &this, &v_args, interpreter);
} }
interpreter.throw_type_error("not a constructor") interpreter.throw_type_error("not a constructor")
} }

238
boa/src/exec/mod.rs

@ -26,7 +26,7 @@ use crate::{
function::{Function as FunctionObject, FunctionBody, ThisMode}, function::{Function as FunctionObject, FunctionBody, ThisMode},
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property, property::Property,
value::{ResultValue, Type, Value, ValueData}, value::{RcBigInt, RcString, ResultValue, Type, Value},
BigInt, Number, BigInt, Number,
}, },
realm::Realm, realm::Realm,
@ -37,7 +37,7 @@ use crate::{
BoaProfiler, BoaProfiler,
}; };
use std::convert::TryFrom; use std::convert::TryFrom;
use std::{borrow::Borrow, ops::Deref}; use std::ops::Deref;
pub trait Executable { pub trait Executable {
/// Runs this executable in the given executor. /// Runs this executable in the given executor.
@ -156,12 +156,12 @@ impl Interpreter {
pub(crate) fn call( pub(crate) fn call(
&mut self, &mut self,
f: &Value, f: &Value,
this: &mut Value, this: &Value,
arguments_list: &[Value], arguments_list: &[Value],
) -> ResultValue { ) -> ResultValue {
match *f.data() { match *f {
ValueData::Object(ref obj) => { Value::Object(ref obj) => {
let obj = (**obj).borrow(); let obj = obj.borrow();
if let ObjectData::Function(ref func) = obj.data { if let ObjectData::Function(ref func) = obj.data {
return func.call(f.clone(), this, arguments_list, self); return func.call(f.clone(), this, arguments_list, self);
} }
@ -173,20 +173,18 @@ impl Interpreter {
/// Converts a value into a rust heap allocated string. /// Converts a value into a rust heap allocated string.
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn to_string(&mut self, value: &Value) -> Result<String, Value> { pub fn to_string(&mut self, value: &Value) -> Result<RcString, Value> {
match value.data() { match value {
ValueData::Null => Ok("null".to_owned()), Value::Null => Ok(RcString::from("null")),
ValueData::Undefined => Ok("undefined".to_owned()), Value::Undefined => Ok(RcString::from("undefined".to_owned())),
ValueData::Boolean(boolean) => Ok(boolean.to_string()), Value::Boolean(boolean) => Ok(RcString::from(boolean.to_string())),
ValueData::Rational(rational) => Ok(Number::to_native_string(*rational)), Value::Rational(rational) => Ok(RcString::from(Number::to_native_string(*rational))),
ValueData::Integer(integer) => Ok(integer.to_string()), Value::Integer(integer) => Ok(RcString::from(integer.to_string())),
ValueData::String(string) => Ok(string.clone()), Value::String(string) => Ok(string.clone()),
ValueData::Symbol(_) => { Value::Symbol(_) => Err(self.construct_type_error("can't convert symbol to string")),
Err(self.construct_type_error("can't convert symbol to string")) Value::BigInt(ref bigint) => Ok(RcString::from(bigint.to_string())),
} Value::Object(_) => {
ValueData::BigInt(ref bigint) => Ok(bigint.to_string()), let primitive = self.to_primitive(value, PreferredType::String)?;
ValueData::Object(_) => {
let primitive = self.to_primitive(&mut value.clone(), PreferredType::String);
self.to_string(&primitive) self.to_string(&primitive)
} }
} }
@ -194,33 +192,31 @@ impl Interpreter {
/// Helper function. /// Helper function.
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn to_bigint(&mut self, value: &Value) -> Result<BigInt, Value> { pub fn to_bigint(&mut self, value: &Value) -> Result<RcBigInt, Value> {
match value.data() { match value {
ValueData::Null => Err(self.construct_type_error("cannot convert null to a BigInt")), Value::Null => Err(self.construct_type_error("cannot convert null to a BigInt")),
ValueData::Undefined => { Value::Undefined => {
Err(self.construct_type_error("cannot convert undefined to a BigInt")) Err(self.construct_type_error("cannot convert undefined to a BigInt"))
} }
ValueData::String(ref string) => Ok(BigInt::from_string(string, self)?), Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, self)?)),
ValueData::Boolean(true) => Ok(BigInt::from(1)), Value::Boolean(true) => Ok(RcBigInt::from(BigInt::from(1))),
ValueData::Boolean(false) => Ok(BigInt::from(0)), Value::Boolean(false) => Ok(RcBigInt::from(BigInt::from(0))),
ValueData::Integer(num) => Ok(BigInt::from(*num)), Value::Integer(num) => Ok(RcBigInt::from(BigInt::from(*num))),
ValueData::Rational(num) => { Value::Rational(num) => {
if let Ok(bigint) = BigInt::try_from(*num) { if let Ok(bigint) = BigInt::try_from(*num) {
return Ok(bigint); return Ok(RcBigInt::from(bigint));
} }
Err(self.construct_type_error(format!( Err(self.construct_type_error(format!(
"The number {} cannot be converted to a BigInt because it is not an integer", "The number {} cannot be converted to a BigInt because it is not an integer",
num num
))) )))
} }
ValueData::BigInt(b) => Ok(b.clone()), Value::BigInt(b) => Ok(b.clone()),
ValueData::Object(_) => { Value::Object(_) => {
let primitive = self.to_primitive(&mut value.clone(), PreferredType::Number); let primitive = self.to_primitive(value, PreferredType::Number)?;
self.to_bigint(&primitive) self.to_bigint(&primitive)
} }
ValueData::Symbol(_) => { Value::Symbol(_) => Err(self.construct_type_error("cannot convert Symbol to a BigInt")),
Err(self.construct_type_error("cannot convert Symbol to a BigInt"))
}
} }
} }
@ -265,19 +261,19 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumber /// See: https://tc39.es/ecma262/#sec-tonumber
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn to_number(&mut self, value: &Value) -> Result<f64, Value> { pub fn to_number(&mut self, value: &Value) -> Result<f64, Value> {
match *value.data() { match *value {
ValueData::Null => Ok(0.0), Value::Null => Ok(0.0),
ValueData::Undefined => Ok(f64::NAN), Value::Undefined => Ok(f64::NAN),
ValueData::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }), Value::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }),
// TODO: this is probably not 100% correct, see https://tc39.es/ecma262/#sec-tonumber-applied-to-the-string-type // TODO: this is probably not 100% correct, see https://tc39.es/ecma262/#sec-tonumber-applied-to-the-string-type
ValueData::String(ref string) => Ok(string.parse().unwrap_or(f64::NAN)), Value::String(ref string) => Ok(string.parse().unwrap_or(f64::NAN)),
ValueData::Rational(number) => Ok(number), Value::Rational(number) => Ok(number),
ValueData::Integer(integer) => Ok(f64::from(integer)), Value::Integer(integer) => Ok(f64::from(integer)),
ValueData::Symbol(_) => Err(self.construct_type_error("argument must not be a symbol")), Value::Symbol(_) => Err(self.construct_type_error("argument must not be a symbol")),
ValueData::BigInt(_) => Err(self.construct_type_error("argument must not be a bigint")), Value::BigInt(_) => Err(self.construct_type_error("argument must not be a bigint")),
ValueData::Object(_) => { Value::Object(_) => {
let prim_value = self.to_primitive(&mut (value.clone()), PreferredType::Number); let primitive = self.to_primitive(value, PreferredType::Number)?;
self.to_number(&prim_value) self.to_number(&primitive)
} }
} }
} }
@ -287,7 +283,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric /// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub fn to_numeric(&mut self, value: &Value) -> ResultValue { pub fn to_numeric(&mut self, value: &Value) -> ResultValue {
let primitive = self.to_primitive(&mut value.clone(), PreferredType::Number); let primitive = self.to_primitive(value, PreferredType::Number)?;
if primitive.is_bigint() { if primitive.is_bigint() {
return Ok(primitive); return Ok(primitive);
} }
@ -301,7 +297,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric /// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_numeric_number(&mut self, value: &Value) -> Result<f64, Value> { pub(crate) fn to_numeric_number(&mut self, value: &Value) -> Result<f64, Value> {
let primitive = self.to_primitive(&mut value.clone(), PreferredType::Number); let primitive = self.to_primitive(value, PreferredType::Number)?;
if let Some(ref bigint) = primitive.as_bigint() { if let Some(ref bigint) = primitive.as_bigint() {
return Ok(bigint.to_f64()); return Ok(bigint.to_f64());
} }
@ -312,11 +308,11 @@ impl Interpreter {
/// ///
/// This is useful for the spread operator, for any other object an `Err` is returned /// This is useful for the spread operator, for any other object an `Err` is returned
pub(crate) fn extract_array_properties(&mut self, value: &Value) -> Result<Vec<Value>, ()> { pub(crate) fn extract_array_properties(&mut self, value: &Value) -> Result<Vec<Value>, ()> {
if let ValueData::Object(ref x) = *value.deref().borrow() { if let Value::Object(ref x) = value {
// Check if object is array // Check if object is array
if let ObjectData::Array = x.deref().borrow().data { if let ObjectData::Array = x.deref().borrow().data {
let length: i32 = self.value_to_rust_number(&value.get_field("length")) as i32; let length = i32::from(&value.get_field("length"));
let values: Vec<Value> = (0..length) let values = (0..length)
.map(|idx| value.get_field(idx.to_string())) .map(|idx| value.get_field(idx.to_string()))
.collect(); .collect();
return Ok(values); return Ok(values);
@ -328,58 +324,73 @@ impl Interpreter {
Err(()) Err(())
} }
/// <https://tc39.es/ecma262/#sec-ordinarytoprimitive> /// Converts an object to a primitive.
pub(crate) fn ordinary_to_primitive(&mut self, o: &mut Value, hint: PreferredType) -> Value { ///
/// More information:
/// - [ECMAScript][spec]
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive
pub(crate) fn ordinary_to_primitive(&mut self, o: &Value, hint: PreferredType) -> ResultValue {
// 1. Assert: Type(O) is Object.
debug_assert!(o.get_type() == Type::Object); debug_assert!(o.get_type() == Type::Object);
// 2. Assert: Type(hint) is String and its value is either "string" or "number".
debug_assert!(hint == PreferredType::String || hint == PreferredType::Number); debug_assert!(hint == PreferredType::String || hint == PreferredType::Number);
let method_names: Vec<&str> = if hint == PreferredType::String {
vec!["toString", "valueOf"] // 3. If hint is "string", then
// a. Let methodNames be « "toString", "valueOf" ».
// 4. Else,
// a. Let methodNames be « "valueOf", "toString" ».
let method_names = if hint == PreferredType::String {
["toString", "valueOf"]
} else { } else {
vec!["valueOf", "toString"] ["valueOf", "toString"]
}; };
for name in method_names.iter() {
// 5. For each name in methodNames in List order, do
for name in &method_names {
// a. Let method be ? Get(O, name).
let method: Value = o.get_field(*name); let method: Value = o.get_field(*name);
// b. If IsCallable(method) is true, then
if method.is_function() { if method.is_function() {
let result = self.call(&method, o, &[]); // i. Let result be ? Call(method, O).
match result { let result = self.call(&method, &o, &[])?;
Ok(val) => { // ii. If Type(result) is not Object, return result.
if val.is_object() { if !result.is_object() {
// TODO: throw exception return Ok(result);
continue;
} else {
return val;
}
}
Err(_) => continue,
} }
} }
} }
Value::undefined() // 6. Throw a TypeError exception.
self.throw_type_error("cannot convert object to primitive value")
} }
/// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. /// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
///
/// <https://tc39.es/ecma262/#sec-toprimitive> /// <https://tc39.es/ecma262/#sec-toprimitive>
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_primitive( pub(crate) fn to_primitive(
&mut self, &mut self,
input: &mut Value, input: &Value,
preferred_type: PreferredType, preferred_type: PreferredType,
) -> Value { ) -> ResultValue {
let mut hint: PreferredType; // 1. Assert: input is an ECMAScript language value. (always a value not need to check)
match (*input).deref() { // 2. If Type(input) is Object, then
ValueData::Object(_) => { if let Value::Object(_) = input {
hint = preferred_type; let mut hint = preferred_type;
// Skip d, e we don't support Symbols yet // Skip d, e we don't support Symbols yet
// TODO: add when symbols are supported // TODO: add when symbols are supported
if hint == PreferredType::Default { // TODO: Add other steps.
hint = PreferredType::Number; if hint == PreferredType::Default {
}; hint = PreferredType::Number;
};
self.ordinary_to_primitive(input, hint)
} // g. Return ? OrdinaryToPrimitive(input, hint).
_ => input.clone(), self.ordinary_to_primitive(input, hint)
} else {
// 3. Return input.
Ok(input.clone())
} }
} }
@ -387,8 +398,8 @@ impl Interpreter {
/// ///
/// https://tc39.es/ecma262/#sec-topropertykey /// https://tc39.es/ecma262/#sec-topropertykey
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_property_key(&mut self, value: &mut Value) -> ResultValue { pub(crate) fn to_property_key(&mut self, value: &Value) -> ResultValue {
let key = self.to_primitive(value, PreferredType::String); let key = self.to_primitive(value, PreferredType::String)?;
if key.is_symbol() { if key.is_symbol() {
Ok(key) Ok(key)
} else { } else {
@ -397,7 +408,7 @@ impl Interpreter {
} }
/// https://tc39.es/ecma262/#sec-hasproperty /// https://tc39.es/ecma262/#sec-hasproperty
pub(crate) fn has_property(&self, obj: &mut Value, key: &Value) -> bool { pub(crate) fn has_property(&self, obj: &Value, key: &Value) -> bool {
if let Some(obj) = obj.as_object() { if let Some(obj) = obj.as_object() {
if !Property::is_property_key(key) { if !Property::is_property_key(key) {
false false
@ -413,9 +424,9 @@ impl Interpreter {
/// https://tc39.es/ecma262/#sec-toobject /// https://tc39.es/ecma262/#sec-toobject
#[allow(clippy::wrong_self_convention)] #[allow(clippy::wrong_self_convention)]
pub(crate) fn to_object(&mut self, value: &Value) -> ResultValue { pub(crate) fn to_object(&mut self, value: &Value) -> ResultValue {
match value.data() { match value {
ValueData::Undefined | ValueData::Null => Err(Value::undefined()), Value::Undefined | Value::Null => Err(Value::undefined()),
ValueData::Boolean(boolean) => { Value::Boolean(boolean) => {
let proto = self let proto = self
.realm .realm
.environment .environment
@ -428,7 +439,7 @@ impl Interpreter {
ObjectData::Boolean(*boolean), ObjectData::Boolean(*boolean),
)) ))
} }
ValueData::Integer(integer) => { Value::Integer(integer) => {
let proto = self let proto = self
.realm .realm
.environment .environment
@ -440,7 +451,7 @@ impl Interpreter {
ObjectData::Number(f64::from(*integer)), ObjectData::Number(f64::from(*integer)),
)) ))
} }
ValueData::Rational(rational) => { Value::Rational(rational) => {
let proto = self let proto = self
.realm .realm
.environment .environment
@ -453,7 +464,7 @@ impl Interpreter {
ObjectData::Number(*rational), ObjectData::Number(*rational),
)) ))
} }
ValueData::String(ref string) => { Value::String(ref string) => {
let proto = self let proto = self
.realm .realm
.environment .environment
@ -466,7 +477,7 @@ impl Interpreter {
ObjectData::String(string.clone()), ObjectData::String(string.clone()),
)) ))
} }
ValueData::Symbol(ref symbol) => { Value::Symbol(ref symbol) => {
let proto = self let proto = self
.realm .realm
.environment .environment
@ -479,7 +490,7 @@ impl Interpreter {
ObjectData::Symbol(symbol.clone()), ObjectData::Symbol(symbol.clone()),
)) ))
} }
ValueData::BigInt(ref bigint) => { Value::BigInt(ref bigint) => {
let proto = self let proto = self
.realm .realm
.environment .environment
@ -490,36 +501,7 @@ impl Interpreter {
Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone())); Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone()));
Ok(bigint_obj) Ok(bigint_obj)
} }
ValueData::Object(_) => Ok(value.clone()), Value::Object(_) => Ok(value.clone()),
}
}
pub(crate) fn value_to_rust_number(&mut self, value: &Value) -> f64 {
match *value.deref().borrow() {
ValueData::Null => f64::from(0),
ValueData::Boolean(boolean) => {
if boolean {
f64::from(1)
} else {
f64::from(0)
}
}
ValueData::Rational(num) => num,
ValueData::Integer(num) => f64::from(num),
ValueData::String(ref string) => string.parse::<f64>().unwrap(),
ValueData::BigInt(ref bigint) => bigint.to_f64(),
ValueData::Object(_) => {
let prim_value = self.to_primitive(&mut (value.clone()), PreferredType::Number);
self.to_string(&prim_value)
.expect("cannot convert value to string")
.parse::<f64>()
.expect("cannot parse value to f64")
}
ValueData::Undefined => f64::NAN,
_ => {
// TODO: Make undefined?
f64::from(0)
}
} }
} }

13
boa/src/exec/operator/mod.rs

@ -12,7 +12,6 @@ use crate::{
}, },
BoaProfiler, BoaProfiler,
}; };
use std::borrow::BorrowMut;
impl Executable for Assign { impl Executable for Assign {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue { fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
@ -78,11 +77,11 @@ impl Executable for BinOp {
}) })
} }
op::BinOp::Comp(op) => { op::BinOp::Comp(op) => {
let mut v_a = self.lhs().run(interpreter)?; let v_a = self.lhs().run(interpreter)?;
let mut v_b = self.rhs().run(interpreter)?; let v_b = self.rhs().run(interpreter)?;
Ok(Value::from(match op { Ok(Value::from(match op {
CompOp::Equal => v_a.equals(v_b.borrow_mut(), interpreter), CompOp::Equal => v_a.equals(&v_b, interpreter)?,
CompOp::NotEqual => !v_a.equals(v_b.borrow_mut(), interpreter), CompOp::NotEqual => !v_a.equals(&v_b, interpreter)?,
CompOp::StrictEqual => v_a.strict_equals(&v_b), CompOp::StrictEqual => v_a.strict_equals(&v_b),
CompOp::StrictNotEqual => !v_a.strict_equals(&v_b), CompOp::StrictNotEqual => !v_a.strict_equals(&v_b),
CompOp::GreaterThan => v_a.to_number() > v_b.to_number(), CompOp::GreaterThan => v_a.to_number() > v_b.to_number(),
@ -96,8 +95,8 @@ impl Executable for BinOp {
v_b.get_type().as_str() v_b.get_type().as_str()
)); ));
} }
let key = interpreter.to_property_key(&mut v_a)?; let key = interpreter.to_property_key(&v_a)?;
interpreter.has_property(&mut v_b, &key) interpreter.has_property(&v_b, &key)
} }
})) }))
} }

23
boa/src/realm.rs

@ -8,7 +8,7 @@ use crate::{
builtins::{ builtins::{
self, self,
function::{Function, NativeFunctionData}, function::{Function, NativeFunctionData},
value::{Value, ValueData}, value::Value,
}, },
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
@ -27,7 +27,7 @@ use rustc_hash::{FxHashMap, FxHashSet};
#[derive(Debug)] #[derive(Debug)]
pub struct Realm { pub struct Realm {
pub global_obj: Value, pub global_obj: Value,
pub global_env: Gc<GcCell<Box<GlobalEnvironmentRecord>>>, pub global_env: Gc<GcCell<GlobalEnvironmentRecord>>,
pub environment: LexicalEnvironment, pub environment: LexicalEnvironment,
} }
@ -65,18 +65,15 @@ impl Realm {
pub fn register_global_func(self, func_name: &str, func: NativeFunctionData) -> Self { pub fn register_global_func(self, func_name: &str, func: NativeFunctionData) -> Self {
let func = Function::builtin(Vec::new(), func); let func = Function::builtin(Vec::new(), func);
self.global_obj self.global_obj
.set_field(Value::from(func_name), ValueData::from_func(func)); .set_field(Value::from(func_name), Value::from_func(func));
self self
} }
} }
// Similar to new_global_environment in lexical_environment, except we need to return a GlobalEnvirionment // Similar to new_global_environment in lexical_environment, except we need to return a GlobalEnvirionment
fn new_global_environment( fn new_global_environment(global: Value, this_value: Value) -> Gc<GcCell<GlobalEnvironmentRecord>> {
global: Value, let obj_rec = ObjectEnvironmentRecord {
this_value: Value,
) -> Gc<GcCell<Box<GlobalEnvironmentRecord>>> {
let obj_rec = Box::new(ObjectEnvironmentRecord {
bindings: global, bindings: global,
outer_env: None, outer_env: None,
/// Object Environment Records created for with statements (13.11) /// Object Environment Records created for with statements (13.11)
@ -85,17 +82,17 @@ fn new_global_environment(
/// with each object Environment Record. By default, the value of withEnvironment is false /// with each object Environment Record. By default, the value of withEnvironment is false
/// for any object Environment Record. /// for any object Environment Record.
with_environment: false, with_environment: false,
}); };
let dcl_rec = Box::new(DeclarativeEnvironmentRecord { let dcl_rec = DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(), env_rec: FxHashMap::default(),
outer_env: None, outer_env: None,
}); };
Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord { Gc::new(GcCell::new(GlobalEnvironmentRecord {
object_record: obj_rec, object_record: obj_rec,
global_this_binding: this_value, global_this_binding: this_value,
declarative_record: dcl_rec, declarative_record: dcl_rec,
var_names: FxHashSet::default(), var_names: FxHashSet::default(),
}))) }))
} }

Loading…
Cancel
Save