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

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

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

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

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

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

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

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

@ -20,7 +20,7 @@ use crate::{
builtins::{
function::make_builtin_fn,
object::InternalState,
value::{display_obj, ResultValue, Value},
value::{display_obj, RcString, ResultValue, Value},
},
exec::Interpreter,
BoaProfiler,
@ -31,8 +31,8 @@ use std::time::SystemTime;
/// This is the internal console object state.
#[derive(Debug, Default)]
pub struct ConsoleState {
count_map: FxHashMap<String, u32>,
timer_map: FxHashMap<String, u128>,
count_map: FxHashMap<RcString, u32>,
timer_map: FxHashMap<RcString, u128>,
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())?;
match data.len() {
0 => Ok(String::new()),
1 => Ok(target),
1 => Ok(target.to_string()),
_ => {
let mut formatted = String::new();
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
/// [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();
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
/// [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| {
state.groups.clear();
});
@ -193,7 +193,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
///
/// [spec]: https://console.spec.whatwg.org/#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| {
logger(LogMessage::Log(formatter(args, ctx)?), state);
Ok(())
@ -211,7 +211,7 @@ pub fn debug(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
///
/// [spec]: https://console.spec.whatwg.org/#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| {
logger(LogMessage::Error(formatter(args, ctx)?), state);
Ok(())
@ -229,7 +229,7 @@ pub fn error(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
///
/// [spec]: https://console.spec.whatwg.org/#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| {
logger(LogMessage::Info(formatter(args, ctx)?), state);
Ok(())
@ -247,7 +247,7 @@ pub fn info(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
///
/// [spec]: https://console.spec.whatwg.org/#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| {
logger(LogMessage::Log(formatter(args, ctx)?), state);
Ok(())
@ -265,7 +265,7 @@ pub fn log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal
///
/// [spec]: https://console.spec.whatwg.org/#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() {
this.with_internal_state_ref::<_, Result<(), Value>, _>(|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
/// [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| {
logger(LogMessage::Warn(formatter(args, ctx)?), state);
Ok(())
@ -312,10 +312,10 @@ pub fn warn(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa
///
/// [spec]: https://console.spec.whatwg.org/#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) {
Some(value) => ctx.to_string(value)?,
None => "default".to_owned(),
None => "default".into(),
};
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
/// [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) {
Some(value) => ctx.to_string(value)?,
None => "default".to_owned(),
None => "default".into(),
};
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
/// [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) {
Some(value) => ctx.to_string(value)?,
None => "default".to_owned(),
None => "default".into(),
};
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
/// [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) {
Some(value) => ctx.to_string(value)?,
None => "default".to_owned(),
None => "default".into(),
};
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
/// [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) {
Some(value) => ctx.to_string(value)?,
None => "default".to_owned(),
None => "default".into(),
};
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();
logger(
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
/// [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)?;
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
/// [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| {
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
/// [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| {
let undefined = Value::undefined();
logger(

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

@ -43,7 +43,7 @@ impl Error {
pub(crate) const LENGTH: usize = 1;
/// 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() {
this.set_field(
"message",
@ -71,7 +71,7 @@ impl Error {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("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;
/// 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() {
this.set_field(
"message",
@ -60,7 +60,7 @@ impl RangeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("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;
/// 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() {
this.set_field(
"message",
@ -59,7 +59,7 @@ impl ReferenceError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("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;
/// 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() {
this.set_field(
"message",
@ -67,7 +67,7 @@ impl TypeError {
/// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field("name");
let message = this.get_field("message");
Ok(Value::from(format!("{}: {}", name, message)))

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

@ -16,7 +16,7 @@ use crate::{
array::Array,
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{ResultValue, Value},
value::{RcString, ResultValue, Value},
},
environment::function_environment_record::BindingStatus,
environment::lexical_environment::{new_function_environment, Environment},
@ -28,7 +28,7 @@ use gc::{unsafe_empty_trace, Finalize, Trace};
use std::fmt::{self, Debug};
/// _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
#[derive(Debug, Copy, Clone)]
@ -178,7 +178,7 @@ impl Function {
pub fn call(
&self,
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],
interpreter: &mut Interpreter,
) -> ResultValue {
@ -236,7 +236,7 @@ impl Function {
pub fn construct(
&self,
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],
interpreter: &mut Interpreter,
) -> ResultValue {
@ -376,7 +376,8 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
.writable(true)
.configurable(true);
obj.properties_mut().insert(index.to_string(), prop);
obj.properties_mut()
.insert(RcString::from(index.to_string()), prop);
index += 1;
}
@ -386,7 +387,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
/// Create new function `[[Construct]]`
///
// 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(
Vec::new(),
|_, _, _| Ok(Value::undefined()),

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

@ -44,7 +44,7 @@ impl Json {
///
/// [spec]: https://tc39.es/ecma262/#sec-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>(
&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
/// [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) {
Some(obj) if obj.is_symbol() || obj.is_function() || obj.is_undefined() => {
return Ok(Value::undefined())
@ -132,13 +132,13 @@ impl Json {
.iter()
.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(
key.to_owned(),
Property::default().value(ctx.call(
replacer,
&mut this_arg,
&[Value::string(key), val.clone()],
&this_arg,
&[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(PROTOTYPE);
assert_eq!(
same_value(&object_prototype, &global_object_prototype, true),
true
);
assert_eq!(
same_value(&array_prototype, &global_array_prototype, true),
same_value(&object_prototype, &global_object_prototype),
true
);
assert_eq!(same_value(&array_prototype, &global_array_prototype), true);
}
#[test]

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

@ -24,6 +24,7 @@ use std::f64;
#[cfg(test)]
mod tests;
/// Javascript `Math` object.
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub(crate) struct Math;
@ -39,7 +40,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -51,7 +52,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -63,7 +64,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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
.get(0)
.map_or(f64::NAN, |x| f64::from(x).acosh())
@ -78,7 +79,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -90,7 +91,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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
.get(0)
.map_or(f64::NAN, |x| f64::from(x).asinh())
@ -105,7 +106,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -117,7 +118,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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
.get(0)
.map_or(f64::NAN, |x| f64::from(x).atanh())
@ -132,7 +133,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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() {
f64::NAN
} else {
@ -149,7 +150,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -161,7 +162,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -173,7 +174,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -185,7 +186,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -197,7 +198,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -209,7 +210,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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
.get(0)
.map_or(f64::NAN, |x| f64::from(x).floor())
@ -224,7 +225,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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() {
f64::NAN
} else {
@ -246,7 +247,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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() {
f64::NAN
} else {
@ -268,7 +269,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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() {
f64::NAN
} else {
@ -290,7 +291,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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;
for arg in args {
let num = f64::from(arg);
@ -307,7 +308,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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;
for arg in args {
let num = f64::from(arg);
@ -324,7 +325,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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 {
let num = f64::from(args.get(0).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
/// [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>()))
}
@ -354,7 +355,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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
.get(0)
.map_or(f64::NAN, |x| f64::from(x).round())
@ -369,7 +370,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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() {
f64::NAN
} else {
@ -391,7 +392,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -403,7 +404,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -415,11 +416,19 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
/// 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())
}
@ -431,7 +440,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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())
}
@ -443,7 +452,7 @@ impl Math {
///
/// [spec]: https://tc39.es/ecma262/#sec-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
.get(0)
.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,
symbol::Symbol,
undefined::Undefined,
value::{ResultValue, Value, ValueData},
value::{ResultValue, Value},
};
/// Initializes builtin objects and functions
@ -67,8 +67,8 @@ pub fn init(global: &Value) {
Undefined::init,
];
match global.data() {
ValueData::Object(ref global_object) => {
match global {
Value::Object(ref global_object) => {
for init in &globals {
let (name, value) = init(global);
global_object.borrow_mut().insert_field(name, value);

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

@ -21,7 +21,7 @@ use super::{
object::ObjectData,
};
use crate::{
builtins::value::{ResultValue, Value, ValueData},
builtins::value::{ResultValue, Value},
exec::Interpreter,
BoaProfiler,
};
@ -57,10 +57,10 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue
fn this_number_value(value: &Value, ctx: &mut Interpreter) -> Result<f64, Value> {
match *value.data() {
ValueData::Integer(integer) => return Ok(f64::from(integer)),
ValueData::Rational(rational) => return Ok(rational),
ValueData::Object(ref object) => {
match *value {
Value::Integer(integer) => return Ok(f64::from(integer)),
Value::Rational(rational) => return Ok(rational),
Value::Object(ref object) => {
if let Some(number) = object.borrow().as_number() {
return Ok(number);
}
@ -83,11 +83,7 @@ impl Number {
/// `[[Construct]]` - Creates a Number instance
///
/// `[[Call]]` - Creates a number primitive
pub(crate) fn make_number(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn make_number(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => ctx.to_numeric_number(value)?,
None => 0.0,
@ -109,7 +105,7 @@ impl Number {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_exponential(
this: &mut Value,
this: &Value,
_args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
@ -129,7 +125,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed
#[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 precision = match args.get(0) {
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
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_locale_string(
this: &mut Value,
this: &Value,
_args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
@ -177,11 +173,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_precision(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn to_precision(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_num = Self::this_number_value(this, ctx)?;
let _num_str_len = format!("{}", this_num).len();
let _precision = match args.get(0) {
@ -345,11 +337,7 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_string(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
// 1. Let x be ? thisNumberValue(this value).
let x = Self::this_number_value(this, ctx)?;
@ -402,11 +390,7 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/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 {
Ok(Value::from(Self::this_number_value(this, ctx)?))
}
@ -424,14 +408,10 @@ impl Number {
///
/// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt
pub(crate) fn parse_int(
_this: &mut Value,
args: &[Value],
_ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn parse_int(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
if let (Some(val), r) = (args.get(0), args.get(1)) {
let mut radix = if let Some(rx) = r {
if let ValueData::Integer(i) = rx.data() {
if let Value::Integer(i) = rx {
*i as u32
} else {
// Handling a second argument that isn't an integer but was provided so cannot be defaulted.
@ -442,8 +422,8 @@ impl Number {
0
};
match val.data() {
ValueData::String(s) => {
match val {
Value::String(s) => {
// Attempt to infer radix from given string.
if radix == 0 {
@ -466,8 +446,8 @@ impl Number {
Ok(Value::from(f64::NAN))
}
}
ValueData::Integer(i) => Ok(Value::integer(*i)),
ValueData::Rational(f) => Ok(Value::integer(*f as i32)),
Value::Integer(i) => Ok(Value::integer(*i)),
Value::Rational(f) => Ok(Value::integer(*f as i32)),
_ => {
// Wrong argument type to parseInt.
Ok(Value::from(f64::NAN))
@ -495,13 +475,13 @@ impl Number {
/// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat
pub(crate) fn parse_float(
_this: &mut Value,
_this: &Value,
args: &[Value],
_ctx: &mut Interpreter,
) -> ResultValue {
if let Some(val) = args.get(0) {
match val.data() {
ValueData::String(s) => {
match val {
Value::String(s) => {
if let Ok(i) = s.parse::<i32>() {
// Attempt to parse an integer first so that it can be stored as an integer
// to improve performance
@ -513,8 +493,8 @@ impl Number {
Ok(Value::from(f64::NAN))
}
}
ValueData::Integer(i) => Ok(Value::integer(*i)),
ValueData::Rational(f) => Ok(Value::rational(*f)),
Value::Integer(i) => Ok(Value::integer(*i)),
Value::Rational(f) => Ok(Value::rational(*f)),
_ => {
// Wrong argument type to parseFloat.
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::{
object::{Object, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{same_value, Value, ValueData},
value::{same_value, RcString, Value},
};
use crate::BoaProfiler;
use std::borrow::Borrow;
use std::ops::Deref;
impl Object {
/// Check if object has property.
@ -29,8 +27,8 @@ impl Object {
if !parent.is_null() {
// the parent value variant should be an object
// In the unlikely event it isn't return false
return match *parent {
ValueData::Object(ref obj) => (*obj).deref().borrow().has_property(val),
return match parent {
Value::Object(ref obj) => obj.borrow().has_property(val),
_ => false,
};
}
@ -48,11 +46,7 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible
#[inline]
pub fn is_extensible(&self) -> bool {
let val = self.get_internal_slot("extensible");
match *val.deref().borrow() {
ValueData::Boolean(b) => b,
_ => false,
}
self.extensible
}
/// Disable extensibility.
@ -63,7 +57,7 @@ impl Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
#[inline]
pub fn prevent_extensions(&mut self) -> bool {
self.set_internal_slot("extensible", Value::from(false));
self.extensible = false;
true
}
@ -241,7 +235,6 @@ impl Object {
&& !same_value(
&desc.value.clone().unwrap(),
&current.value.clone().unwrap(),
false,
)
{
return false;
@ -253,21 +246,13 @@ impl Object {
} else {
if !current.configurable.unwrap() {
if desc.set.is_some()
&& !same_value(
&desc.set.clone().unwrap(),
&current.set.clone().unwrap(),
false,
)
&& !same_value(&desc.set.clone().unwrap(), &current.set.clone().unwrap())
{
return false;
}
if desc.get.is_some()
&& !same_value(
&desc.get.clone().unwrap(),
&current.get.clone().unwrap(),
false,
)
&& !same_value(&desc.get.clone().unwrap(), &current.get.clone().unwrap())
{
return false;
}
@ -293,8 +278,8 @@ impl Object {
debug_assert!(Property::is_property_key(prop));
// Prop could either be a String or Symbol
match *(*prop) {
ValueData::String(ref st) => {
match *prop {
Value::String(ref st) => {
self.properties()
.get(st)
.map_or_else(Property::default, |v| {
@ -312,23 +297,24 @@ impl Object {
d
})
}
ValueData::Symbol(ref symbol) => self
.symbol_properties()
.get(&symbol.hash())
.map_or_else(Property::default, |v| {
let mut d = Property::default();
if v.is_data_descriptor() {
d.value = v.value.clone();
d.writable = v.writable;
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.enumerable = v.enumerable;
d.configurable = v.configurable;
d
}),
Value::Symbol(ref symbol) => {
self.symbol_properties()
.get(&symbol.hash())
.map_or_else(Property::default, |v| {
let mut d = Property::default();
if v.is_data_descriptor() {
d.value = v.value.clone();
d.writable = v.writable;
} else {
debug_assert!(v.is_accessor_descriptor());
d.get = v.get.clone();
d.set = v.set.clone();
}
d.enumerable = v.enumerable;
d.configurable = v.configurable;
d
})
}
_ => Property::default(),
}
}
@ -347,11 +333,10 @@ impl Object {
pub fn set_prototype_of(&mut self, val: Value) -> bool {
debug_assert!(val.is_object() || val.is_null());
let current = self.get_internal_slot(PROTOTYPE);
if same_value(&current, &val, false) {
if same_value(&current, &val) {
return true;
}
let extensible = self.get_internal_slot("extensible");
if extensible.is_null() {
if !self.is_extensible() {
return false;
}
let mut p = val.clone();
@ -359,7 +344,7 @@ impl Object {
while !done {
if p.is_null() {
done = true
} else if same_value(&Value::from(self.clone()), &p, false) {
} else if same_value(&Value::from(self.clone()), &p) {
return false;
} else {
p = p.get_internal_slot(PROTOTYPE);
@ -402,7 +387,7 @@ impl Object {
#[inline]
pub(crate) fn insert_property<N>(&mut self, name: N, p: Property)
where
N: Into<String>,
N: Into<RcString>,
{
self.properties.insert(name.into(), p);
}
@ -420,7 +405,7 @@ impl Object {
#[inline]
pub(crate) fn insert_field<N>(&mut self, name: N, value: Value) -> Option<Property>
where
N: Into<String>,
N: Into<RcString>,
{
self.properties.insert(
name.into(),

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

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

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

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

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

@ -17,7 +17,7 @@ use crate::{
builtins::{
object::{Object, ObjectData},
property::Property,
value::{ResultValue, Value, ValueData},
value::{RcString, ResultValue, Value},
RegExp,
},
exec::Interpreter,
@ -28,7 +28,6 @@ use std::string::String as StdString;
use std::{
cmp::{max, min},
f64::NAN,
ops::Deref,
};
/// JavaScript `String` implementation.
@ -42,13 +41,13 @@ impl String {
/// The amount of arguments this function object takes.
pub(crate) const LENGTH: usize = 1;
fn this_string_value(this: &Value, ctx: &mut Interpreter) -> Result<StdString, Value> {
match this.data() {
ValueData::String(ref string) => return Ok(string.clone()),
ValueData::Object(ref object) => {
fn this_string_value(this: &Value, ctx: &mut Interpreter) -> Result<RcString, Value> {
match this {
Value::String(ref string) => return Ok(string.clone()),
Value::Object(ref object) => {
let object = object.borrow();
if let Some(string) = object.as_string() {
return Ok(string.to_owned());
return Ok(string);
}
}
_ => {}
@ -61,16 +60,12 @@ impl String {
///
/// [[Call]] - Returns a new native `string`
/// <https://tc39.es/ecma262/#sec-string-constructor-string-value>
pub(crate) fn make_string(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn make_string(this: &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
// to its Javascript Identifier (global constructor method name)
let string = match args.get(0) {
Some(ref value) => ctx.to_string(value)?,
None => StdString::new(),
None => RcString::default(),
};
let length = string.chars().count();
@ -85,7 +80,7 @@ impl String {
/// Get the string value to a primitive string
#[allow(clippy::wrong_self_convention)]
#[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
Ok(Value::from(Self::this_string_value(this, ctx)?))
}
@ -106,7 +101,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -148,11 +143,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt
pub(crate) fn char_code_at(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn char_code_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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -192,11 +183,11 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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.
// Then we convert it into a Rust String by wrapping it in from_value
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 {
string.push_str(&ctx.to_string(arg)?);
@ -216,7 +207,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -239,7 +230,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -286,11 +277,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
pub(crate) fn starts_with(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn starts_with(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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -319,7 +306,7 @@ impl String {
} else {
// Only use the part of the string from "start"
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
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
pub(crate) fn ends_with(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn ends_with(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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -367,7 +350,7 @@ impl String {
} else {
// Only use the part of the string up to "end"
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
/// [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.
// Then we convert it into a Rust String by wrapping it in from_value
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"
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
fn get_regex_string(value: &Value) -> StdString {
match value.deref() {
ValueData::String(ref body) => body.into(),
ValueData::Object(ref obj) => {
match value {
Value::String(ref body) => body.to_string(),
Value::Object(ref obj) => {
let obj = obj.borrow();
if obj.internal_slots().get("RegExpMatcher").is_some() {
@ -443,7 +426,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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
let primitive_val = ctx.to_string(this)?;
if args.is_empty() {
@ -460,8 +443,8 @@ impl String {
let replace_value = if args.len() > 1 {
// replace_object could be a string or function or not exist at all
let replace_object: &Value = args.get(1).expect("second argument expected");
match replace_object.deref() {
ValueData::String(val) => {
match replace_object {
Value::String(val) => {
// https://tc39.es/ecma262/#table-45
let mut result = val.to_string();
let re = Regex::new(r"\$(\d)").unwrap();
@ -500,7 +483,7 @@ impl String {
result
}
ValueData::Object(_) => {
Value::Object(_) => {
// This will return the matched substring first, then captured parenthesized groups later
let mut results: Vec<Value> = caps
.iter()
@ -518,7 +501,7 @@ impl String {
let result = ctx.call(&replace_object, this, &results).unwrap();
ctx.to_string(&result)?
ctx.to_string(&result)?.to_string()
}
_ => "undefined".to_string(),
}
@ -545,7 +528,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -573,7 +556,7 @@ impl String {
// checking "starts with" the search string
for index in start..length {
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
return Ok(Value::from(index));
}
@ -595,7 +578,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf
pub(crate) fn last_index_of(
this: &mut Value,
this: &Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
@ -627,7 +610,7 @@ impl String {
let mut highest_index = -1;
for index in start..length {
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;
}
}
@ -647,10 +630,9 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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
pub(crate) fn r#match(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let mut re =
RegExp::make_regexp(&mut Value::from(Object::default()), &[args[0].clone()], ctx)?;
RegExp::r#match(&mut re, ctx.to_string(this)?, ctx)
pub(crate) fn r#match(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let re = RegExp::make_regexp(&Value::from(Object::default()), &[args[0].clone()], ctx)?;
RegExp::r#match(&re, ctx.to_string(this)?, ctx)
}
/// Abstract method `StringPad`.
@ -658,9 +640,9 @@ impl String {
/// Performs the actual string padding for padStart/End.
/// <https://tc39.es/ecma262/#sec-stringpad/>
fn string_pad(
primitive: StdString,
primitive: RcString,
max_length: i32,
fill_string: Option<StdString>,
fill_string: Option<RcString>,
at_start: bool,
) -> ResultValue {
let primitive_length = primitive.len() as i32;
@ -669,20 +651,13 @@ impl String {
return Ok(Value::from(primitive));
}
let filler = match fill_string {
Some(filler) => filler,
None => " ".to_owned(),
};
if filler == "" {
return Ok(Value::from(primitive));
}
let filter = fill_string.as_deref().unwrap_or(" ");
let fill_len = max_length.wrapping_sub(primitive_length);
let mut fill_str = StdString::new();
while fill_str.len() < fill_len as usize {
fill_str.push_str(&filler);
fill_str.push_str(filter);
}
// Cut to size max_length
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
/// [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 {
let primitive_val = ctx.to_string(this)?;
pub(crate) fn pad_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive = ctx.to_string(this)?;
if args.is_empty() {
return Err(Value::from("padEnd requires maxLength argument"));
}
@ -716,13 +691,9 @@ impl String {
.expect("failed to get argument for String method"),
);
let fill_string = if args.len() != 1 {
Some(ctx.to_string(args.get(1).expect("Could not get argument"))?)
} else {
None
};
let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?;
Self::string_pad(primitive_val, max_length, fill_string, false)
Self::string_pad(primitive, max_length, fill_string, false)
}
/// `String.prototype.padStart( targetLength [, padString] )`
@ -737,12 +708,8 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart
pub(crate) fn pad_start(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let primitive_val = ctx.to_string(this)?;
pub(crate) fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive = ctx.to_string(this)?;
if args.is_empty() {
return Err(Value::from("padStart requires maxLength argument"));
}
@ -751,12 +718,9 @@ impl String {
.expect("failed to get argument for String method"),
);
let fill_string = match args.len() {
1 => None,
_ => Some(ctx.to_string(args.get(1).expect("Could not get argument"))?),
};
let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?;
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.
@ -790,7 +754,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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)?;
Ok(Value::from(
this_str.trim_matches(Self::is_trimmable_whitespace),
@ -809,7 +773,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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)?;
Ok(Value::from(
this_str.trim_start_matches(Self::is_trimmable_whitespace),
@ -828,7 +792,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.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)?;
Ok(Value::from(
this_str.trim_end_matches(Self::is_trimmable_whitespace),
@ -846,11 +810,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_lowercase(
this: &mut Value,
_: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn to_lowercase(this: &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.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str = ctx.to_string(this)?;
@ -872,11 +832,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_uppercase(
this: &mut Value,
_: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn to_uppercase(this: &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.
// Then we convert it into a Rust String by wrapping it in from_value
let this_str = ctx.to_string(this)?;
@ -895,11 +851,7 @@ impl String {
///
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring
pub(crate) fn substring(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
pub(crate) fn substring(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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -947,7 +899,7 @@ impl String {
/// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/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.
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.to_string(this)?;
@ -1002,7 +954,7 @@ impl String {
///
/// [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
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
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
/// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges
// TODO: update this method to return iterator
pub(crate) fn match_all(
this: &mut Value,
args: &[Value],
ctx: &mut Interpreter,
) -> ResultValue {
let mut re: Value = match args.get(0) {
pub(crate) fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let re: Value = match args.get(0) {
Some(arg) => {
if arg.is_null() {
RegExp::make_regexp(
&mut Value::from(Object::default()),
&Value::from(Object::default()),
&[Value::from(ctx.to_string(arg)?), Value::from("g")],
ctx,
)
} else if arg.is_undefined() {
RegExp::make_regexp(
&mut Value::from(Object::default()),
&Value::from(Object::default()),
&[Value::undefined(), Value::from("g")],
ctx,
)
@ -1044,13 +992,13 @@ impl String {
}
}
None => RegExp::make_regexp(
&mut Value::from(Object::default()),
&Value::from(Object::default()),
&[Value::from(""), Value::from("g")],
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.

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

@ -20,14 +20,14 @@ mod tests;
use super::function::{make_builtin_fn, make_constructor_fn};
use crate::{
builtins::value::{ResultValue, Value, ValueData},
builtins::value::{RcString, RcSymbol, ResultValue, Value},
exec::Interpreter,
BoaProfiler,
};
use gc::{Finalize, Trace};
#[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 {
/// The name of the object.
@ -46,13 +46,13 @@ impl Symbol {
self.1
}
fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result<Self, Value> {
match value.data() {
ValueData::Symbol(ref symbol) => return Ok(symbol.clone()),
ValueData::Object(ref object) => {
fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result<RcSymbol, Value> {
match value {
Value::Symbol(ref symbol) => return Ok(symbol.clone()),
Value::Object(ref object) => {
let object = object.borrow();
if let Some(symbol) = object.as_symbol() {
return Ok(symbol.clone());
return Ok(symbol);
}
}
_ => {}
@ -72,11 +72,9 @@ impl Symbol {
///
/// [spec]: https://tc39.es/ecma262/#sec-symbol-description
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol
pub(crate) fn call(_: &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) {
Some(ref value) if !value.is_undefined() => {
Some(ctx.to_string(value)?.into_boxed_str())
}
Some(ref value) if !value.is_undefined() => Some(ctx.to_string(value)?),
_ => None,
};
@ -94,7 +92,7 @@ impl Symbol {
/// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString
#[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 description = symbol.description().unwrap_or("");
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 {
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)]
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 {
fn from(value: usize) -> Value {
Value::integer(value as i32)
@ -120,7 +132,7 @@ where
let mut array = Object::default();
for (i, item) in value.iter().enumerate() {
array.properties_mut().insert(
i.to_string(),
RcString::from(i.to_string()),
Property::default().value(item.clone().into()),
);
}
@ -135,9 +147,10 @@ where
fn from(value: Vec<T>) -> Self {
let mut array = Object::default();
for (i, item) in value.into_iter().enumerate() {
array
.properties_mut()
.insert(i.to_string(), Property::default().value(item.into()));
array.properties_mut().insert(
RcString::from(i.to_string()),
Property::default().value(item.into()),
);
}
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 {
fn from(_: ()) -> Self {
Value::null()

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

@ -1,11 +1,5 @@
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
/// Can be used to print both properties and internal slots
/// 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 {
// 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
// which type of Object it represents for special printing
match v.borrow().data {
@ -95,7 +89,7 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String {
log_string_from(
&v.borrow()
.properties()
.get(&i.to_string())
.get(i.to_string().as_str())
.unwrap()
.value
.clone()
@ -111,16 +105,13 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String {
_ => display_obj(&x, print_internals),
}
}
ValueData::Symbol(ref symbol) => match symbol.description() {
Some(ref desc) => format!("Symbol({})", desc),
None => String::from("Symbol()"),
},
Value::Symbol(ref symbol) => symbol.to_string(),
_ => format!("{}", x),
}
}
/// 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
// 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 {
@ -133,12 +124,12 @@ pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String {
let mut encounters = HashSet::new();
fn display_obj_internal(
data: &ValueData,
data: &Value,
encounters: &mut HashSet<usize>,
indent: usize,
print_internals: bool,
) -> String {
if let ValueData::Object(ref v) = *data {
if let Value::Object(ref v) = *data {
// The in-memory address of the current object
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)
}
impl Display for ValueData {
impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Null => write!(f, "null"),

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

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

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

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

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

@ -11,13 +11,16 @@ pub use crate::builtins::value::val_type::Type;
use crate::builtins::{
function::Function,
object::{InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
object::{
GcObject, InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE,
PROTOTYPE,
},
property::Property,
BigInt, Symbol,
};
use crate::exec::Interpreter;
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 std::{
any::Any,
@ -34,32 +37,57 @@ pub mod display;
pub mod equality;
pub mod hash;
pub mod operations;
pub mod rcbigint;
pub mod rcstring;
pub mod rcsymbol;
pub use conversions::*;
pub(crate) use display::display_obj;
pub use equality::*;
pub use hash::*;
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`)
#[must_use]
pub type ResultValue = Result<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter.
#[derive(Debug, Clone, Trace, Finalize, Default)]
pub struct Value(Gc<ValueData>);
/// A Javascript value
#[derive(Trace, Finalize, Debug, Clone)]
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 {
/// Creates a new `undefined` value.
#[inline]
pub fn undefined() -> Self {
Self(Gc::new(ValueData::Undefined))
Self::Undefined
}
/// Creates a new `null` value.
#[inline]
pub fn null() -> Self {
Self(Gc::new(ValueData::Null))
Self::Null
}
/// Creates a new number with `NaN` value.
@ -72,9 +100,9 @@ impl Value {
#[inline]
pub fn string<S>(value: S) -> Self
where
S: Into<String>,
S: Into<RcString>,
{
Self(Gc::new(ValueData::String(value.into())))
Self::String(value.into())
}
/// Creates a new number value.
@ -83,7 +111,7 @@ impl Value {
where
N: Into<f64>,
{
Self(Gc::new(ValueData::Rational(value.into())))
Self::Rational(value.into())
}
/// Creates a new number value.
@ -92,7 +120,7 @@ impl Value {
where
I: Into<i32>,
{
Self(Gc::new(ValueData::Integer(value.into())))
Self::Integer(value.into())
}
/// Creates a new number value.
@ -106,38 +134,35 @@ impl Value {
/// Creates a new bigint value.
#[inline]
pub fn bigint(value: BigInt) -> Self {
Self(Gc::new(ValueData::BigInt(value)))
pub fn bigint<B>(value: B) -> Self
where
B: Into<RcBigInt>,
{
Self::BigInt(value.into())
}
/// Creates a new boolean value.
#[inline]
pub fn boolean(value: bool) -> Self {
Self(Gc::new(ValueData::Boolean(value)))
Self::Boolean(value)
}
/// Creates a new object value.
#[inline]
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.
#[inline]
pub fn symbol(symbol: Symbol) -> Self {
Self(Gc::new(ValueData::Symbol(symbol)))
}
/// Gets the underlying `ValueData` structure.
#[inline]
pub fn data(&self) -> &ValueData {
&*self.0
pub(crate) fn symbol(symbol: Symbol) -> Self {
Self::Symbol(RcSymbol::from(symbol))
}
/// Helper function to convert the `Value` to a number and compute its power.
pub fn as_num_to_power(&self, other: Self) -> Self {
match (self.data(), other.data()) {
(ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => Self::bigint(a.clone().pow(b)),
match (self, other) {
(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())),
}
}
@ -145,6 +170,7 @@ impl Value {
/// Returns a new empty object
pub fn new_object(global: Option<&Value>) -> Self {
let _timer = BoaProfiler::global().start_event("new_object", "value");
if let Some(global) = global {
let object_prototype = global.get_field("Object").get_field(PROTOTYPE);
@ -225,10 +251,10 @@ impl Value {
/// Conversts the `Value` to `JSON`.
pub fn to_json(&self, interpreter: &mut Interpreter) -> Result<JSONValue, Value> {
match *self.data() {
ValueData::Null => Ok(JSONValue::Null),
ValueData::Boolean(b) => Ok(JSONValue::Bool(b)),
ValueData::Object(ref obj) => {
match *self {
Self::Null => Ok(JSONValue::Null),
Self::Boolean(b) => Ok(JSONValue::Bool(b)),
Self::Object(ref obj) => {
if obj.borrow().is_array() {
let mut arr: Vec<JSONValue> = Vec::new();
for k in obj.borrow().properties().keys() {
@ -248,59 +274,26 @@ impl Value {
let key = k.clone();
let value = self.get_field(k.to_string());
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))
}
}
ValueData::String(ref str) => Ok(JSONValue::String(str.clone())),
ValueData::Rational(num) => Ok(JSONNumber::from_f64(num)
Self::String(ref str) => Ok(JSONValue::String(str.to_string())),
Self::Rational(num) => Ok(JSONNumber::from_f64(num)
.map(JSONValue::Number)
.unwrap_or(JSONValue::Null)),
ValueData::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))),
ValueData::BigInt(_) => {
Self::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))),
Self::BigInt(_) => {
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");
}
}
}
}
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
///
/// For now always returns true.
@ -430,7 +423,7 @@ impl ValueData {
Self::Rational(n) if n != 0.0 && !n.is_nan() => true,
Self::Integer(n) if n != 0 => true,
Self::Boolean(v) => v,
Self::BigInt(ref n) if *n != 0 => true,
Self::BigInt(ref n) if *n.as_inner() != 0 => true,
_ => false,
}
}
@ -488,7 +481,7 @@ impl ValueData {
Self::String(ref s) if !s.is_empty() => true,
Self::Rational(n) if n != 0.0 && !n.is_nan() => 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,
_ => 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
/// 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
pub fn get_field<F>(&self, field: F) -> Value
pub fn get_field<F>(&self, field: F) -> Self
where
F: Into<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
Self::String(ref s) => {
match self.get_property(s) {
@ -727,7 +720,7 @@ impl ValueData {
/// Set the property in the value.
pub fn set_property<S>(&self, field: S, property: Property) -> Property
where
S: Into<String>,
S: Into<RcString>,
{
if let Some(mut object) = self.as_object_mut() {
object
@ -758,7 +751,7 @@ impl ValueData {
}
}
impl Default for ValueData {
impl Default for Value {
fn default() -> Self {
Self::Undefined
}

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

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

8
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 {
let obj_rec = Box::new(ObjectEnvironmentRecord {
let obj_rec = ObjectEnvironmentRecord {
bindings: global,
outer_env: None,
/// 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
/// for any object Environment Record.
with_environment: false,
});
};
let dcl_rec = Box::new(DeclarativeEnvironmentRecord {
let dcl_rec = DeclarativeEnvironmentRecord {
env_rec: FxHashMap::default(),
outer_env: None,
});
};
Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord {
object_record: obj_rec,

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

@ -4,7 +4,7 @@ use super::{Executable, Interpreter, InterpreterState};
use crate::{
builtins::{
object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
value::{ResultValue, Type, Value, ValueData},
value::{ResultValue, Type, Value},
},
syntax::ast::node::{Call, New, Node},
BoaProfiler,
@ -13,7 +13,7 @@ use crate::{
impl Executable for Call {
fn run(&self, interpreter: &mut Interpreter) -> ResultValue {
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) => {
let mut obj = get_const_field.obj().run(interpreter)?;
if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol {
@ -45,7 +45,7 @@ impl Executable for Call {
}
// 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
interpreter.set_current_state(InterpreterState::Executing);
@ -66,15 +66,15 @@ impl Executable for New {
for arg in self.args() {
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
this.set_internal_slot(INSTANCE_PROTOTYPE, func_object.get_field(PROTOTYPE));
match func_object.data() {
ValueData::Object(ref obj) => {
let obj = (**obj).borrow();
match func_object {
Value::Object(ref obj) => {
let obj = obj.borrow();
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")
}

238
boa/src/exec/mod.rs

@ -26,7 +26,7 @@ use crate::{
function::{Function as FunctionObject, FunctionBody, ThisMode},
object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{ResultValue, Type, Value, ValueData},
value::{RcBigInt, RcString, ResultValue, Type, Value},
BigInt, Number,
},
realm::Realm,
@ -37,7 +37,7 @@ use crate::{
BoaProfiler,
};
use std::convert::TryFrom;
use std::{borrow::Borrow, ops::Deref};
use std::ops::Deref;
pub trait Executable {
/// Runs this executable in the given executor.
@ -156,12 +156,12 @@ impl Interpreter {
pub(crate) fn call(
&mut self,
f: &Value,
this: &mut Value,
this: &Value,
arguments_list: &[Value],
) -> ResultValue {
match *f.data() {
ValueData::Object(ref obj) => {
let obj = (**obj).borrow();
match *f {
Value::Object(ref obj) => {
let obj = obj.borrow();
if let ObjectData::Function(ref func) = obj.data {
return func.call(f.clone(), this, arguments_list, self);
}
@ -173,20 +173,18 @@ impl Interpreter {
/// Converts a value into a rust heap allocated string.
#[allow(clippy::wrong_self_convention)]
pub fn to_string(&mut self, value: &Value) -> Result<String, Value> {
match value.data() {
ValueData::Null => Ok("null".to_owned()),
ValueData::Undefined => Ok("undefined".to_owned()),
ValueData::Boolean(boolean) => Ok(boolean.to_string()),
ValueData::Rational(rational) => Ok(Number::to_native_string(*rational)),
ValueData::Integer(integer) => Ok(integer.to_string()),
ValueData::String(string) => Ok(string.clone()),
ValueData::Symbol(_) => {
Err(self.construct_type_error("can't convert symbol to string"))
}
ValueData::BigInt(ref bigint) => Ok(bigint.to_string()),
ValueData::Object(_) => {
let primitive = self.to_primitive(&mut value.clone(), PreferredType::String);
pub fn to_string(&mut self, value: &Value) -> Result<RcString, Value> {
match value {
Value::Null => Ok(RcString::from("null")),
Value::Undefined => Ok(RcString::from("undefined".to_owned())),
Value::Boolean(boolean) => Ok(RcString::from(boolean.to_string())),
Value::Rational(rational) => Ok(RcString::from(Number::to_native_string(*rational))),
Value::Integer(integer) => Ok(RcString::from(integer.to_string())),
Value::String(string) => Ok(string.clone()),
Value::Symbol(_) => Err(self.construct_type_error("can't convert symbol to string")),
Value::BigInt(ref bigint) => Ok(RcString::from(bigint.to_string())),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::String)?;
self.to_string(&primitive)
}
}
@ -194,33 +192,31 @@ impl Interpreter {
/// Helper function.
#[allow(clippy::wrong_self_convention)]
pub fn to_bigint(&mut self, value: &Value) -> Result<BigInt, Value> {
match value.data() {
ValueData::Null => Err(self.construct_type_error("cannot convert null to a BigInt")),
ValueData::Undefined => {
pub fn to_bigint(&mut self, value: &Value) -> Result<RcBigInt, Value> {
match value {
Value::Null => Err(self.construct_type_error("cannot convert null to a BigInt")),
Value::Undefined => {
Err(self.construct_type_error("cannot convert undefined to a BigInt"))
}
ValueData::String(ref string) => Ok(BigInt::from_string(string, self)?),
ValueData::Boolean(true) => Ok(BigInt::from(1)),
ValueData::Boolean(false) => Ok(BigInt::from(0)),
ValueData::Integer(num) => Ok(BigInt::from(*num)),
ValueData::Rational(num) => {
Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, self)?)),
Value::Boolean(true) => Ok(RcBigInt::from(BigInt::from(1))),
Value::Boolean(false) => Ok(RcBigInt::from(BigInt::from(0))),
Value::Integer(num) => Ok(RcBigInt::from(BigInt::from(*num))),
Value::Rational(num) => {
if let Ok(bigint) = BigInt::try_from(*num) {
return Ok(bigint);
return Ok(RcBigInt::from(bigint));
}
Err(self.construct_type_error(format!(
"The number {} cannot be converted to a BigInt because it is not an integer",
num
)))
}
ValueData::BigInt(b) => Ok(b.clone()),
ValueData::Object(_) => {
let primitive = self.to_primitive(&mut value.clone(), PreferredType::Number);
Value::BigInt(b) => Ok(b.clone()),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::Number)?;
self.to_bigint(&primitive)
}
ValueData::Symbol(_) => {
Err(self.construct_type_error("cannot convert Symbol to a BigInt"))
}
Value::Symbol(_) => Err(self.construct_type_error("cannot convert Symbol to a BigInt")),
}
}
@ -265,19 +261,19 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumber
#[allow(clippy::wrong_self_convention)]
pub fn to_number(&mut self, value: &Value) -> Result<f64, Value> {
match *value.data() {
ValueData::Null => Ok(0.0),
ValueData::Undefined => Ok(f64::NAN),
ValueData::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }),
match *value {
Value::Null => Ok(0.0),
Value::Undefined => Ok(f64::NAN),
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
ValueData::String(ref string) => Ok(string.parse().unwrap_or(f64::NAN)),
ValueData::Rational(number) => Ok(number),
ValueData::Integer(integer) => Ok(f64::from(integer)),
ValueData::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")),
ValueData::Object(_) => {
let prim_value = self.to_primitive(&mut (value.clone()), PreferredType::Number);
self.to_number(&prim_value)
Value::String(ref string) => Ok(string.parse().unwrap_or(f64::NAN)),
Value::Rational(number) => Ok(number),
Value::Integer(integer) => Ok(f64::from(integer)),
Value::Symbol(_) => Err(self.construct_type_error("argument must not be a symbol")),
Value::BigInt(_) => Err(self.construct_type_error("argument must not be a bigint")),
Value::Object(_) => {
let primitive = self.to_primitive(value, PreferredType::Number)?;
self.to_number(&primitive)
}
}
}
@ -287,7 +283,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)]
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() {
return Ok(primitive);
}
@ -301,7 +297,7 @@ impl Interpreter {
/// See: https://tc39.es/ecma262/#sec-tonumeric
#[allow(clippy::wrong_self_convention)]
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() {
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
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
if let ObjectData::Array = x.deref().borrow().data {
let length: i32 = self.value_to_rust_number(&value.get_field("length")) as i32;
let values: Vec<Value> = (0..length)
let length = i32::from(&value.get_field("length"));
let values = (0..length)
.map(|idx| value.get_field(idx.to_string()))
.collect();
return Ok(values);
@ -328,58 +324,73 @@ impl Interpreter {
Err(())
}
/// <https://tc39.es/ecma262/#sec-ordinarytoprimitive>
pub(crate) fn ordinary_to_primitive(&mut self, o: &mut Value, hint: PreferredType) -> Value {
/// Converts an object to a primitive.
///
/// 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);
// 2. Assert: Type(hint) is String and its value is either "string" or "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 {
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);
// b. If IsCallable(method) is true, then
if method.is_function() {
let result = self.call(&method, o, &[]);
match result {
Ok(val) => {
if val.is_object() {
// TODO: throw exception
continue;
} else {
return val;
}
}
Err(_) => continue,
// i. Let result be ? Call(method, O).
let result = self.call(&method, &o, &[])?;
// ii. If Type(result) is not Object, return result.
if !result.is_object() {
return Ok(result);
}
}
}
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.
///
/// <https://tc39.es/ecma262/#sec-toprimitive>
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_primitive(
&mut self,
input: &mut Value,
input: &Value,
preferred_type: PreferredType,
) -> Value {
let mut hint: PreferredType;
match (*input).deref() {
ValueData::Object(_) => {
hint = preferred_type;
// Skip d, e we don't support Symbols yet
// TODO: add when symbols are supported
if hint == PreferredType::Default {
hint = PreferredType::Number;
};
self.ordinary_to_primitive(input, hint)
}
_ => input.clone(),
) -> ResultValue {
// 1. Assert: input is an ECMAScript language value. (always a value not need to check)
// 2. If Type(input) is Object, then
if let Value::Object(_) = input {
let mut hint = preferred_type;
// Skip d, e we don't support Symbols yet
// TODO: add when symbols are supported
// TODO: Add other steps.
if hint == PreferredType::Default {
hint = PreferredType::Number;
};
// g. Return ? OrdinaryToPrimitive(input, hint).
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
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_property_key(&mut self, value: &mut Value) -> ResultValue {
let key = self.to_primitive(value, PreferredType::String);
pub(crate) fn to_property_key(&mut self, value: &Value) -> ResultValue {
let key = self.to_primitive(value, PreferredType::String)?;
if key.is_symbol() {
Ok(key)
} else {
@ -397,7 +408,7 @@ impl Interpreter {
}
/// 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 !Property::is_property_key(key) {
false
@ -413,9 +424,9 @@ impl Interpreter {
/// https://tc39.es/ecma262/#sec-toobject
#[allow(clippy::wrong_self_convention)]
pub(crate) fn to_object(&mut self, value: &Value) -> ResultValue {
match value.data() {
ValueData::Undefined | ValueData::Null => Err(Value::undefined()),
ValueData::Boolean(boolean) => {
match value {
Value::Undefined | Value::Null => Err(Value::undefined()),
Value::Boolean(boolean) => {
let proto = self
.realm
.environment
@ -428,7 +439,7 @@ impl Interpreter {
ObjectData::Boolean(*boolean),
))
}
ValueData::Integer(integer) => {
Value::Integer(integer) => {
let proto = self
.realm
.environment
@ -440,7 +451,7 @@ impl Interpreter {
ObjectData::Number(f64::from(*integer)),
))
}
ValueData::Rational(rational) => {
Value::Rational(rational) => {
let proto = self
.realm
.environment
@ -453,7 +464,7 @@ impl Interpreter {
ObjectData::Number(*rational),
))
}
ValueData::String(ref string) => {
Value::String(ref string) => {
let proto = self
.realm
.environment
@ -466,7 +477,7 @@ impl Interpreter {
ObjectData::String(string.clone()),
))
}
ValueData::Symbol(ref symbol) => {
Value::Symbol(ref symbol) => {
let proto = self
.realm
.environment
@ -479,7 +490,7 @@ impl Interpreter {
ObjectData::Symbol(symbol.clone()),
))
}
ValueData::BigInt(ref bigint) => {
Value::BigInt(ref bigint) => {
let proto = self
.realm
.environment
@ -490,36 +501,7 @@ impl Interpreter {
Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone()));
Ok(bigint_obj)
}
ValueData::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)
}
Value::Object(_) => Ok(value.clone()),
}
}

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

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

23
boa/src/realm.rs

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

Loading…
Cancel
Save