Browse Source

Value refactor (#383)

pull/389/head
HalidOdat 5 years ago committed by GitHub
parent
commit
1e18cb02d0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 254
      boa/src/builtins/array/mod.rs
  2. 28
      boa/src/builtins/boolean/mod.rs
  3. 2
      boa/src/builtins/boolean/tests.rs
  4. 56
      boa/src/builtins/console/mod.rs
  5. 32
      boa/src/builtins/console/tests.rs
  6. 14
      boa/src/builtins/error.rs
  7. 10
      boa/src/builtins/function/mod.rs
  8. 12
      boa/src/builtins/json/mod.rs
  9. 185
      boa/src/builtins/math/mod.rs
  10. 8
      boa/src/builtins/mod.rs
  11. 34
      boa/src/builtins/number/mod.rs
  12. 4
      boa/src/builtins/number/tests.rs
  13. 13
      boa/src/builtins/object/internal_methods_trait.rs
  14. 47
      boa/src/builtins/object/mod.rs
  15. 70
      boa/src/builtins/property/mod.rs
  16. 10
      boa/src/builtins/property/tests.rs
  17. 103
      boa/src/builtins/regexp/mod.rs
  18. 2
      boa/src/builtins/regexp/tests.rs
  19. 256
      boa/src/builtins/string/mod.rs
  20. 2
      boa/src/builtins/string/tests.rs
  21. 16
      boa/src/builtins/symbol/mod.rs
  22. 2
      boa/src/builtins/symbol/tests.rs
  23. 247
      boa/src/builtins/value/conversions.rs
  24. 210
      boa/src/builtins/value/mod.rs
  25. 87
      boa/src/builtins/value/operations.rs
  26. 26
      boa/src/builtins/value/tests.rs
  27. 6
      boa/src/environment/declarative_environment_record.rs
  28. 6
      boa/src/environment/function_environment_record.rs
  29. 22
      boa/src/environment/global_environment_record.rs
  30. 8
      boa/src/environment/lexical_environment.rs
  31. 19
      boa/src/environment/object_environment_record.rs
  32. 171
      boa/src/exec/mod.rs
  33. 6
      boa/src/realm.rs

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

@ -16,18 +16,17 @@ use crate::{
builtins::{
object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property,
value::{from_value, to_value, undefined, ResultValue, Value, ValueData},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
};
use gc::Gc;
use std::borrow::Borrow;
use std::cmp::{max, min};
use std::ops::Deref;
/// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
let array = ValueData::new_obj(Some(
let array = Value::new_object(Some(
&interpreter
.get_realm()
.environment
@ -44,7 +43,7 @@ pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
.borrow()
.get_field_slice(PROTOTYPE),
);
array.borrow().set_field_slice("length", to_value(0));
array.borrow().set_field_slice("length", Value::from(0));
Ok(array)
}
@ -56,20 +55,19 @@ pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultVal
let array_obj_ptr = array_obj.clone();
// Wipe existing contents of the array object
let orig_length: i32 =
from_value(array_obj.get_field_slice("length")).expect("failed to convert length to i32");
let orig_length = i32::from(&array_obj.get_field_slice("length"));
for n in 0..orig_length {
array_obj_ptr.remove_prop(&n.to_string());
array_obj_ptr.remove_property(&n.to_string());
}
// Create length
let length = Property::new()
.value(to_value(array_contents.len() as i32))
.value(Value::from(array_contents.len() as i32))
.writable(true)
.configurable(false)
.enumerable(false);
array_obj_ptr.set_prop("length".to_string(), length);
array_obj_ptr.set_property("length".to_string(), length);
for (n, value) in array_contents.iter().enumerate() {
array_obj_ptr.set_field_slice(&n.to_string(), value.clone());
@ -80,8 +78,7 @@ pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultVal
/// Utility function which takes an existing array object and puts additional
/// values on the end, correctly rewriting the length
pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> ResultValue {
let orig_length: i32 =
from_value(array_ptr.get_field_slice("length")).expect("failed to conveert lenth to i32");
let orig_length = i32::from(&array_ptr.get_field_slice("length"));
for (n, value) in add_values.iter().enumerate() {
let new_index = orig_length.wrapping_add(n as i32);
@ -90,7 +87,7 @@ pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> Re
array_ptr.set_field_slice(
"length",
to_value(orig_length.wrapping_add(add_values.len() as i32)),
Value::from(orig_length.wrapping_add(add_values.len() as i32)),
);
Ok(array_ptr.clone())
@ -117,10 +114,10 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re
let mut length = args.len() as i32;
match args.len() {
1 if args[0].is_integer() => {
length = from_value::<i32>(args[0].clone()).expect("Could not convert argument to i32");
length = i32::from(&args[0]);
// TODO: It should not create an array of undefineds, but an empty array ("holy" array in V8) with length `n`.
for n in 0..length {
this.set_field_slice(&n.to_string(), Gc::new(ValueData::Undefined));
this.set_field_slice(&n.to_string(), Value::undefined());
}
}
1 if args[0].is_double() => {
@ -136,12 +133,12 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re
// finally create length property
let length = Property::new()
.value(to_value(length))
.value(Value::from(length))
.writable(true)
.configurable(false)
.enumerable(false);
this.set_prop("length".to_string(), length);
this.set_property("length".to_string(), length);
Ok(this.clone())
}
@ -158,12 +155,12 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re
/// [spec]: https://tc39.es/ecma262/#sec-array.isarray
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
pub fn is_array(_this: &mut Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue {
let value_true = Gc::new(ValueData::Boolean(true));
let value_false = Gc::new(ValueData::Boolean(false));
let value_true = Value::boolean(true);
let value_false = Value::boolean(false);
match args.get(0) {
Some(arg) => {
match *(*arg).clone() {
match arg.data() {
// 1.
ValueData::Object(ref obj) => {
// 2.
@ -202,15 +199,13 @@ pub fn concat(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa
// one)
let mut new_values: Vec<Value> = Vec::new();
let this_length: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let this_length = i32::from(&this.get_field_slice("length"));
for n in 0..this_length {
new_values.push(this.get_field_slice(&n.to_string()));
}
for concat_array in args {
let concat_length: i32 = from_value(concat_array.get_field_slice("length"))
.expect("Could not convert argument to i32");
let concat_length = i32::from(&concat_array.get_field_slice("length"));
for n in 0..concat_length {
new_values.push(concat_array.get_field_slice(&n.to_string()));
}
@ -247,15 +242,14 @@ pub fn push(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// [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 fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let curr_length: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let curr_length = i32::from(&this.get_field_slice("length"));
if curr_length < 1 {
return Ok(Gc::new(ValueData::Undefined));
return Ok(Value::undefined());
}
let pop_index = curr_length.wrapping_sub(1);
let pop_value: Value = this.get_field_slice(&pop_index.to_string());
this.remove_prop(&pop_index.to_string());
this.set_field_slice("length", to_value(pop_index));
this.remove_property(&pop_index.to_string());
this.set_field_slice("length", Value::from(pop_index));
Ok(pop_value)
}
@ -271,25 +265,22 @@ pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
pub fn for_each(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"Missing argument for Array.prototype.forEach".to_string(),
));
return Err(Value::from("Missing argument for Array.prototype.forEach"));
}
let callback_arg = args.get(0).expect("Could not get `callbackFn` argument.");
let mut this_arg = args.get(1).cloned().unwrap_or_else(undefined);
let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
let length = i32::from(&this.get_field_slice("length"));
for i in 0..length {
let element = this.get_field_slice(&i.to_string());
let arguments = [element, to_value(i), this.clone()];
let arguments = [element, Value::from(i), this.clone()];
interpreter.call(callback_arg, &mut this_arg, &arguments)?;
}
Ok(Gc::new(ValueData::Undefined))
Ok(Value::undefined())
}
/// `Array.prototype.join( separator )`
@ -312,14 +303,13 @@ pub fn join(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
};
let mut elem_strs: Vec<String> = Vec::new();
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let length = i32::from(&this.get_field_slice("length"));
for n in 0..length {
let elem_str: String = this.get_field_slice(&n.to_string()).to_string();
elem_strs.push(elem_str);
}
Ok(to_value(elem_strs.join(&separator)))
Ok(Value::from(elem_strs.join(&separator)))
}
/// `Array.prototype.toString( separator )`
@ -336,10 +326,9 @@ pub fn join(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString
pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let method_name = "join";
let mut arguments = vec![to_value(",")];
let mut arguments = vec![Value::from(",")];
// 2.
let mut method: Value =
from_value(this.get_field_slice(method_name)).expect("failed to get Array.prototype.join");
let mut method = this.get_field_slice(method_name);
// 3.
if !method.is_function() {
method = _ctx
@ -349,7 +338,6 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R
.get_field_slice(PROTOTYPE)
.get_field_slice("toString");
method = from_value(method).expect("failed to get Object.prototype.toString");
arguments = Vec::new();
}
// 4.
@ -361,7 +349,7 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R
},
Err(v) => format!("error: {}", v),
};
Ok(to_value(match_string))
Ok(Value::from(match_string))
}
/// `Array.prototype.reverse()`
@ -377,8 +365,7 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
#[allow(clippy::else_if_without_else)]
pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let len = i32::from(&this.get_field_slice("length"));
let middle: i32 = len.wrapping_div(2);
for lower in 0..middle {
@ -395,10 +382,10 @@ pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu
this.set_field_slice(&lower.to_string(), upper_value);
} else if upper_exists {
this.set_field_slice(&lower.to_string(), upper_value);
this.remove_prop(&upper.to_string());
this.remove_property(&upper.to_string());
} else if lower_exists {
this.set_field_slice(&upper.to_string(), lower_value);
this.remove_prop(&lower.to_string());
this.remove_property(&lower.to_string());
}
}
@ -416,11 +403,10 @@ pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu
/// [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 fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let len = i32::from(&this.get_field_slice("length"));
if len == 0 {
this.set_field_slice("length", to_value(0_i32));
this.set_field_slice("length", Value::from(0));
// Since length is 0, this will be an Undefined value
return Ok(this.get_field_slice(&0.to_string()));
}
@ -432,16 +418,16 @@ pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
let to = (k.wrapping_sub(1)).to_string();
let from_value = this.get_field_slice(&from);
if from_value == Gc::new(ValueData::Undefined) {
this.remove_prop(&to);
if from_value == Value::undefined() {
this.remove_property(&to);
} else {
this.set_field_slice(&to, from_value);
}
}
let final_index = len.wrapping_sub(1);
this.remove_prop(&(final_index).to_string());
this.set_field_slice("length", to_value(final_index));
this.remove_property(&(final_index).to_string());
this.set_field_slice("length", Value::from(final_index));
Ok(first)
}
@ -459,8 +445,7 @@ pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [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 fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let len = i32::from(&this.get_field_slice("length"));
let arg_c: i32 = args.len() as i32;
if arg_c > 0 {
@ -469,8 +454,8 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV
let to = (k.wrapping_add(arg_c).wrapping_sub(1)).to_string();
let from_value = this.get_field_slice(&from);
if from_value == Gc::new(ValueData::Undefined) {
this.remove_prop(&to);
if from_value == Value::undefined() {
this.remove_property(&to);
} else {
this.set_field_slice(&to, from_value);
}
@ -486,8 +471,8 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV
}
let temp = len.wrapping_add(arg_c);
this.set_field_slice("length", to_value(temp));
Ok(to_value(temp))
this.set_field_slice("length", Value::from(temp));
Ok(Value::from(temp))
}
/// `Array.prototype.every( callback, [ thisArg ] )`
@ -505,32 +490,32 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every
pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing callback when calling function Array.prototype.every".to_string(),
return Err(Value::from(
"missing callback when calling function Array.prototype.every",
));
}
let callback = &args[0];
let mut this_arg = if args.len() > 1 {
args[1].clone()
} else {
Gc::new(ValueData::Undefined)
Value::undefined()
};
let mut i = 0;
let max_len: i32 = from_value(this.get_field_slice("length")).unwrap();
let max_len = i32::from(&this.get_field_slice("length"));
let mut len = max_len;
while i < len {
let element = this.get_field_slice(&i.to_string());
let arguments = [element, to_value(i), this.clone()];
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter
.call(callback, &mut this_arg, &arguments)?
.is_true();
if !result {
return Ok(to_value(false));
return Ok(Value::from(false));
}
len = min(max_len, from_value(this.get_field_slice("length")).unwrap());
len = min(max_len, i32::from(&this.get_field_slice("length")));
i += 1;
}
Ok(to_value(true))
Ok(Value::from(true))
}
/// `Array.prototype.map( callback, [ thisArg ] )`
@ -546,29 +531,28 @@ pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map
pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
return Err(Value::from(
"missing argument 0 when calling function Array.prototype.map",
));
}
let callback = args.get(0).cloned().unwrap_or_else(undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(undefined);
let callback = args.get(0).cloned().unwrap_or_else(Value::undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
let length = i32::from(&this.get_field_slice("length"));
let new = new_array(&interpreter)?;
let values = (0..length)
let values: Vec<Value> = (0..length)
.map(|idx| {
let element = this.get_field_slice(&idx.to_string());
let args = [element, to_value(idx), new.clone()];
let args = [element, Value::from(idx), new.clone()];
interpreter
.call(&callback, &mut this_val, &args)
.unwrap_or_else(|_| undefined())
.unwrap_or_else(|_| Value::undefined())
})
.collect::<Vec<Value>>();
.collect();
construct_array(&new, &values)
}
@ -595,17 +579,15 @@ pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> R
pub fn index_of(this: &mut 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(to_value(-1));
return Ok(Value::from(-1));
}
let search_element = args[0].clone();
let len: i32 = from_value(this.get_field_slice("length"))
.expect("Expected array property \"length\" is not set.");
let len = i32::from(&this.get_field_slice("length"));
let mut idx = match args.get(1) {
Some(from_idx_ptr) => {
let from_idx = from_value(from_idx_ptr.clone())
.expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument");
let from_idx = i32::from(from_idx_ptr);
if from_idx < 0 {
len + from_idx
@ -620,13 +602,13 @@ pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result
let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element {
return Ok(to_value(idx));
return Ok(Value::from(idx));
}
idx += 1;
}
Ok(to_value(-1))
Ok(Value::from(-1))
}
/// `Array.prototype.lastIndexOf( searchElement[, fromIndex ] )`
@ -650,17 +632,15 @@ pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result
pub fn last_index_of(this: &mut 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(to_value(-1));
return Ok(Value::from(-1));
}
let search_element = args[0].clone();
let len: i32 = from_value(this.get_field_slice("length"))
.expect("Expected array property \"length\" is not set.");
let len = i32::from(&this.get_field_slice("length"));
let mut idx = match args.get(1) {
Some(from_idx_ptr) => {
let from_idx = from_value(from_idx_ptr.clone())
.expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument");
let from_idx = i32::from(from_idx_ptr);
if from_idx >= 0 {
min(from_idx, len - 1)
@ -675,13 +655,13 @@ pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> R
let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element {
return Ok(to_value(idx));
return Ok(Value::from(idx));
}
idx -= 1;
}
Ok(to_value(-1))
Ok(Value::from(-1))
}
/// `Array.prototype.find( callback, [thisArg] )`
@ -698,26 +678,26 @@ pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> R
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing callback when calling function Array.prototype.find".to_string(),
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 {
Gc::new(ValueData::Undefined)
Value::undefined()
};
let len: i32 = from_value(this.get_field_slice("length")).unwrap();
let len = i32::from(&this.get_field_slice("length"));
for i in 0..len {
let element = this.get_field_slice(&i.to_string());
let arguments = [element.clone(), to_value(i), this.clone()];
let arguments = [element.clone(), Value::from(i), this.clone()];
let result = interpreter.call(callback, &mut this_arg, &arguments)?;
if result.is_true() {
return Ok(element);
}
}
Ok(Gc::new(ValueData::Undefined))
Ok(Value::undefined())
}
/// `Array.prototype.findIndex( predicate [ , thisArg ] )`
@ -734,33 +714,29 @@ pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex
pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"Missing argument for Array.prototype.findIndex".to_string(),
return Err(Value::from(
"Missing argument for Array.prototype.findIndex",
));
}
let predicate_arg = args.get(0).expect("Could not get `predicate` argument.");
let mut this_arg = args
.get(1)
.cloned()
.unwrap_or_else(|| Gc::new(ValueData::Undefined));
let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
let length = i32::from(&this.get_field_slice("length"));
for i in 0..length {
let element = this.get_field_slice(&i.to_string());
let arguments = [element, to_value(i), this.clone()];
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter.call(predicate_arg, &mut this_arg, &arguments)?;
if result.is_true() {
return Ok(Gc::new(ValueData::Rational(f64::from(i))));
return Ok(Value::rational(f64::from(i)));
}
}
Ok(Gc::new(ValueData::Rational(f64::from(-1))))
Ok(Value::rational(-1_f64))
}
/// `Array.prototype.fill( value[, start[, end]] )`
@ -775,8 +751,8 @@ pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interprete
/// [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 fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument");
let default_value = undefined();
let len: i32 = i32::from(&this.get_field_slice("length"));
let default_value = Value::undefined();
let value = args.get(0).unwrap_or(&default_value);
let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32;
let relative_end_val = args.get(2).unwrap_or(&default_value);
@ -814,23 +790,19 @@ pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// [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 fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let search_element = args
.get(0)
.cloned()
.unwrap_or_else(|| Gc::new(ValueData::Undefined));
let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
let length = i32::from(&this.get_field_slice("length"));
for idx in 0..length {
let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element {
return Ok(to_value(true));
return Ok(Value::from(true));
}
}
Ok(to_value(false))
Ok(Value::from(false))
}
/// `Array.prototype.slice( [begin[, end]] )`
@ -849,15 +821,14 @@ pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) ->
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice
pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
let new_array = new_array(interpreter)?;
let len: i32 =
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let len = i32::from(&this.get_field_slice("length"));
let start = match args.get(0) {
Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"),
Some(v) => i32::from(v),
None => 0,
};
let end = match args.get(1) {
Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"),
Some(v) => i32::from(v),
None => len,
};
@ -881,7 +852,7 @@ pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
);
new_array_len = new_array_len.wrapping_add(1);
}
new_array.set_field_slice("length", to_value(new_array_len));
new_array.set_field_slice("length", Value::from(new_array_len));
Ok(new_array)
}
@ -898,16 +869,15 @@ pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) ->
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
return Err(Value::from(
"missing argument 0 when calling function Array.prototype.filter",
));
}
let callback = args.get(0).cloned().unwrap_or_else(undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(undefined);
let callback = args.get(0).cloned().unwrap_or_else(Value::undefined);
let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length: i32 =
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
let length = i32::from(&this.get_field_slice("length"));
let new = new_array(&interpreter)?;
@ -915,11 +885,11 @@ pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -
.filter_map(|idx| {
let element = this.get_field_slice(&idx.to_string());
let args = [element.clone(), to_value(idx), new.clone()];
let args = [element.clone(), Value::from(idx), new.clone()];
let callback_result = interpreter
.call(&callback, &mut this_val, &args)
.unwrap_or_else(|_| undefined());
.unwrap_or_else(|_| Value::undefined());
if callback_result.is_true() {
Some(element)
@ -949,42 +919,42 @@ pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some
pub fn some(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(to_value(
"missing callback when calling function Array.prototype.some".to_string(),
return Err(Value::from(
"missing callback when calling function Array.prototype.some",
));
}
let callback = &args[0];
let mut this_arg = if args.len() > 1 {
args[1].clone()
} else {
Gc::new(ValueData::Undefined)
Value::undefined()
};
let mut i = 0;
let max_len: i32 = from_value(this.get_field_slice("length")).unwrap();
let max_len = i32::from(&this.get_field_slice("length"));
let mut len = max_len;
while i < len {
let element = this.get_field_slice(&i.to_string());
let arguments = [element, to_value(i), this.clone()];
let arguments = [element, Value::from(i), this.clone()];
let result = interpreter
.call(callback, &mut this_arg, &arguments)?
.is_true();
if result {
return Ok(to_value(true));
return Ok(Value::from(true));
}
// the length of the array must be updated because the callback can mutate it.
len = min(max_len, from_value(this.get_field_slice("length")).unwrap());
len = min(max_len, i32::from(&this.get_field_slice("length")));
i += 1;
}
Ok(to_value(false))
Ok(Value::from(false))
}
/// Create a new `Array` object.
pub fn create(global: &Value) -> Value {
// Create prototype
let prototype = ValueData::new_obj(None);
let length = Property::default().value(to_value(0_i32));
let prototype = Value::new_object(None);
let length = Property::default().value(Value::from(0));
prototype.set_prop_slice("length", length);
prototype.set_property_slice("length", length);
make_builtin_fn!(concat, named "concat", with length 1, of prototype);
make_builtin_fn!(push, named "push", with length 1, of prototype);

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

@ -15,7 +15,7 @@ mod tests;
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
};
@ -29,7 +29,7 @@ pub fn construct_boolean(this: &mut Value, args: &[Value], _: &mut Interpreter)
if let Some(ref value) = args.get(0) {
this.set_internal_slot("BooleanData", to_boolean(value));
} else {
this.set_internal_slot("BooleanData", to_boolean(&to_value(false)));
this.set_internal_slot("BooleanData", to_boolean(&Value::from(false)));
}
// no need to return `this` as its passed by reference
@ -41,7 +41,7 @@ pub fn call_boolean(_: &mut Value, args: &[Value], _: &mut Interpreter) -> Resul
// Get the argument, if any
match args.get(0) {
Some(ref value) => Ok(to_boolean(value)),
None => Ok(to_boolean(&to_value(false))),
None => Ok(to_boolean(&Value::from(false))),
}
}
@ -55,7 +55,7 @@ pub fn call_boolean(_: &mut Value, args: &[Value], _: &mut Interpreter) -> Resul
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let b = this_boolean_value(this);
Ok(to_value(b.to_string()))
Ok(Value::from(b.to_string()))
}
/// The valueOf() method returns the primitive value of a `Boolean` object.
@ -75,12 +75,12 @@ pub fn value_of(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVal
/// Creates a new boolean value from the input
pub fn to_boolean(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Object(_) => to_value(true),
ValueData::String(ref s) if !s.is_empty() => to_value(true),
ValueData::Rational(n) if n != 0.0 && !n.is_nan() => to_value(true),
ValueData::Integer(n) if n != 0 => to_value(true),
ValueData::Boolean(v) => to_value(v),
_ => to_value(false),
ValueData::Object(_) => Value::from(true),
ValueData::String(ref s) if !s.is_empty() => Value::from(true),
ValueData::Rational(n) if n != 0.0 && !n.is_nan() => Value::from(true),
ValueData::Integer(n) if n != 0 => Value::from(true),
ValueData::Boolean(v) => Value::from(v),
_ => Value::from(false),
}
}
@ -92,9 +92,9 @@ pub fn to_boolean(value: &Value) -> Value {
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
pub fn this_boolean_value(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Boolean(v) => to_value(v),
ValueData::Boolean(v) => Value::from(v),
ValueData::Object(ref v) => (v).deref().borrow().get_internal_slot("BooleanData"),
_ => to_value(false),
_ => Value::from(false),
}
}
@ -102,8 +102,8 @@ pub fn this_boolean_value(value: &Value) -> Value {
pub fn create(global: &Value) -> Value {
// Create Prototype
// https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object
let prototype = ValueData::new_obj(Some(global));
prototype.set_internal_slot("BooleanData", to_boolean(&to_value(false)));
let prototype = Value::new_object(Some(global));
prototype.set_internal_slot("BooleanData", to_boolean(&Value::from(false)));
make_builtin_fn!(to_string, named "toString", of prototype);
make_builtin_fn!(value_of, named "valueOf", of prototype);

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

@ -5,7 +5,7 @@ use crate::{builtins::value::same_value, forward, forward_val};
#[test]
fn check_boolean_constructor_is_function() {
let global = ValueData::new_obj(None);
let global = Value::new_object(None);
let boolean_constructor = create(&global);
assert_eq!(boolean_constructor.is_function(), true);
}

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

@ -19,9 +19,7 @@ mod tests;
use crate::{
builtins::{
object::InternalState,
value::{
display_obj, from_value, to_value, undefined, FromValue, ResultValue, Value, ValueData,
},
value::{display_obj, ResultValue, Value},
},
exec::Interpreter,
};
@ -48,10 +46,11 @@ pub enum LogMessage {
}
/// Helper function that returns the argument at a specified index.
fn get_arg_at_index<T: FromValue + Default>(args: &[Value], index: usize) -> Option<T> {
args.get(index)
.cloned()
.map(|s| from_value::<T>(s).expect("Convert error"))
fn get_arg_at_index<'a, T>(args: &'a [Value], index: usize) -> Option<T>
where
T: From<&'a Value> + Default,
{
args.get(index).map(|s| T::from(s))
}
/// Helper function for logging messages.
@ -147,12 +146,12 @@ pub fn assert(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa
let mut args: Vec<Value> = args.iter().skip(1).cloned().collect();
let message = "Assertion failed".to_string();
if args.is_empty() {
args.push(to_value::<String>(message));
args.push(Value::from(message));
} else if !args[0].is_string() {
args.insert(0, to_value::<String>(message));
args.insert(0, Value::from(message));
} else {
let concat = format!("{}: {}", message, args[0]);
args[0] = to_value::<String>(concat);
args[0] = Value::from(concat);
}
this.with_internal_state_ref(|state| {
@ -160,7 +159,7 @@ pub fn assert(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa
});
}
Ok(undefined())
Ok(Value::undefined())
}
/// `console.clear()`
@ -178,7 +177,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
state.groups.clear();
});
Ok(undefined())
Ok(Value::undefined())
}
/// `console.debug(...data)`
@ -193,7 +192,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug
pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(undefined())
Ok(Value::undefined())
}
/// `console.error(...data)`
@ -208,7 +207,7 @@ pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error
pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state));
Ok(undefined())
Ok(Value::undefined())
}
/// `console.info(...data)`
@ -223,7 +222,7 @@ pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info
pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state));
Ok(undefined())
Ok(Value::undefined())
}
/// `console.log(...data)`
@ -238,7 +237,7 @@ pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log
pub fn log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(undefined())
Ok(Value::undefined())
}
/// `console.trace(...data)`
@ -264,7 +263,7 @@ pub fn trace(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal
});
}
Ok(undefined())
Ok(Value::undefined())
}
/// `console.warn(...data)`
@ -279,7 +278,7 @@ pub fn trace(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn
pub fn warn(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state));
Ok(undefined())
Ok(Value::undefined())
}
/// `console.count(label)`
@ -303,7 +302,7 @@ pub fn count(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal
logger(LogMessage::Info(format!("{} {}", msg, c)), state);
});
Ok(undefined())
Ok(Value::undefined())
}
/// `console.countReset(label)`
@ -325,7 +324,7 @@ pub fn count_reset(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res
logger(LogMessage::Warn(format!("countReset {}", label)), state);
});
Ok(undefined())
Ok(Value::undefined())
}
/// Returns current system time in ms.
@ -361,7 +360,7 @@ pub fn time(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
}
});
Ok(undefined())
Ok(Value::undefined())
}
/// `console.timeLog(label, ...data)`
@ -393,7 +392,7 @@ pub fn time_log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result
}
});
Ok(undefined())
Ok(Value::undefined())
}
/// `console.timeEnd(label)`
@ -424,7 +423,7 @@ pub fn time_end(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result
}
});
Ok(undefined())
Ok(Value::undefined())
}
/// `console.group(...data)`
@ -445,7 +444,7 @@ pub fn group(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal
state.groups.push(group_label);
});
Ok(undefined())
Ok(Value::undefined())
}
/// `console.groupEnd(label)`
@ -463,7 +462,7 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa
state.groups.pop();
});
Ok(undefined())
Ok(Value::undefined())
}
/// `console.dir(item, options)`
@ -478,18 +477,19 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir
pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| {
let undefined = Value::undefined();
logger(
LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined()), true)),
LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined), true)),
state,
);
});
Ok(undefined())
Ok(Value::undefined())
}
/// Create a new `console` object
pub fn create(global: &Value) -> Value {
let console = ValueData::new_obj(Some(global));
let console = Value::new_object(Some(global));
make_builtin_fn!(assert, named "assert", of console);
make_builtin_fn!(clear, named "clear", of console);

32
boa/src/builtins/console/tests.rs

@ -1,5 +1,4 @@
use crate::builtins::{console::formatter, value::ValueData};
use gc::Gc;
use crate::builtins::{console::formatter, value::Value};
#[test]
fn formatter_no_args_is_empty_string() {
@ -8,14 +7,14 @@ fn formatter_no_args_is_empty_string() {
#[test]
fn formatter_empty_format_string_is_empty_string() {
let val = Gc::new(ValueData::String("".to_string()));
let val = Value::string("".to_string());
let res = formatter(&[val]);
assert_eq!(res, "");
}
#[test]
fn formatter_format_without_args_renders_verbatim() {
let val = [Gc::new(ValueData::String("%d %s %% %f".to_string()))];
let val = [Value::string("%d %s %% %f".to_string())];
let res = formatter(&val);
assert_eq!(res, "%d %s %% %f");
}
@ -23,9 +22,9 @@ fn formatter_format_without_args_renders_verbatim() {
#[test]
fn formatter_empty_format_string_concatenates_rest_of_args() {
let val = [
Gc::new(ValueData::String("".to_string())),
Gc::new(ValueData::String("to powinno zostać".to_string())),
Gc::new(ValueData::String("połączone".to_string())),
Value::string("".to_string()),
Value::string("to powinno zostać".to_string()),
Value::string("połączone".to_string()),
];
let res = formatter(&val);
assert_eq!(res, " to powinno zostać połączone");
@ -34,12 +33,10 @@ fn formatter_empty_format_string_concatenates_rest_of_args() {
#[test]
fn formatter_utf_8_checks() {
let val = [
Gc::new(ValueData::String(
"Są takie chwile %dą %są tu%sów %привет%ź".to_string(),
)),
Gc::new(ValueData::Integer(123)),
Gc::new(ValueData::Rational(1.23)),
Gc::new(ValueData::String("ł".to_string())),
Value::string("Są takie chwile %dą %są tu%sów %привет%ź".to_string()),
Value::integer(123),
Value::rational(1.23),
Value::string("ł".to_string()),
];
let res = formatter(&val);
assert_eq!(res, "Są takie chwile 123ą 1.23ą tułów %привет%ź");
@ -48,8 +45,8 @@ fn formatter_utf_8_checks() {
#[test]
fn formatter_trailing_format_leader_renders() {
let val = [
Gc::new(ValueData::String("%%%%%".to_string())),
Gc::new(ValueData::String("|".to_string())),
Value::string("%%%%%".to_string()),
Value::string("|".to_string()),
];
let res = formatter(&val);
assert_eq!(res, "%%% |")
@ -58,10 +55,7 @@ fn formatter_trailing_format_leader_renders() {
#[test]
#[allow(clippy::approx_constant)]
fn formatter_float_format_works() {
let val = [
Gc::new(ValueData::String("%f".to_string())),
Gc::new(ValueData::Rational(3.1415)),
];
let val = [Value::string("%f".to_string()), Value::rational(3.1415)];
let res = formatter(&val);
assert_eq!(res, "3.141500")
}

14
boa/src/builtins/error.rs

@ -13,7 +13,7 @@
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
value::{to_value, undefined, ResultValue, Value, ValueData},
value::{ResultValue, Value},
},
exec::Interpreter,
};
@ -23,7 +23,7 @@ pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Resu
if !args.is_empty() {
this.set_field_slice(
"message",
to_value(
Value::from(
args.get(0)
.expect("failed getting error message")
.to_string(),
@ -33,7 +33,7 @@ pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Resu
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_kind(ObjectKind::Error);
Ok(undefined())
Ok(Value::undefined())
}
/// `Error.prototype.toString()`
@ -49,14 +49,14 @@ pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Resu
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let name = this.get_field_slice("name");
let message = this.get_field_slice("message");
Ok(to_value(format!("{}: {}", name, message)))
Ok(Value::from(format!("{}: {}", name, message)))
}
/// Create a new `Error` object.
pub fn create(global: &Value) -> Value {
let prototype = ValueData::new_obj(Some(global));
prototype.set_field_slice("message", to_value(""));
prototype.set_field_slice("name", to_value("Error"));
let prototype = Value::new_object(Some(global));
prototype.set_field_slice("message", Value::from(""));
prototype.set_field_slice("name", Value::from("Error"));
make_builtin_fn!(to_string, named "toString", of prototype);
make_constructor_fn!(make_error, global, prototype)
}

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

@ -16,7 +16,7 @@ use crate::{
array,
object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE},
property::Property,
value::{to_value, undefined, ResultValue, Value, ValueData},
value::{ResultValue, Value},
},
environment::lexical_environment::{new_function_environment, Environment},
exec::Executor,
@ -329,10 +329,10 @@ pub fn create_function_prototype() {
pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
let len = arguments_list.len();
let mut obj = Object::default();
obj.set_internal_slot("ParameterMap", undefined());
obj.set_internal_slot("ParameterMap", Value::undefined());
// Set length
let mut length = Property::default();
length = length.writable(true).value(to_value(len));
length = length.writable(true).value(Value::from(len));
// Define length as a property
obj.define_own_property("length".to_string(), length);
let mut index: usize = 0;
@ -349,7 +349,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
index += 1;
}
to_value(obj)
Value::from(obj)
}
/// Create new function `[[Construct]]`
@ -361,7 +361,7 @@ pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> Resu
}
pub fn create(global: &Value) -> Value {
let prototype = ValueData::new_obj(Some(global));
let prototype = Value::new_object(Some(global));
make_constructor_fn!(make_function, make_function, global, prototype)
}

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

@ -13,7 +13,7 @@
//! [json]: https://www.json.org/json-en.html
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON
use crate::builtins::value::{to_value, ResultValue, Value, ValueData};
use crate::builtins::value::{ResultValue, Value};
use crate::exec::Interpreter;
use serde_json::{self, Value as JSONValue};
@ -41,8 +41,8 @@ pub fn parse(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
.clone()
.to_string(),
) {
Ok(json) => Ok(to_value(json)),
Err(err) => Err(to_value(err.to_string())),
Ok(json) => Ok(Value::from(json)),
Err(err) => Err(Value::from(err.to_string())),
}
}
@ -65,17 +65,17 @@ pub fn parse(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
pub fn stringify(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("cannot get argument for JSON.stringify");
let json = obj.to_json().to_string();
Ok(to_value(json))
Ok(Value::from(json))
}
/// Create a new `JSON` object.
pub fn create(global: &Value) -> Value {
let json = ValueData::new_obj(Some(global));
let json = Value::new_object(Some(global));
make_builtin_fn!(parse, named "parse", with length 2, of json);
make_builtin_fn!(stringify, named "stringify", with length 3, of json);
to_value(json)
json
}
/// Initialise the `JSON` object on the global object.

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

@ -12,7 +12,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
use crate::{
builtins::value::{from_value, to_value, ResultValue, Value, ValueData},
builtins::value::{ResultValue, Value},
exec::Interpreter,
};
use rand::random;
@ -30,12 +30,10 @@ mod tests;
/// [spec]: https://tc39.es/ecma262/#sec-math.abs
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
pub fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.abs()
f64::from(args.get(0).expect("Could not get argument")).abs()
}))
}
@ -48,12 +46,10 @@ pub fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.acos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
pub fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.acos()
f64::from(args.get(0).expect("Could not get argument")).acos()
}))
}
@ -66,12 +62,10 @@ pub fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.acosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
pub fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.acosh()
f64::from(args.get(0).expect("Could not get argument")).acosh()
}))
}
@ -84,12 +78,10 @@ pub fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.asin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
pub fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.asin()
f64::from(args.get(0).expect("Could not get argument")).asin()
}))
}
@ -102,12 +94,10 @@ pub fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.asinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
pub fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.asinh()
f64::from(args.get(0).expect("Could not get argument")).asinh()
}))
}
@ -120,12 +110,10 @@ pub fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.atan
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
pub fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.atan()
f64::from(args.get(0).expect("Could not get argument")).atan()
}))
}
@ -138,12 +126,10 @@ pub fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.atanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
pub fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.atanh()
f64::from(args.get(0).expect("Could not get argument")).atanh()
}))
}
@ -156,11 +142,10 @@ pub fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.atan2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
f64::from(args.get(0).expect("Could not get argument"))
.atan2(args.get(1).expect("Could not get argument").to_number())
}))
}
@ -174,12 +159,10 @@ pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
pub fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.cbrt()
f64::from(args.get(0).expect("Could not get argument")).cbrt()
}))
}
@ -192,12 +175,10 @@ pub fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.ceil
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
pub fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.ceil()
f64::from(args.get(0).expect("Could not get argument")).ceil()
}))
}
@ -210,12 +191,10 @@ pub fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.cos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
pub fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.cos()
f64::from(args.get(0).expect("Could not get argument")).cos()
}))
}
@ -228,12 +207,10 @@ pub fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.cosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
pub fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.cosh()
f64::from(args.get(0).expect("Could not get argument")).cosh()
}))
}
@ -246,12 +223,10 @@ pub fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.exp
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
pub fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.exp()
f64::from(args.get(0).expect("Could not get argument")).exp()
}))
}
@ -264,12 +239,10 @@ pub fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.floor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
pub fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.floor()
f64::from(args.get(0).expect("Could not get argument")).floor()
}))
}
@ -282,11 +255,10 @@ pub fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
pub fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
let value = f64::from(args.get(0).expect("Could not get argument"));
if value <= 0.0 {
f64::NAN
@ -305,11 +277,10 @@ pub fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.log10
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
pub fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
let value = f64::from(args.get(0).expect("Could not get argument"));
if value <= 0.0 {
f64::NAN
@ -328,11 +299,10 @@ pub fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.log2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
pub fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
let value = f64::from(args.get(0).expect("Could not get argument"));
if value <= 0.0 {
f64::NAN
@ -353,10 +323,10 @@ pub fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::NEG_INFINITY;
for arg in args {
let num = arg.to_number();
let num = f64::from(arg);
max = max.max(num);
}
Ok(to_value(max))
Ok(Value::from(max))
}
/// Get the minimum of several numbers.
@ -370,10 +340,10 @@ pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
pub fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::INFINITY;
for arg in args {
let num = arg.to_number();
let num = f64::from(arg);
max = max.min(num);
}
Ok(to_value(max))
Ok(Value::from(max))
}
/// Raise a number to a power.
@ -385,11 +355,9 @@ pub fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.pow
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.len() >= 2 {
let num: f64 = from_value(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
let power: f64 = from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
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"));
num.powf(power)
} else {
f64::NAN
@ -405,7 +373,7 @@ pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.random
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
pub fn _random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(random::<f64>()))
Ok(Value::from(random::<f64>()))
}
/// Round a number to the nearest integer.
@ -417,12 +385,10 @@ pub fn _random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.round
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
pub fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.round()
f64::from(args.get(0).expect("Could not get argument")).round()
}))
}
@ -435,11 +401,10 @@ pub fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.sign
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
pub fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
let value = f64::from(args.get(0).expect("Could not get argument"));
if value == 0.0 || value == -0.0 {
value
@ -458,12 +423,10 @@ pub fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.sin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
pub fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.sin()
f64::from(args.get(0).expect("Could not get argument")).sin()
}))
}
@ -476,12 +439,10 @@ pub fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.sinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
pub fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.sinh()
f64::from(args.get(0).expect("Could not get argument")).sinh()
}))
}
@ -494,22 +455,18 @@ pub fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
pub fn sqrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.sqrt()
f64::from(args.get(0).expect("Could not get argument")).sqrt()
}))
}
/// Get the tangent of a number
pub fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.tan()
f64::from(args.get(0).expect("Could not get argument")).tan()
}))
}
@ -522,12 +479,10 @@ pub fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.tanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
pub fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.tanh()
f64::from(args.get(0).expect("Could not get argument")).tanh()
}))
}
@ -540,27 +495,25 @@ pub fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.trunc
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
pub fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.is_empty() {
Ok(Value::from(if args.is_empty() {
f64::NAN
} else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone())
.expect("Could not convert argument to f64")
.trunc()
f64::from(args.get(0).expect("Could not get argument")).trunc()
}))
}
/// Create a new `Math` object
pub fn create(global: &Value) -> Value {
let math = ValueData::new_obj(Some(global));
math.set_field_slice("E", to_value(f64::consts::E));
math.set_field_slice("LN2", to_value(f64::consts::LN_2));
math.set_field_slice("LN10", to_value(f64::consts::LN_10));
math.set_field_slice("LOG2E", to_value(f64::consts::LOG2_E));
math.set_field_slice("LOG10E", to_value(f64::consts::LOG10_E));
math.set_field_slice("SQRT1_2", to_value(0.5_f64.sqrt()));
math.set_field_slice("SQRT2", to_value(f64::consts::SQRT_2));
math.set_field_slice("PI", to_value(f64::consts::PI));
let math = Value::new_object(Some(global));
math.set_field_slice("E", Value::from(f64::consts::E));
math.set_field_slice("LN2", Value::from(f64::consts::LN_2));
math.set_field_slice("LN10", Value::from(f64::consts::LN_10));
math.set_field_slice("LOG2E", Value::from(f64::consts::LOG2_E));
math.set_field_slice("LOG10E", Value::from(f64::consts::LOG10_E));
math.set_field_slice("SQRT1_2", Value::from(0.5_f64.sqrt()));
math.set_field_slice("SQRT2", Value::from(f64::consts::SQRT_2));
math.set_field_slice("PI", Value::from(f64::consts::PI));
make_builtin_fn!(abs, named "abs", with length 1, of math);
make_builtin_fn!(acos, named "acos", with length 1, of math);
make_builtin_fn!(acosh, named "acosh", with length 1, of math);

8
boa/src/builtins/mod.rs

@ -12,8 +12,8 @@ macro_rules! make_builtin_fn {
let mut new_func = crate::builtins::object::Object::function();
new_func.set_call(func);
let new_func_obj = to_value(new_func);
new_func_obj.set_field_slice("length", to_value($l));
let new_func_obj = Value::from(new_func);
new_func_obj.set_field_slice("length", Value::from($l));
$p.set_field_slice($name, new_func_obj);
};
($fn:ident, named $name:expr, of $p:ident) => {
@ -42,7 +42,7 @@ macro_rules! make_constructor_fn {
constructor_obj.set_construct(constructor_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype);
let constructor_val = to_value(constructor_obj);
let constructor_val = Value::from(constructor_obj);
// Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone());
@ -71,7 +71,7 @@ macro_rules! make_constructor_fn {
constructor_obj.set_construct(construct_fn);
constructor_obj.set_call(call_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype);
let constructor_val = to_value(constructor_obj);
let constructor_val = Value::from(constructor_obj);
// Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone());

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

@ -19,7 +19,7 @@ mod tests;
use crate::{
builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
};
@ -30,19 +30,19 @@ fn to_number(value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Boolean(b) => {
if b {
to_value(1)
Value::from(1)
} else {
to_value(0)
Value::from(0)
}
}
ValueData::Symbol(_) | ValueData::Undefined => to_value(f64::NAN),
ValueData::Integer(i) => to_value(f64::from(i)),
ValueData::Symbol(_) | ValueData::Undefined => Value::from(f64::NAN),
ValueData::Integer(i) => Value::from(f64::from(i)),
ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"),
ValueData::Null => to_value(0),
ValueData::Rational(n) => to_value(n),
ValueData::Null => Value::from(0),
ValueData::Rational(n) => Value::from(n),
ValueData::String(ref s) => match s.parse::<f64>() {
Ok(n) => to_value(n),
Err(_) => to_value(f64::NAN),
Ok(n) => Value::from(n),
Err(_) => Value::from(f64::NAN),
},
}
}
@ -60,7 +60,7 @@ fn num_to_exponential(n: f64) -> String {
pub fn make_number(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => to_number(value),
None => to_number(&to_value(0)),
None => to_number(&Value::from(0)),
};
this.set_internal_slot("NumberData", data);
Ok(this.clone())
@ -72,7 +72,7 @@ pub fn make_number(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) ->
pub fn call_number(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let data = match args.get(0) {
Some(ref value) => to_number(value),
None => to_number(&to_value(0)),
None => to_number(&Value::from(0)),
};
Ok(data)
}
@ -90,7 +90,7 @@ pub fn call_number(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) ->
pub fn to_exponential(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_number();
let this_str_num = num_to_exponential(this_num);
Ok(to_value(this_str_num))
Ok(Value::from(this_str_num))
}
/// `Number.prototype.toFixed( [digits] )`
@ -113,7 +113,7 @@ pub fn to_fixed(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> Res
None => 0,
};
let this_fixed_num = format!("{:.*}", precision, this_num);
Ok(to_value(this_fixed_num))
Ok(Value::from(this_fixed_num))
}
/// `Number.prototype.toLocaleString( [locales [, options]] )`
@ -132,7 +132,7 @@ pub fn to_fixed(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> Res
pub fn to_locale_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let this_num = to_number(this).to_number();
let this_str_num = format!("{}", this_num);
Ok(to_value(this_str_num))
Ok(Value::from(this_str_num))
}
/// `Number.prototype.toPrecision( [precision] )`
@ -170,7 +170,7 @@ pub fn to_precision(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) ->
/// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString
pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
Ok(to_value(format!("{}", to_number(this).to_number())))
Ok(Value::from(format!("{}", to_number(this).to_number())))
}
/// `Number.prototype.toString()`
@ -189,8 +189,8 @@ pub fn value_of(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> Re
/// Create a new `Number` object
pub fn create(global: &Value) -> Value {
let prototype = ValueData::new_obj(Some(global));
prototype.set_internal_slot("NumberData", to_value(0));
let prototype = Value::new_object(Some(global));
prototype.set_internal_slot("NumberData", Value::from(0));
make_builtin_fn!(to_exponential, named "toExponential", with length 1, of prototype);
make_builtin_fn!(to_fixed, named "toFixed", with length 1, of prototype);

4
boa/src/builtins/number/tests.rs

@ -1,10 +1,10 @@
#![allow(clippy::float_cmp)]
use crate::{builtins::value::ValueData, exec::Executor, forward, forward_val, realm::Realm};
use crate::{builtins::value::Value, exec::Executor, forward, forward_val, realm::Realm};
#[test]
fn check_number_constructor_is_function() {
let global = ValueData::new_obj(None);
let global = Value::new_object(None);
let number_constructor = super::create(&global);
assert_eq!(number_constructor.is_function(), true);
}

13
boa/src/builtins/object/internal_methods_trait.rs

@ -8,9 +8,8 @@
use crate::builtins::{
object::{Object, INSTANCE_PROTOTYPE},
property::Property,
value::{same_value, to_value, Value, ValueData},
value::{same_value, Value, ValueData},
};
use gc::Gc;
use std::borrow::Borrow;
use std::ops::Deref;
@ -70,7 +69,7 @@ pub trait ObjectInternalMethods {
///
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions
fn prevent_extensions(&mut self) -> bool {
self.set_internal_slot("extensible", to_value(false));
self.set_internal_slot("extensible", Value::from(false));
true
}
@ -108,7 +107,7 @@ pub trait ObjectInternalMethods {
// parent will either be null or an Object
let parent = self.get_prototype_of();
if parent.is_null() {
return Gc::new(ValueData::Undefined);
return Value::undefined();
}
let parent_obj = Object::from(&parent).expect("Failed to get object");
@ -122,11 +121,11 @@ pub trait ObjectInternalMethods {
let getter = desc.get.clone();
if getter.is_none() || getter.expect("Failed to get object").is_undefined() {
return Gc::new(ValueData::Undefined);
return Value::undefined();
}
// TODO!!!!! Call getter from here
Gc::new(ValueData::Undefined)
Value::undefined()
}
/// [[Set]]
@ -169,7 +168,7 @@ pub trait ObjectInternalMethods {
}
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool {
let mut current = self.get_own_property(&to_value(property_key.to_string()));
let mut current = self.get_own_property(&Value::from(property_key.to_string()));
let extensible = self.is_extensible();
// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor

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

@ -17,11 +17,11 @@ use crate::{
builtins::{
function::Function,
property::Property,
value::{from_value, same_value, to_value, undefined, ResultValue, Value, ValueData},
value::{same_value, ResultValue, Value, ValueData},
},
exec::Interpreter,
};
use gc::{unsafe_empty_trace, Finalize, Gc, GcCell, Trace};
use gc::{unsafe_empty_trace, Finalize, Trace};
use rustc_hash::FxHashMap;
use std::{
borrow::Borrow,
@ -103,7 +103,7 @@ impl ObjectInternalMethods for Object {
while !done {
if p.is_null() {
done = true
} else if same_value(&to_value(self.clone()), &p, false) {
} else if same_value(&Value::from(self.clone()), &p, false) {
return false;
} else {
p = p.get_internal_slot(PROTOTYPE);
@ -132,7 +132,7 @@ impl ObjectInternalMethods for Object {
fn get_internal_slot(&self, name: &str) -> Value {
match self.internal_slots.get(name) {
Some(v) => v.clone(),
None => Gc::new(ValueData::Null),
None => Value::null(),
}
}
@ -208,7 +208,7 @@ impl ObjectInternalMethods for Object {
/// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
#[allow(clippy::option_unwrap_used)]
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool {
let mut current = self.get_own_property(&to_value(property_key.to_string()));
let mut current = self.get_own_property(&Value::from(property_key.to_string()));
let extensible = self.is_extensible();
// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
@ -346,7 +346,7 @@ impl Object {
construct: None,
};
object.set_internal_slot("extensible", to_value(true));
object.set_internal_slot("extensible", Value::from(true));
object
}
@ -362,7 +362,7 @@ impl Object {
construct: None,
};
object.set_internal_slot("extensible", to_value(true));
object.set_internal_slot("extensible", Value::from(true));
object
}
@ -378,7 +378,7 @@ impl Object {
obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
obj.internal_slots
.insert("extensible".to_string(), to_value(true));
.insert("extensible".to_string(), Value::from(true));
obj
}
@ -528,14 +528,12 @@ unsafe impl Trace for ObjectKind {
pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() {
return Ok(Gc::new(ValueData::Object(Box::new(GcCell::new(
Object::from(arg).unwrap(),
)))));
return Ok(Value::object(Object::from(arg).unwrap()));
}
}
let global = &ctx.realm.global_obj;
let object = ValueData::new_obj(Some(global));
let object = Value::new_object(Some(global));
Ok(object)
}
@ -557,12 +555,10 @@ 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], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object");
let prop = from_value::<String>(args.get(1).expect("Cannot get object").clone())
.expect("Cannot get object");
let desc = from_value::<Property>(args.get(2).expect("Cannot get object").clone())
.expect("Cannot get object");
obj.set_prop(prop, desc);
Ok(undefined())
let prop = String::from(args.get(1).expect("Cannot get object"));
let desc = Property::from(args.get(2).expect("Cannot get object"));
obj.set_property(prop, desc);
Ok(Value::undefined())
}
/// `Object.prototype.toString()`
@ -576,7 +572,7 @@ pub fn define_property(_: &mut Value, args: &[Value], _: &mut Interpreter) -> Re
/// [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 {
Ok(to_value(this.to_string()))
Ok(Value::from(this.to_string()))
}
/// `Object.prototype.hasOwnPrototype( property )`
@ -594,23 +590,26 @@ pub fn has_own_property(this: &mut Value, args: &[Value], _: &mut Interpreter) -
let prop = if args.is_empty() {
None
} else {
from_value::<String>(args.get(0).expect("Cannot get object").clone()).ok()
Some(String::from(args.get(0).expect("Cannot get object")))
};
Ok(to_value(
prop.is_some() && this.get_prop(&prop.expect("Cannot get object")).is_some(),
Ok(Value::from(
prop.is_some()
&& this
.get_property(&prop.expect("Cannot get object"))
.is_some(),
))
}
/// Create a new `Object` object.
pub fn create(global: &Value) -> Value {
let prototype = ValueData::new_obj(None);
let prototype = Value::new_object(None);
make_builtin_fn!(has_own_property, named "hasOwnProperty", of prototype);
make_builtin_fn!(to_string, named "toString", of prototype);
let object = make_constructor_fn!(make_object, make_object, global, prototype);
object.set_field_slice("length", to_value(1_i32));
object.set_field_slice("length", Value::from(1));
make_builtin_fn!(set_prototype_of, named "setPrototypeOf", with length 2, of object);
make_builtin_fn!(get_prototype_of, named "getPrototypeOf", with length 1, of object);
make_builtin_fn!(define_property, named "defineProperty", with length 3, of object);

70
boa/src/builtins/property.rs → boa/src/builtins/property/mod.rs

@ -14,7 +14,7 @@
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
//! [section]: https://tc39.es/ecma262/#sec-property-attributes
use crate::builtins::value::{from_value, to_value, FromValue, ToValue, Value, ValueData};
use crate::builtins::value::Value;
use gc::{Finalize, Trace};
/// This represents a Javascript Property AKA The Property Descriptor.
@ -170,59 +170,33 @@ impl Default for Property {
}
}
impl ToValue for Property {
fn to_value(&self) -> Value {
let prop = ValueData::new_obj(None);
prop.set_field_slice("configurable", to_value(self.configurable));
prop.set_field_slice("enumerable", to_value(self.enumerable));
prop.set_field_slice("writable", to_value(self.writable));
prop.set_field_slice("value", to_value(self.value.clone()));
prop.set_field_slice("get", to_value(self.get.clone()));
prop.set_field_slice("set", to_value(self.set.clone()));
prop
impl From<&Property> for Value {
fn from(value: &Property) -> Value {
let property = Value::new_object(None);
property.set_field_slice("configurable", Value::from(value.configurable));
property.set_field_slice("enumerable", Value::from(value.enumerable));
property.set_field_slice("writable", Value::from(value.writable));
property.set_field_slice("value", value.value.clone().unwrap_or_else(Value::null));
property.set_field_slice("get", value.get.clone().unwrap_or_else(Value::null));
property.set_field_slice("set", value.set.clone().unwrap_or_else(Value::null));
property
}
}
impl FromValue for Property {
impl<'a> From<&'a Value> for Property {
/// Attempt to fetch values "configurable", "enumerable", "writable" from the value,
/// if they're not there default to false
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(Self {
configurable: {
match from_value::<bool>(v.get_field_slice("configurable")) {
Ok(v) => Some(v),
Err(_) => Some(false),
}
},
enumerable: {
match from_value::<bool>(v.get_field_slice("enumerable")) {
Ok(v) => Some(v),
Err(_) => Some(false),
}
},
writable: {
match from_value(v.get_field_slice("writable")) {
Ok(v) => Some(v),
Err(_) => Some(false),
}
},
value: Some(v.get_field_slice("value")),
get: Some(v.get_field_slice("get")),
set: Some(v.get_field_slice("set")),
})
fn from(value: &Value) -> Self {
Self {
configurable: { Some(bool::from(&value.get_field_slice("configurable"))) },
enumerable: { Some(bool::from(&value.get_field_slice("enumerable"))) },
writable: { Some(bool::from(&value.get_field_slice("writable"))) },
value: Some(value.get_field_slice("value")),
get: Some(value.get_field_slice("get")),
set: Some(value.get_field_slice("set")),
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn is_property_key_test() {
let v = Value::new(ValueData::String(String::from("Boop")));
assert!(Property::is_property_key(&v));
let v = Value::new(ValueData::Boolean(true));
assert!(!Property::is_property_key(&v));
}
}
mod tests;

10
boa/src/builtins/property/tests.rs

@ -0,0 +1,10 @@
use super::*;
#[test]
fn is_property_key_test() {
let v = Value::string("Boop");
assert!(Property::is_property_key(&v));
let v = Value::boolean(true);
assert!(!Property::is_property_key(&v));
}

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

@ -11,14 +11,13 @@
use std::ops::Deref;
use gc::Gc;
use regex::Regex;
use crate::{
builtins::{
object::{InternalState, Object, ObjectInternalMethods, ObjectKind, PROTOTYPE},
property::Property,
value::{from_value, to_value, undefined, FromValue, ResultValue, Value, ValueData},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
};
@ -59,18 +58,10 @@ struct RegExp {
impl InternalState for RegExp {}
/// Helper function for getting an argument.
fn get_argument<T: FromValue>(args: &[Value], idx: usize) -> Result<T, Value> {
match args.get(idx) {
Some(arg) => from_value(arg.clone()).map_err(to_value),
None => Err(to_value(format!("expected argument at index {}", idx))),
}
}
/// Create a new `RegExp`
pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
if args.is_empty() {
return Err(undefined());
return Err(Value::undefined());
}
let mut regex_body = String::new();
let mut regex_flags = String::new();
@ -85,16 +76,14 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res
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 =
from_value(body.clone()).expect("Could not convert value to String");
regex_body = String::from(body);
}
if let Some(flags) = slots.get("OriginalFlags") {
regex_flags =
from_value(flags.clone()).expect("Could not convert value to String");
regex_flags = String::from(flags);
}
}
}
_ => return Err(undefined()),
_ => return Err(Value::undefined()),
}
// if a second argument is given and it's a string, use it as flags
match args.get(1) {
@ -165,9 +154,9 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res
// This value is used by console.log and other routines to match Object type
// to its Javascript Identifier (global constructor method name)
this.set_kind(ObjectKind::Ordinary);
this.set_internal_slot("RegExpMatcher", undefined());
this.set_internal_slot("OriginalSource", to_value(regex_body));
this.set_internal_slot("OriginalFlags", to_value(regex_flags));
this.set_internal_slot("RegExpMatcher", Value::undefined());
this.set_internal_slot("OriginalSource", Value::from(regex_body));
this.set_internal_slot("OriginalFlags", Value::from(regex_flags));
this.set_internal_state(regexp);
Ok(this.clone())
@ -184,7 +173,7 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res
/// [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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.dot_all)))
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all)))
}
/// `RegExp.prototype.flags`
@ -199,7 +188,7 @@ fn get_dot_all(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu
/// [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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.flags.clone())))
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone())))
}
/// `RegExp.prototype.global`
@ -213,7 +202,7 @@ fn get_flags(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.global)))
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global)))
}
/// `RegExp.prototype.ignoreCase`
@ -227,7 +216,7 @@ fn get_global(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.ignore_case)))
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case)))
}
/// `RegExp.prototype.multiline`
@ -241,7 +230,7 @@ fn get_ignore_case(this: &mut Value, _: &[Value], _: &mut Interpreter) -> Result
/// [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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.multiline)))
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline)))
}
/// `RegExp.prototype.source`
@ -270,7 +259,7 @@ fn get_source(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.sticky)))
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky)))
}
/// `RegExp.prototype.unicode`
@ -285,7 +274,7 @@ fn get_sticky(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
/// [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 {
this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.unicode)))
this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode)))
}
/// `RegExp.prototype.test( string )`
@ -301,9 +290,8 @@ fn get_unicode(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu
/// [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 fn test(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?;
let mut last_index =
from_value::<usize>(this.get_field_slice("lastIndex")).map_err(to_value)?;
let arg_str = String::from(args.get(0).expect("could not get argument"));
let mut last_index = usize::from(&this.get_field_slice("lastIndex"));
let result = this.with_internal_state_ref(|regex: &RegExp| {
let result = if let Some(m) = regex.matcher.find_at(arg_str.as_str(), last_index) {
if regex.use_last_index {
@ -316,9 +304,9 @@ pub fn test(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
}
false
};
Ok(Gc::new(ValueData::Boolean(result)))
Ok(Value::boolean(result))
});
this.set_field_slice("lastIndex", to_value(last_index));
this.set_field_slice("lastIndex", Value::from(last_index));
result
}
@ -335,9 +323,8 @@ pub fn test(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
/// [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 fn exec(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg_str = get_argument::<String>(args, 0)?;
let mut last_index =
from_value::<usize>(this.get_field_slice("lastIndex")).map_err(to_value)?;
let arg_str = String::from(args.get(0).expect("could not get argument"));
let mut last_index = usize::from(&this.get_field_slice("lastIndex"));
let result = this.with_internal_state_ref(|regex: &RegExp| {
let mut locations = regex.matcher.capture_locations();
let result = if let Some(m) =
@ -351,26 +338,27 @@ pub fn exec(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu
let mut result = Vec::with_capacity(locations.len());
for i in 0..locations.len() {
if let Some((start, end)) = locations.get(i) {
result.push(to_value(
result.push(Value::from(
arg_str.get(start..end).expect("Could not get slice"),
));
} else {
result.push(undefined());
result.push(Value::undefined());
}
}
let result = to_value(result);
result.set_prop_slice("index", Property::default().value(to_value(m.start())));
result.set_prop_slice("input", Property::default().value(to_value(arg_str)));
let result = Value::from(result);
result.set_property_slice("index", Property::default().value(Value::from(m.start())));
result.set_property_slice("input", Property::default().value(Value::from(arg_str)));
result
} else {
if regex.use_last_index {
last_index = 0;
}
Gc::new(ValueData::Null)
Value::null()
};
Ok(result)
});
this.set_field_slice("lastIndex", to_value(last_index));
this.set_field_slice("lastIndex", Value::from(last_index));
result
}
@ -390,14 +378,14 @@ pub fn r#match(this: &mut Value, arg: String, ctx: &mut Interpreter) -> ResultVa
if flags.contains('g') {
let mut matches = Vec::new();
for mat in matcher.find_iter(&arg) {
matches.push(to_value(mat.as_str()));
matches.push(Value::from(mat.as_str()));
}
if matches.is_empty() {
return Ok(Gc::new(ValueData::Null));
return Ok(Value::null());
}
Ok(to_value(matches))
Ok(Value::from(matches))
} else {
exec(this, &[to_value(arg)], ctx)
exec(this, &[Value::from(arg)], ctx)
}
}
@ -412,9 +400,9 @@ pub fn r#match(this: &mut Value, arg: String, ctx: &mut Interpreter) -> ResultVa
/// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let body = from_value::<String>(this.get_internal_slot("OriginalSource")).map_err(to_value)?;
let body = String::from(&this.get_internal_slot("OriginalSource"));
let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone());
Ok(to_value(format!("/{}/{}", body, flags)))
Ok(Value::from(format!("/{}/{}", body, flags)))
}
/// `RegExp.prototype[ @@matchAll ]( string )`
@ -437,17 +425,18 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue {
let match_vec = caps
.iter()
.map(|group| match group {
Some(g) => to_value(g.as_str()),
None => undefined(),
Some(g) => Value::from(g.as_str()),
None => Value::undefined(),
})
.collect::<Vec<Value>>();
let match_val = to_value(match_vec);
let match_val = Value::from(match_vec);
match_val.set_prop_slice("index", Property::default().value(to_value(m.start())));
match_val.set_prop_slice(
match_val
.set_property_slice("index", Property::default().value(Value::from(m.start())));
match_val.set_property_slice(
"input",
Property::default().value(to_value(arg_str.clone())),
Property::default().value(Value::from(arg_str.clone())),
);
matches.push(match_val);
@ -461,8 +450,8 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue {
});
let length = matches.len();
let result = to_value(matches);
result.set_field_slice("length", to_value(length));
let result = Value::from(matches);
result.set_field_slice("length", Value::from(length));
result.set_kind(ObjectKind::Array);
Ok(result)
@ -471,8 +460,8 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue {
/// Create a new `RegExp` object.
pub fn create(global: &Value) -> Value {
// Create prototype
let prototype = ValueData::new_obj(Some(global));
prototype.set_field_slice("lastIndex", to_value(0));
let prototype = Value::new_object(Some(global));
prototype.set_field_slice("lastIndex", Value::from(0));
make_builtin_fn!(test, named "test", with length 1, of prototype);
make_builtin_fn!(exec, named "exec", with length 1, of prototype);

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

@ -21,7 +21,7 @@ fn constructors() {
#[test]
fn check_regexp_constructor_is_function() {
let global = ValueData::new_obj(None);
let global = Value::new_object(None);
let regexp_constructor = create(&global);
assert_eq!(regexp_constructor.is_function(), true);
}

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

@ -17,11 +17,10 @@ use crate::{
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
property::Property,
regexp::{make_regexp, match_all as regexp_match_all, r#match as regexp_match},
value::{from_value, to_value, undefined, ResultValue, Value, ValueData},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
};
use gc::Gc;
use regex::Regex;
use std::{
cmp::{max, min},
@ -55,21 +54,21 @@ pub fn make_string(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res
pub fn call_string(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg = match args.get(0) {
Some(v) => v.clone(),
None => undefined(),
None => Value::undefined(),
};
if arg.is_undefined() {
return Ok(to_value(""));
return Ok(Value::from(String::new()));
}
Ok(to_value(arg.to_string()))
Ok(Value::from(arg.to_string()))
}
/// Get the string value to a primitive string
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
// Get String from String Object and send it back as a new value
let primitive_val = this.get_internal_slot("StringData");
Ok(to_value(format!("{}", primitive_val)))
Ok(Value::from(format!("{}", primitive_val)))
}
/// `String.prototype.charAt( index )`
@ -92,12 +91,10 @@ pub fn char_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
// 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.value_to_rust_string(this);
let pos: i32 = from_value(
let pos = i32::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
// Calling .len() on a string would give the wrong result, as they are bytes not the number of
// unicode code points
@ -107,10 +104,10 @@ pub fn char_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
// We should return an empty string is pos is out of range
if pos >= length as i32 || pos < 0 {
return Ok(to_value::<String>(String::new()));
return Ok(Value::from(String::new()));
}
Ok(to_value::<char>(
Ok(Value::from(
primitive_val
.chars()
.nth(pos as usize)
@ -140,15 +137,13 @@ pub fn char_code_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) ->
// Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points
// Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation.
let length = primitive_val.chars().count();
let pos: i32 = from_value(
let pos = i32::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
if pos >= length as i32 || pos < 0 {
return Ok(to_value(NAN));
return Ok(Value::from(NAN));
}
let utf16_val = primitive_val
@ -157,7 +152,7 @@ pub fn char_code_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) ->
.expect("failed to get utf16 value");
// If there is no element at that index, the result is NaN
// TODO: We currently don't have NaN
Ok(to_value(f64::from(utf16_val)))
Ok(Value::from(f64::from(utf16_val)))
}
/// `String.prototype.concat( str1[, ...strN] )`
@ -180,11 +175,11 @@ pub fn concat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
let mut new_str = ctx.value_to_rust_string(this);
for arg in args {
let concat_str: String = from_value(arg.clone()).expect("failed to get argument value");
let concat_str = String::from(arg);
new_str.push_str(&concat_str);
}
Ok(to_value(new_str))
Ok(Value::from(new_str))
}
/// `String.prototype.repeat( count )`
@ -203,13 +198,12 @@ pub fn repeat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
let repeat_times: usize = from_value(
let repeat_times = usize::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
Ok(to_value(primitive_val.repeat(repeat_times)))
.expect("failed to get argument for String method"),
);
Ok(Value::from(primitive_val.repeat(repeat_times)))
}
/// `String.prototype.slice( beginIndex [, endIndex] )`
@ -227,18 +221,12 @@ pub fn slice(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
// Then we convert it into a Rust String by wrapping it in from_value
let primitive_val: String = ctx.value_to_rust_string(this);
let start: i32 = from_value(
let start = i32::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
let end: i32 = from_value(
args.get(1)
.expect("failed to get argument in slice")
.clone(),
)
.expect("failed to parse argument");
.expect("failed to get argument for String method"),
);
let end = i32::from(args.get(1).expect("failed to get argument in slice"));
// Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points
// Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation.
@ -266,7 +254,7 @@ pub fn slice(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV
.expect("Could not get nth char"),
);
}
Ok(to_value(new_str))
Ok(Value::from(new_str))
}
/// `String.prototype.startWith( searchString[, position] )`
@ -285,12 +273,10 @@ pub fn starts_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> R
let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if pattern is regular expression
let search_string: String = from_value(
let search_string = String::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
let length: i32 = primitive_val.chars().count() as i32;
let search_length: i32 = search_string.chars().count() as i32;
@ -299,18 +285,18 @@ pub fn starts_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> R
let position: i32 = if args.len() < 2 {
0
} else {
from_value(args.get(1).expect("failed to get arg").clone()).expect("failed to get argument")
i32::from(args.get(1).expect("failed to get arg"))
};
let start = min(max(position, 0), length);
let end = start.wrapping_add(search_length);
if end > length {
Ok(to_value(false))
Ok(Value::from(false))
} else {
// Only use the part of the string from "start"
let this_string: String = primitive_val.chars().skip(start as usize).collect();
Ok(to_value(this_string.starts_with(&search_string)))
Ok(Value::from(this_string.starts_with(&search_string)))
}
}
@ -330,12 +316,10 @@ pub fn ends_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res
let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(
let search_string = String::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
let length: i32 = primitive_val.chars().count() as i32;
let search_length: i32 = search_string.chars().count() as i32;
@ -345,19 +329,18 @@ pub fn ends_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res
let end_position: i32 = if args.len() < 2 {
length
} else {
from_value(args.get(1).expect("Could not get argumetn").clone())
.expect("Could not convert value to i32")
i32::from(args.get(1).expect("Could not get argumetn"))
};
let end = min(max(end_position, 0), length);
let start = end.wrapping_sub(search_length);
if start < 0 {
Ok(to_value(false))
Ok(Value::from(false))
} else {
// Only use the part of the string up to "end"
let this_string: String = primitive_val.chars().take(end as usize).collect();
Ok(to_value(this_string.ends_with(&search_string)))
Ok(Value::from(this_string.ends_with(&search_string)))
}
}
@ -377,12 +360,10 @@ pub fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(
let search_string = String::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
let length: i32 = primitive_val.chars().count() as i32;
@ -390,8 +371,7 @@ pub fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
let position: i32 = if args.len() < 2 {
0
} else {
from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert value to i32")
i32::from(args.get(1).expect("Could not get argument"))
};
let start = min(max(position, 0), length);
@ -399,7 +379,7 @@ pub fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
// Take the string from "this" and use only the part of it after "start"
let this_string: String = primitive_val.chars().skip(start as usize).collect();
Ok(to_value(this_string.contains(&search_string)))
Ok(Value::from(this_string.contains(&search_string)))
}
/// Return either the string itself or the string of the regex equivalent
@ -411,8 +391,7 @@ fn get_regex_string(value: &Value) -> String {
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") {
return from_value(r#body.clone())
.expect("unable to get body from regex value");
return String::from(r#body);
}
}
"undefined".to_string()
@ -440,7 +419,7 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
// TODO: Support Symbol replacer
let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() {
return Ok(to_value(primitive_val));
return Ok(Value::from(primitive_val));
}
let regex_body = get_regex_string(args.get(0).expect("Value needed"));
@ -497,7 +476,7 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
// This will return the matched substring first, then captured parenthesized groups later
let mut results: Vec<Value> = caps
.iter()
.map(|capture| to_value(capture.unwrap().as_str()))
.map(|capture| Value::from(capture.unwrap().as_str()))
.collect();
// Returns the starting byte offset of the match
@ -505,9 +484,9 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
.get(0)
.expect("Unable to get Byte offset from string for match")
.start();
results.push(to_value(start));
results.push(Value::from(start));
// Push the whole string being examined
results.push(to_value(primitive_val.to_string()));
results.push(Value::from(primitive_val.to_string()));
let result = ctx.call(&replace_object, this, &results).unwrap();
@ -519,7 +498,7 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
"undefined".to_string()
};
Ok(to_value(primitive_val.replacen(
Ok(Value::from(primitive_val.replacen(
&mat.as_str(),
&replace_value,
1,
@ -544,12 +523,10 @@ pub fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(
let search_string = String::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
let length: i32 = primitive_val.chars().count() as i32;
@ -557,8 +534,7 @@ pub fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
let position: i32 = if args.len() < 2 {
0
} else {
from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert value to i32")
i32::from(args.get(1).expect("Could not get argument"))
};
let start = min(max(position, 0), length);
@ -571,11 +547,11 @@ pub fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
let this_string: String = primitive_val.chars().skip(index as usize).collect();
if this_string.starts_with(&search_string) {
// Explicitly return early with the index value
return Ok(to_value(index));
return Ok(Value::from(index));
}
}
// Didn't find a match, so return -1
Ok(to_value(-1))
Ok(Value::from(-1))
}
/// `String.prototype.lastIndexOf( searchValue[, fromIndex] )`
@ -596,12 +572,10 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) ->
let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value(
let search_string = String::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
let length: i32 = primitive_val.chars().count() as i32;
@ -609,8 +583,7 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) ->
let position: i32 = if args.len() < 2 {
0
} else {
from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert value to i32")
i32::from(args.get(1).expect("Could not get argument"))
};
let start = min(max(position, 0), length);
@ -628,7 +601,7 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) ->
}
// This will still be -1 if no matches were found, else with be >= 0
Ok(to_value(highest_index))
Ok(Value::from(highest_index))
}
/// `String.prototype.match( regexp )`
@ -643,7 +616,7 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) ->
/// [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 fn r#match(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let mut re = make_regexp(&mut to_value(Object::default()), &[args[0].clone()], ctx)?;
let mut re = make_regexp(&mut Value::from(Object::default()), &[args[0].clone()], ctx)?;
regexp_match(&mut re, ctx.value_to_rust_string(this), ctx)
}
@ -660,7 +633,7 @@ fn string_pad(
let primitive_length = primitive.len() as i32;
if max_length <= primitive_length {
return Ok(to_value(primitive));
return Ok(Value::from(primitive));
}
let filler = match fill_string {
@ -669,7 +642,7 @@ fn string_pad(
};
if filler == "" {
return Ok(to_value(primitive));
return Ok(Value::from(primitive));
}
let fill_len = max_length.wrapping_sub(primitive_length);
@ -682,9 +655,9 @@ fn string_pad(
let concat_fill_str: String = fill_str.chars().take(fill_len as usize).collect();
if at_start {
Ok(to_value(format!("{}{}", concat_fill_str, &primitive)))
Ok(Value::from(format!("{}{}", concat_fill_str, &primitive)))
} else {
Ok(to_value(format!("{}{}", primitive, &concat_fill_str)))
Ok(Value::from(format!("{}{}", primitive, &concat_fill_str)))
}
}
@ -703,20 +676,16 @@ fn string_pad(
pub fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() {
return Err(to_value("padEnd requires maxLength argument"));
return Err(Value::from("padEnd requires maxLength argument"));
}
let max_length = from_value(
let max_length = i32::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
let fill_string: Option<String> = match args.len() {
1 => None,
_ => Some(
from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert value to Option<String>"),
),
_ => Some(String::from(args.get(1).expect("Could not get argument"))),
};
string_pad(primitive_val, max_length, fill_string, false)
@ -737,20 +706,16 @@ pub fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
pub fn pad_start(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() {
return Err(to_value("padStart requires maxLength argument"));
return Err(Value::from("padStart requires maxLength argument"));
}
let max_length = from_value(
let max_length = i32::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
)
.expect("failed to parse argument for String method");
.expect("failed to get argument for String method"),
);
let fill_string: Option<String> = match args.len() {
1 => None,
_ => Some(
from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert value to Option<String>"),
),
_ => Some(String::from(args.get(1).expect("Could not get argument"))),
};
string_pad(primitive_val, max_length, fill_string, true)
@ -789,7 +754,7 @@ fn is_trimmable_whitespace(c: char) -> bool {
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim
pub fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(this_str.trim_matches(is_trimmable_whitespace)))
Ok(Value::from(this_str.trim_matches(is_trimmable_whitespace)))
}
/// `String.prototype.trimStart()`
@ -806,7 +771,7 @@ pub fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart
pub fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(
Ok(Value::from(
this_str.trim_start_matches(is_trimmable_whitespace),
))
}
@ -825,7 +790,9 @@ pub fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> Resul
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd
pub fn trim_end(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace)))
Ok(Value::from(
this_str.trim_end_matches(is_trimmable_whitespace),
))
}
/// `String.prototype.toLowerCase()`
@ -844,7 +811,7 @@ pub fn to_lowercase(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> Res
let this_str: String = ctx.value_to_rust_string(this);
// The Rust String is mapped to uppercase using the builtin .to_lowercase().
// There might be corner cases where it does not behave exactly like Javascript expects
Ok(to_value(this_str.to_lowercase()))
Ok(Value::from(this_str.to_lowercase()))
}
/// `String.prototype.toUpperCase()`
@ -865,7 +832,7 @@ pub fn to_uppercase(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> Res
let this_str: String = ctx.value_to_rust_string(this);
// The Rust String is mapped to uppercase using the builtin .to_uppercase().
// There might be corner cases where it does not behave exactly like Javascript expects
Ok(to_value(this_str.to_uppercase()))
Ok(Value::from(this_str.to_uppercase()))
}
/// `String.prototype.substring( indexStart[, indexEnd] )`
@ -886,20 +853,17 @@ pub fn substring(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res
let start = if args.is_empty() {
0
} else {
from_value(
i32::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
.expect("failed to get argument for String method"),
)
.expect("failed to parse argument for String method")
};
let length: i32 = primitive_val.chars().count() as i32;
// If less than 2 args specified, end is the length of the this object converted to a String
let end = if args.len() < 2 {
length
} else {
from_value(args.get(1).expect("Could not get argument").clone())
.expect("failed to parse argument for String method")
i32::from(args.get(1).expect("Could not get argument"))
};
// Both start and end args replaced by 0 if they were negative
// or by the length of the String if they were greater
@ -915,7 +879,7 @@ pub fn substring(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res
.skip(from)
.take(to.wrapping_sub(from))
.collect();
Ok(to_value(extracted_string))
Ok(Value::from(extracted_string))
}
/// `String.prototype.substr( start[, length] )`
@ -937,12 +901,10 @@ pub fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
let mut start = if args.is_empty() {
0
} else {
from_value(
i32::from(
args.get(0)
.expect("failed to get argument for String method")
.clone(),
.expect("failed to get argument for String method"),
)
.expect("failed to parse argument for String method")
};
let length: i32 = primitive_val.chars().count() as i32;
// If less than 2 args specified, end is +infinity, the maximum number value.
@ -952,8 +914,7 @@ pub fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
let end = if args.len() < 2 {
i32::max_value()
} else {
from_value(args.get(1).expect("Could not get argument").clone())
.expect("failed to parse argument for String method")
i32::from(args.get(1).expect("Could not get argument"))
};
// If start is negative it become the number of code units from the end of the string
if start < 0 {
@ -965,14 +926,15 @@ pub fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result
// If length is negative we return an empty string
// otherwise we extract the part of the string from start and is length code units long
if result_length <= 0 {
Ok(to_value("".to_string()))
Ok(Value::from("".to_string()))
} else {
let extracted_string: String = primitive_val
.chars()
.skip(start as usize)
.take(result_length as usize)
.collect();
Ok(to_value(extracted_string))
Ok(Value::from(extracted_string))
}
}
@ -1007,28 +969,28 @@ pub fn value_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let mut re: Value = match args.get(0) {
Some(arg) => {
if arg == &Gc::new(ValueData::Null) {
if arg == &Value::null() {
make_regexp(
&mut to_value(Object::default()),
&mut Value::from(Object::default()),
&[
to_value(ctx.value_to_rust_string(arg)),
to_value(String::from("g")),
Value::from(ctx.value_to_rust_string(arg)),
Value::from(String::from("g")),
],
ctx,
)
} else if arg == &undefined() {
} else if arg == &Value::undefined() {
make_regexp(
&mut to_value(Object::default()),
&[undefined(), to_value(String::from("g"))],
&mut Value::from(Object::default()),
&[Value::undefined(), Value::from(String::from("g"))],
ctx,
)
} else {
from_value(arg.clone()).map_err(to_value)
Ok(arg.clone())
}
}
None => make_regexp(
&mut to_value(Object::default()),
&[to_value(String::new()), to_value(String::from("g"))],
&mut Value::from(Object::default()),
&[Value::from(String::new()), Value::from("g")],
ctx,
),
}?;
@ -1039,10 +1001,10 @@ pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res
/// Create a new `String` object.
pub fn create(global: &Value) -> Value {
// Create prototype
let prototype = ValueData::new_obj(Some(global));
let length = Property::default().value(to_value(0_i32));
let prototype = Value::new_object(Some(global));
let length = Property::default().value(Value::from(0));
prototype.set_prop_slice("length", length);
prototype.set_property_slice("length", length);
make_builtin_fn!(char_at, named "charAt", with length 1, of prototype);
make_builtin_fn!(char_code_at, named "charCodeAt", with length 1, of prototype);
make_builtin_fn!(to_string, named "toString", of prototype);

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

@ -5,7 +5,7 @@ use crate::{forward, forward_val};
#[test]
fn check_string_constructor_is_function() {
let global = ValueData::new_obj(None);
let global = Value::new_object(None);
let string_constructor = create(&global);
assert_eq!(string_constructor.is_function(), true);
}

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

@ -24,7 +24,7 @@ use crate::{
internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE,
PROTOTYPE,
},
value::{to_value, undefined, ResultValue, Value, ValueData},
value::{ResultValue, Value, ValueData},
},
exec::Interpreter,
};
@ -51,12 +51,12 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
// Set description which should either be undefined or a string
let desc_string = match args.get(0) {
Some(value) => to_value(value.to_string()),
None => undefined(),
Some(value) => Value::from(value.to_string()),
None => Value::undefined(),
};
sym_instance.set_internal_slot("Description", desc_string);
sym_instance.set_internal_slot("SymbolData", to_value(random::<i32>()));
sym_instance.set_internal_slot("SymbolData", Value::from(random::<i32>()));
// Set __proto__ internal slot
let proto = ctx
@ -66,9 +66,9 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
.get_field_slice(PROTOTYPE);
sym_instance.set_internal_slot(INSTANCE_PROTOTYPE, proto);
Ok(Gc::new(ValueData::Symbol(Box::new(GcCell::new(
Ok(Value(Gc::new(ValueData::Symbol(Box::new(GcCell::new(
sym_instance,
)))))
))))))
}
/// `Symbol.prototype.toString()`
@ -84,13 +84,13 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let s: Value = this.get_internal_slot("Description");
let full_string = format!(r#"Symbol({})"#, s.to_string());
Ok(to_value(full_string))
Ok(Value::from(full_string))
}
/// Create a new `Symbol` object.
pub fn create(global: &Value) -> Value {
// Create prototype object
let prototype = ValueData::new_obj(Some(global));
let prototype = Value::new_object(Some(global));
make_builtin_fn!(to_string, named "toString", of prototype);
make_constructor_fn!(call_symbol, call_symbol, global, prototype)
}

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

@ -5,7 +5,7 @@ use crate::{forward, forward_val};
#[test]
fn check_symbol_constructor_is_function() {
let global: Gc<ValueData> = ValueData::new_obj(None);
let global = Value::new_object(None);
let symbol_constructor = create(&global);
assert_eq!(symbol_constructor.is_function(), true);
}

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

@ -1,202 +1,187 @@
use super::*;
use std::convert::TryFrom;
/// Conversion to Javascript values from Rust values
pub trait ToValue {
/// Convert this value to a Rust value
fn to_value(&self) -> Value;
}
/// Conversion to Rust values from Javascript values
pub trait FromValue {
/// Convert this value to a Javascript value
fn from_value(value: Value) -> Result<Self, &'static str>
where
Self: Sized;
}
impl ToValue for Value {
fn to_value(&self) -> Value {
self.clone()
impl From<&Value> for Value {
fn from(value: &Value) -> Self {
value.clone()
}
}
impl FromValue for Value {
fn from_value(value: Value) -> Result<Self, &'static str> {
Ok(value)
impl From<String> for Value {
fn from(value: String) -> Self {
Self::string(value)
}
}
impl ToValue for String {
fn to_value(&self) -> Value {
Gc::new(ValueData::String(self.clone()))
impl From<&Value> for String {
fn from(value: &Value) -> Self {
value.to_string()
}
}
impl FromValue for String {
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_string())
impl From<&str> for Value {
fn from(value: &str) -> Value {
Value::string(value)
}
}
impl<'s> ToValue for &'s str {
fn to_value(&self) -> Value {
Gc::new(ValueData::String(
String::from_str(*self).expect("Could not convert string to self to String"),
))
impl From<char> for Value {
fn from(value: char) -> Self {
Value::string(value.to_string())
}
}
impl ToValue for char {
fn to_value(&self) -> Value {
Gc::new(ValueData::String(self.to_string()))
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct TryFromCharError;
impl Display for TryFromCharError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Could not convert value to a char type")
}
}
impl FromValue for char {
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_string()
.chars()
.next()
.expect("Could not get next char"))
impl TryFrom<&Value> for char {
type Error = TryFromCharError;
fn try_from(value: &Value) -> Result<Self, Self::Error> {
if let Some(c) = value.to_string().chars().next() {
Ok(c)
} else {
Err(TryFromCharError)
}
}
}
impl ToValue for f64 {
fn to_value(&self) -> Value {
Gc::new(ValueData::Rational(*self))
impl From<f64> for Value {
fn from(value: f64) -> Self {
Self::rational(value)
}
}
impl FromValue for f64 {
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_number())
impl From<&Value> for f64 {
fn from(value: &Value) -> Self {
value.to_number()
}
}
impl ToValue for i32 {
fn to_value(&self) -> Value {
Gc::new(ValueData::Integer(*self))
impl From<i32> for Value {
fn from(value: i32) -> Value {
Value::integer(value)
}
}
impl FromValue for i32 {
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_integer())
impl From<&Value> for i32 {
fn from(value: &Value) -> i32 {
value.to_integer()
}
}
impl ToValue for usize {
fn to_value(&self) -> Value {
Gc::new(ValueData::Integer(*self as i32))
impl From<usize> for Value {
fn from(value: usize) -> Value {
Value::integer(value as i32)
}
}
impl FromValue for usize {
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_integer() as Self)
impl From<&Value> for usize {
fn from(value: &Value) -> usize {
value.to_integer() as Self
}
}
impl ToValue for bool {
fn to_value(&self) -> Value {
Gc::new(ValueData::Boolean(*self))
impl From<bool> for Value {
fn from(value: bool) -> Self {
Value::boolean(value)
}
}
impl FromValue for bool {
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.is_true())
impl From<&Value> for bool {
fn from(value: &Value) -> Self {
value.is_true()
}
}
impl<'s, T: ToValue> ToValue for &'s [T] {
fn to_value(&self) -> Value {
let mut arr = Object::default();
for (i, item) in self.iter().enumerate() {
arr.properties
.insert(i.to_string(), Property::default().value(item.to_value()));
impl<T> From<&[T]> for Value
where
T: Clone + Into<Value>,
{
fn from(value: &[T]) -> Self {
let mut array = Object::default();
for (i, item) in value.iter().enumerate() {
array.properties.insert(
i.to_string(),
Property::default().value(item.clone().into()),
);
}
to_value(arr)
Self::from(array)
}
}
impl<T: ToValue> ToValue for Vec<T> {
fn to_value(&self) -> Value {
let mut arr = Object::default();
for (i, item) in self.iter().enumerate() {
arr.properties
.insert(i.to_string(), Property::default().value(item.to_value()));
impl<T> From<Vec<T>> for Value
where
T: Into<Value>,
{
fn from(value: Vec<T>) -> Self {
let mut array = Object::default();
for (i, item) in value.into_iter().enumerate() {
array
.properties
.insert(i.to_string(), Property::default().value(item.into()));
}
to_value(arr)
Value::from(array)
}
}
impl<T: FromValue> FromValue for Vec<T> {
fn from_value(v: Value) -> Result<Self, &'static str> {
let len = v.get_field_slice("length").to_integer();
let mut vec = Self::with_capacity(len as usize);
for i in 0..len {
vec.push(from_value(v.get_field_slice(&i.to_string()))?)
}
Ok(vec)
impl From<Object> for Value {
fn from(object: Object) -> Self {
Value::object(object)
}
}
impl ToValue for Object {
fn to_value(&self) -> Value {
Gc::new(ValueData::Object(Box::new(GcCell::new(self.clone()))))
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
pub struct TryFromObjectError;
impl Display for TryFromObjectError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Could not convert value to an Object type")
}
}
impl FromValue for Object {
fn from_value(v: Value) -> Result<Self, &'static str> {
match *v {
ValueData::Object(ref obj) => Ok(obj.clone().into_inner()),
_ => Err("Value is not a valid object"),
impl 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 ToValue for JSONValue {
fn to_value(&self) -> Value {
Gc::new(ValueData::from_json(self.clone()))
impl From<JSONValue> for Value {
fn from(value: JSONValue) -> Self {
Self(Gc::new(ValueData::from_json(value)))
}
}
impl FromValue for JSONValue {
fn from_value(v: Value) -> Result<Self, &'static str> {
Ok(v.to_json())
impl From<&Value> for JSONValue {
fn from(value: &Value) -> Self {
value.to_json()
}
}
impl ToValue for () {
fn to_value(&self) -> Value {
Gc::new(ValueData::Null)
}
}
impl FromValue for () {
fn from_value(_: Value) -> Result<(), &'static str> {
Ok(())
impl From<()> for Value {
fn from(_: ()) -> Self {
Value::null()
}
}
impl<T: ToValue> ToValue for Option<T> {
fn to_value(&self) -> Value {
match *self {
Some(ref v) => v.to_value(),
None => Gc::new(ValueData::Null),
impl<T> From<Option<T>> for Value
where
T: Into<Value>,
{
fn from(value: Option<T>) -> Self {
match value {
Some(value) => value.into(),
None => Value::null(),
}
}
}
impl<T: FromValue> FromValue for Option<T> {
fn from_value(value: Value) -> Result<Self, &'static str> {
Ok(if value.is_null_or_undefined() {
None
} else {
Some(FromValue::from_value(value)?)
})
}
}
/// A utility function that just calls `FromValue::from_value`
pub fn from_value<A: FromValue>(v: Value) -> Result<A, &'static str> {
FromValue::from_value(v)
}
/// A utility function that just calls `ToValue::to_value`
pub fn to_value<A: ToValue>(v: A) -> Value {
v.to_value()
}

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

@ -34,11 +34,118 @@ pub use operations::*;
pub type ResultValue = Result<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter.
pub type Value = Gc<ValueData>;
#[derive(Debug, Clone, Trace, Finalize, Default)]
pub struct Value(pub(crate) Gc<ValueData>);
#[inline]
pub fn undefined() -> Value {
Gc::new(ValueData::Undefined)
impl Value {
/// Creates a new `undefined` value.
#[inline]
pub fn undefined() -> Self {
Self(Gc::new(ValueData::Undefined))
}
/// Creates a new `null` value.
#[inline]
pub fn null() -> Self {
Self(Gc::new(ValueData::Null))
}
/// Creates a new string value.
#[inline]
pub fn string<S>(value: S) -> Self
where
S: Into<String>,
{
Self(Gc::new(ValueData::String(value.into())))
}
/// Creates a new number value.
#[inline]
pub fn rational<N>(value: N) -> Self
where
N: Into<f64>,
{
Self(Gc::new(ValueData::Rational(value.into())))
}
/// Creates a new number value.
#[inline]
pub fn integer<I>(value: I) -> Self
where
I: Into<i32>,
{
Self(Gc::new(ValueData::Integer(value.into())))
}
/// Creates a new number value.
#[inline]
pub fn number<N>(value: N) -> Self
where
N: Into<f64>,
{
Self::rational(value.into())
}
/// Creates a new boolean value.
#[inline]
pub fn boolean(value: bool) -> Self {
Self(Gc::new(ValueData::Boolean(value)))
}
/// Creates a new object value.
#[inline]
pub fn object(object: Object) -> Self {
Self(Gc::new(ValueData::Object(Box::new(GcCell::new(object)))))
}
/// Gets the underlying `ValueData` structure.
#[inline]
pub fn data(&self) -> &ValueData {
&*self.0
}
/// Helper function to convert the `Value` to a number and compute its power.
pub fn as_num_to_power(&self, other: Self) -> Self {
Self::rational(self.to_number().powf(other.to_number()))
}
/// Returns a new empty object
pub fn new_object(global: Option<&Value>) -> Self {
if let Some(global) = global {
let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE);
let object = Object::create(object_prototype);
Self::object(object)
} else {
Self::object(Object::default())
}
}
/// Similar to `new_object`, but you can pass a prototype to create from, plus a kind
pub fn new_object_from_prototype(proto: Value, kind: ObjectKind) -> Self {
let mut object = Object::default();
object.kind = kind;
object
.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
Self::object(object)
}
}
impl Deref for Value {
type Target = ValueData;
fn deref(&self) -> &Self::Target {
self.data()
}
}
impl Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
Display::fmt(&self.0, f)
}
}
/// A Javascript value
@ -63,31 +170,6 @@ pub enum ValueData {
}
impl ValueData {
/// Returns a new empty object
pub fn new_obj(global: Option<&Value>) -> Value {
if let Some(glob) = global {
let obj_proto = glob.get_field_slice("Object").get_field_slice(PROTOTYPE);
let obj = Object::create(obj_proto);
Gc::new(Self::Object(Box::new(GcCell::new(obj))))
} else {
let obj = Object::default();
Gc::new(Self::Object(Box::new(GcCell::new(obj))))
}
}
/// Similar to `new_obj`, but you can pass a prototype to create from,
/// plus a kind
pub fn new_obj_from_prototype(proto: Value, kind: ObjectKind) -> Value {
let mut obj = Object::default();
obj.kind = kind;
obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto);
Gc::new(Self::Object(Box::new(GcCell::new(obj))))
}
/// This will tell us if we can exten an object or not, not properly implemented yet
///
/// For now always returns true.
@ -249,10 +331,10 @@ impl ValueData {
}
}
/// remove_prop removes a property from a Value object.
/// Removes a property from a Value object.
///
/// It will return a boolean based on if the value was removed, if there was no value to remove false is returned
pub fn remove_prop(&self, field: &str) {
pub fn remove_property(&self, field: &str) {
match *self {
Self::Object(ref obj) => obj.borrow_mut().deref_mut().properties.remove(field),
_ => None,
@ -262,12 +344,12 @@ impl ValueData {
/// Resolve the property in the object.
///
/// A copy of the Property is returned.
pub fn get_prop(&self, field: &str) -> Option<Property> {
pub fn get_property(&self, field: &str) -> Option<Property> {
// Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154
// This is only for primitive strings, String() objects have their lengths calculated in string.rs
if self.is_string() && field == "length" {
if let Self::String(ref s) = *self {
return Some(Property::default().value(to_value(s.len() as i32)));
return Some(Property::default().value(Value::from(s.len())));
}
}
@ -292,7 +374,7 @@ impl ValueData {
match obj.properties.get(field) {
Some(val) => Some(val.clone()),
None => match obj.internal_slots.get(&INSTANCE_PROTOTYPE.to_string()) {
Some(value) => value.get_prop(field),
Some(value) => value.get_property(field),
None => None,
},
}
@ -301,7 +383,7 @@ impl ValueData {
/// update_prop will overwrite individual [Property] fields, unlike
/// Set_prop, which will overwrite prop with a new Property
/// Mostly used internally for now
pub fn update_prop(
pub fn update_property(
&self,
field: &str,
value: Option<Value>,
@ -338,12 +420,12 @@ impl ValueData {
let hash = obj.clone();
hash.into_inner()
}
_ => return Gc::new(Self::Undefined),
_ => return Value::undefined(),
};
match obj.internal_slots.get(field) {
Some(val) => val.clone(),
None => Gc::new(Self::Undefined),
None => Value::undefined(),
}
}
@ -354,7 +436,7 @@ impl ValueData {
match *field {
// Our field will either be a String or a Symbol
Self::String(ref s) => {
match self.get_prop(s) {
match self.get_property(s) {
Some(prop) => {
// If the Property has [[Get]] set to a function, we should run that and return the Value
let prop_getter = match prop.get {
@ -373,11 +455,11 @@ impl ValueData {
val.clone()
}
}
None => Gc::new(Self::Undefined),
None => Value::undefined(),
}
}
Self::Symbol(_) => unimplemented!(),
_ => Gc::new(Self::Undefined),
_ => Value::undefined(),
}
}
@ -449,14 +531,14 @@ impl ValueData {
/// Check to see if the Value has the field, mainly used by environment records
pub fn has_field(&self, field: &str) -> bool {
self.get_prop(field).is_some()
self.get_property(field).is_some()
}
/// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist
pub fn get_field_slice(&self, field: &str) -> Value {
// get_field used to accept strings, but now Symbols accept it needs to accept a value
// So this function will now need to Box strings back into values (at least for now)
let f = Gc::new(Self::String(field.to_string()));
let f = Value::string(field.to_string());
self.get_field(f)
}
@ -467,10 +549,9 @@ impl ValueData {
if obj.borrow().kind == ObjectKind::Array {
if let Ok(num) = field.to_string().parse::<usize>() {
if num > 0 {
let len: i32 = from_value(self.get_field_slice("length"))
.expect("Could not convert argument to i32");
let len = i32::from(&self.get_field_slice("length"));
if len < (num + 1) as i32 {
self.set_field_slice("length", to_value(num + 1));
self.set_field_slice("length", Value::from(num + 1));
}
}
}
@ -481,7 +562,7 @@ impl ValueData {
obj.borrow_mut().set(field, val.clone());
} else {
obj.borrow_mut()
.set(to_value(field.to_string()), val.clone());
.set(Value::from(field.to_string()), val.clone());
}
}
@ -492,7 +573,7 @@ impl ValueData {
pub fn set_field_slice(&self, field: &str, val: Value) -> Value {
// set_field used to accept strings, but now Symbols accept it needs to accept a value
// So this function will now need to Box strings back into values (at least for now)
let f = Gc::new(Self::String(field.to_string()));
let f = Value::string(field.to_string());
self.set_field(f, val)
}
@ -514,7 +595,7 @@ impl ValueData {
}
/// Set the property in the value
pub fn set_prop(&self, field: String, prop: Property) -> Property {
pub fn set_property(&self, field: String, prop: Property) -> Property {
if let Self::Object(ref obj) = *self {
obj.borrow_mut().properties.insert(field, prop.clone());
}
@ -522,8 +603,8 @@ impl ValueData {
}
/// Set the property in the value
pub fn set_prop_slice(&self, field: &str, prop: Property) -> Property {
self.set_prop(field.to_string(), prop)
pub fn set_property_slice(&self, field: &str, prop: Property) -> Property {
self.set_property(field.to_string(), prop)
}
/// Set internal state of an Object. Discards the previous state if it was set.
@ -544,9 +625,9 @@ impl ValueData {
// Set [[Call]] internal slot
new_func.set_call(native_func);
// Wrap Object in GC'd Value
let new_func_val = to_value(new_func);
let new_func_val = Value::from(new_func);
// Set length to parameters
new_func_val.set_field_slice("length", to_value(length));
new_func_val.set_field_slice("length", Value::from(length));
new_func_val
}
@ -563,12 +644,12 @@ impl ValueData {
for (idx, json) in vs.iter().enumerate() {
new_obj.properties.insert(
idx.to_string(),
Property::default().value(to_value(json.clone())),
Property::default().value(Value::from(json.clone())),
);
}
new_obj.properties.insert(
"length".to_string(),
Property::default().value(to_value(vs.len() as i32)),
Property::default().value(Value::from(vs.len())),
);
Self::Object(Box::new(GcCell::new(new_obj)))
}
@ -577,7 +658,7 @@ impl ValueData {
for (key, json) in obj.iter() {
new_obj.properties.insert(
key.clone(),
Property::default().value(to_value(json.clone())),
Property::default().value(Value::from(json.clone())),
);
}
@ -629,10 +710,6 @@ impl ValueData {
}
}
}
pub fn as_num_to_power(&self, other: Self) -> Self {
Self::Rational(self.to_number().powf(other.to_number()))
}
}
impl Default for ValueData {
@ -709,30 +786,27 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String {
// Can use the private "type" field of an Object to match on
// which type of Object it represents for special printing
match v.borrow().kind {
ObjectKind::String => from_value(
ObjectKind::String => String::from(
v.borrow()
.internal_slots
.get("StringData")
.expect("Cannot get primitive value from String")
.clone(),
)
.expect("Cannot clone primitive value from String"),
.expect("Cannot get primitive value from String"),
),
ObjectKind::Boolean => {
let bool_data = v.borrow().get_internal_slot("BooleanData").to_string();
format!("Boolean {{ {} }}", bool_data)
}
ObjectKind::Array => {
let len: i32 = from_value(
v.borrow()
let len = i32::from(
&v.borrow()
.properties
.get("length")
.unwrap()
.value
.clone()
.expect("Could not borrow value"),
)
.expect("Could not convert JS value to i32");
);
if len == 0 {
return String::from("[]");

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

@ -1,92 +1,98 @@
use super::*;
impl PartialEq for ValueData {
impl PartialEq for Value {
fn eq(&self, other: &Self) -> bool {
match (self.clone(), other.clone()) {
match (self.data(), other.data()) {
// TODO: fix this
// _ if self.ptr.to_inner() == &other.ptr.to_inner() => true,
_ if self.is_null_or_undefined() && other.is_null_or_undefined() => true,
(Self::String(_), _) | (_, Self::String(_)) => self.to_string() == other.to_string(),
(Self::Boolean(a), Self::Boolean(b)) if a == b => true,
(Self::Rational(a), Self::Rational(b)) if a == b && !a.is_nan() && !b.is_nan() => true,
(Self::Rational(a), _) if a == other.to_number() => true,
(_, Self::Rational(a)) if a == self.to_number() => true,
(Self::Integer(a), Self::Integer(b)) if a == b => true,
(ValueData::String(_), _) | (_, ValueData::String(_)) => {
self.to_string() == other.to_string()
}
(ValueData::Boolean(a), ValueData::Boolean(b)) if a == b => true,
(ValueData::Rational(a), ValueData::Rational(b))
if a == b && !a.is_nan() && !b.is_nan() =>
{
true
}
(ValueData::Rational(a), _) if *a == other.to_number() => true,
(_, ValueData::Rational(a)) if *a == self.to_number() => true,
(ValueData::Integer(a), ValueData::Integer(b)) if a == b => true,
_ => false,
}
}
}
impl Add for ValueData {
impl Add for Value {
type Output = Self;
fn add(self, other: Self) -> Self {
match (self, other) {
(Self::String(ref s), ref o) => {
Self::String(format!("{}{}", s.clone(), &o.to_string()))
match (self.data(), other.data()) {
(ValueData::String(ref s), ref o) => {
Self::string(format!("{}{}", s.clone(), &o.to_string()))
}
(ref s, Self::String(ref o)) => Self::String(format!("{}{}", s.to_string(), o)),
(ref s, ref o) => Self::Rational(s.to_number() + o.to_number()),
(ref s, ValueData::String(ref o)) => Self::string(format!("{}{}", s.to_string(), o)),
(ref s, ref o) => Self::rational(s.to_number() + o.to_number()),
}
}
}
impl Sub for ValueData {
impl Sub for Value {
type Output = Self;
fn sub(self, other: Self) -> Self {
Self::Rational(self.to_number() - other.to_number())
Self::rational(self.to_number() - other.to_number())
}
}
impl Mul for ValueData {
impl Mul for Value {
type Output = Self;
fn mul(self, other: Self) -> Self {
Self::Rational(self.to_number() * other.to_number())
Self::rational(self.to_number() * other.to_number())
}
}
impl Div for ValueData {
impl Div for Value {
type Output = Self;
fn div(self, other: Self) -> Self {
Self::Rational(self.to_number() / other.to_number())
Self::rational(self.to_number() / other.to_number())
}
}
impl Rem for ValueData {
impl Rem for Value {
type Output = Self;
fn rem(self, other: Self) -> Self {
Self::Rational(self.to_number() % other.to_number())
Self::rational(self.to_number() % other.to_number())
}
}
impl BitAnd for ValueData {
impl BitAnd for Value {
type Output = Self;
fn bitand(self, other: Self) -> Self {
Self::Integer(self.to_integer() & other.to_integer())
Self::integer(self.to_integer() & other.to_integer())
}
}
impl BitOr for ValueData {
impl BitOr for Value {
type Output = Self;
fn bitor(self, other: Self) -> Self {
Self::Integer(self.to_integer() | other.to_integer())
Self::integer(self.to_integer() | other.to_integer())
}
}
impl BitXor for ValueData {
impl BitXor for Value {
type Output = Self;
fn bitxor(self, other: Self) -> Self {
Self::Integer(self.to_integer() ^ other.to_integer())
Self::integer(self.to_integer() ^ other.to_integer())
}
}
impl Shl for ValueData {
impl Shl for Value {
type Output = Self;
fn shl(self, other: Self) -> Self {
Self::Integer(self.to_integer() << other.to_integer())
Self::integer(self.to_integer() << other.to_integer())
}
}
impl Shr for ValueData {
impl Shr for Value {
type Output = Self;
fn shr(self, other: Self) -> Self {
Self::Integer(self.to_integer() >> other.to_integer())
Self::integer(self.to_integer() >> other.to_integer())
}
}
impl Not for ValueData {
impl Not for Value {
type Output = Self;
fn not(self) -> Self {
Self::Boolean(!self.is_true())
Self::boolean(!self.is_true())
}
}
@ -99,8 +105,8 @@ impl Not for ValueData {
pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool {
if strict {
// Do both Values point to the same underlying valueData?
let x_ptr = Gc::into_raw(x.clone());
let y_ptr = Gc::into_raw(y.clone());
let x_ptr = Gc::into_raw(x.0.clone());
let y_ptr = Gc::into_raw(y.0.clone());
return x_ptr == y_ptr;
}
@ -109,8 +115,8 @@ pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool {
}
if x.get_type() == "number" {
let native_x: f64 = from_value(x.clone()).expect("failed to get value");
let native_y: f64 = from_value(y.clone()).expect("failed to get value");
let native_x = f64::from(x);
let native_y = f64::from(y);
return native_x.abs() - native_y.abs() == 0.0;
}
@ -128,10 +134,7 @@ pub fn same_value_non_number(x: &Value, y: &Value) -> bool {
}
false
}
"boolean" => {
from_value::<bool>(x.clone()).expect("failed to get value")
== from_value::<bool>(y.clone()).expect("failed to get value")
}
"boolean" => bool::from(x) == bool::from(y),
"object" => *x == *y,
_ => false,
}

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

@ -2,14 +2,14 @@ use super::*;
#[test]
fn check_is_object() {
let val = ValueData::new_obj(None);
let val = Value::new_object(None);
assert_eq!(val.is_object(), true);
}
#[test]
fn check_string_to_value() {
let s = String::from("Hello");
let v = s.to_value();
let v = Value::from(s);
assert_eq!(v.is_string(), true);
assert_eq!(v.is_null(), false);
}
@ -23,26 +23,26 @@ fn check_undefined() {
#[test]
fn check_get_set_field() {
let obj = ValueData::new_obj(None);
let obj = Value::new_object(None);
// Create string and convert it to a Value
let s = String::from("bar").to_value();
let s = Value::from("bar");
obj.set_field_slice("foo", s);
assert_eq!(obj.get_field_slice("foo").to_string(), "bar");
}
#[test]
fn check_integer_is_true() {
assert_eq!(1.to_value().is_true(), true);
assert_eq!(0.to_value().is_true(), false);
assert_eq!((-1).to_value().is_true(), true);
assert_eq!(Value::from(1).is_true(), true);
assert_eq!(Value::from(0).is_true(), false);
assert_eq!(Value::from(-1).is_true(), true);
}
#[test]
fn check_number_is_true() {
assert_eq!(1.0.to_value().is_true(), true);
assert_eq!(0.1.to_value().is_true(), true);
assert_eq!(0.0.to_value().is_true(), false);
assert_eq!((-0.0).to_value().is_true(), false);
assert_eq!((-1.0).to_value().is_true(), true);
assert_eq!(NAN.to_value().is_true(), false);
assert_eq!(Value::from(1.0).is_true(), true);
assert_eq!(Value::from(0.1).is_true(), true);
assert_eq!(Value::from(0.0).is_true(), false);
assert_eq!(Value::from(-0.0).is_true(), false);
assert_eq!(Value::from(-1.0).is_true(), true);
assert_eq!(Value::from(NAN).is_true(), false);
}

6
boa/src/environment/declarative_environment_record.rs

@ -6,13 +6,13 @@
//! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.es/ecma262/#sec-declarative-environment-records)
use crate::{
builtins::value::{Value, ValueData},
builtins::value::Value,
environment::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
};
use gc::{Finalize, Gc, Trace};
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap;
/// Declarative Bindings have a few properties for book keeping purposes, such as mutability (const vs let).
@ -154,7 +154,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord {
}
fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined)
Value::undefined()
}
fn get_outer_environment(&self) -> Option<Environment> {

6
boa/src/environment/function_environment_record.rs

@ -9,14 +9,14 @@
//! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
use crate::{
builtins::value::{Value, ValueData},
builtins::value::Value,
environment::{
declarative_environment_record::DeclarativeEnvironmentRecordBinding,
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
};
use gc::{Finalize, Gc, Trace};
use gc::{Finalize, Trace};
use rustc_hash::FxHashMap;
/// Different binding status for `this`.
@ -220,7 +220,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
}
fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined)
Value::undefined()
}
fn get_outer_environment(&self) -> Option<Environment> {

22
boa/src/environment/global_environment_record.rs

@ -8,7 +8,7 @@
//! More info: <https://tc39.es/ecma262/#sec-global-environment-records>
use crate::{
builtins::value::{Value, ValueData},
builtins::value::Value,
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
environment_record_trait::EnvironmentRecordTrait,
@ -16,7 +16,7 @@ use crate::{
object_environment_record::ObjectEnvironmentRecord,
},
};
use gc::{Finalize, Gc, Trace};
use gc::{Finalize, Trace};
use rustc_hash::FxHashSet;
#[derive(Debug, Trace, Finalize, Clone)]
@ -42,7 +42,7 @@ impl GlobalEnvironmentRecord {
pub fn has_restricted_global_property(&self, name: &str) -> bool {
let global_object = &self.object_record.bindings;
let existing_prop = global_object.get_prop(name);
let existing_prop = global_object.get_property(name);
match existing_prop {
Some(prop) => {
if prop.value.is_none() || prop.configurable.unwrap_or(false) {
@ -61,7 +61,7 @@ impl GlobalEnvironmentRecord {
let extensible = global_object.is_extensible();
if !has_property && extensible {
obj_rec.create_mutable_binding(name.clone(), deletion);
obj_rec.initialize_binding(&name, Gc::new(ValueData::Undefined));
obj_rec.initialize_binding(&name, Value::undefined());
}
let var_declared_names = &mut self.var_names;
@ -72,10 +72,10 @@ impl GlobalEnvironmentRecord {
pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) {
let global_object = &mut self.object_record.bindings;
let existing_prop = global_object.get_prop(&name);
let existing_prop = global_object.get_property(&name);
if let Some(prop) = existing_prop {
if prop.value.is_none() || prop.configurable.unwrap_or(false) {
global_object.update_prop(
global_object.update_property(
name,
Some(value),
Some(true),
@ -84,7 +84,13 @@ impl GlobalEnvironmentRecord {
);
}
} else {
global_object.update_prop(name, Some(value), Some(true), Some(true), Some(deletion));
global_object.update_property(
name,
Some(value),
Some(true),
Some(true),
Some(deletion),
);
}
}
}
@ -170,7 +176,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord {
}
fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined)
Value::undefined()
}
fn get_outer_environment(&self) -> Option<Environment> {

8
boa/src/environment/lexical_environment.rs

@ -6,7 +6,7 @@
//! This is the entrypoint to lexical environments.
use crate::{
builtins::value::{Value, ValueData},
builtins::value::Value,
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
environment_record_trait::EnvironmentRecordTrait,
@ -211,7 +211,7 @@ impl LexicalEnvironment {
self.environments()
.find(|env| env.borrow().has_binding(name))
.map(|env| env.borrow().get_binding_value(name, false))
.unwrap_or_else(|| Gc::new(ValueData::Undefined))
.unwrap_or_else(Value::undefined)
}
}
@ -234,10 +234,10 @@ pub fn new_function_environment(
env_rec: FxHashMap::default(),
function: f,
this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported
home_object: Gc::new(ValueData::Undefined),
home_object: Value::undefined(),
new_target,
outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects
this_value: Gc::new(ValueData::Undefined), // TODO: this_value should start as an Option as its not always there to begin with
this_value: Value::undefined(), // TODO: this_value should start as an Option as its not always there to begin with
})))
}

19
boa/src/environment/object_environment_record.rs

@ -7,16 +7,13 @@
//! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records)
use crate::{
builtins::{
property::Property,
value::{Value, ValueData},
},
builtins::{property::Property, value::Value},
environment::{
environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType},
},
};
use gc::{Finalize, Gc, Trace};
use gc::{Finalize, Trace};
#[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord {
@ -42,12 +39,12 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
// only for it to be replace with the real value later. We could just add the name to a Vector instead
let bindings = &mut self.bindings;
let prop = Property::default()
.value(Gc::new(ValueData::Undefined))
.value(Value::undefined())
.writable(true)
.enumerable(true)
.configurable(deletion);
bindings.set_prop(name, prop);
bindings.set_property(name, prop);
}
fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> bool {
@ -66,7 +63,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
debug_assert!(value.is_object() || value.is_function());
let bindings = &mut self.bindings;
bindings.update_prop(name, Some(value), None, None, Some(strict));
bindings.update_property(name, Some(value), None, None, Some(strict));
}
fn get_binding_value(&self, name: &str, strict: bool) -> Value {
@ -77,12 +74,12 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
// TODO: throw error here
// Error handling not implemented yet
}
Gc::new(ValueData::Undefined)
Value::undefined()
}
}
fn delete_binding(&mut self, name: &str) -> bool {
self.bindings.remove_prop(name);
self.bindings.remove_property(name);
true
}
@ -101,7 +98,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
return self.bindings.clone();
}
Gc::new(ValueData::Undefined)
Value::undefined()
}
fn get_outer_environment(&self) -> Option<Environment> {

171
boa/src/exec/mod.rs

@ -12,7 +12,7 @@ use crate::{
PROTOTYPE,
},
property::Property,
value::{from_value, to_value, ResultValue, Value, ValueData},
value::{ResultValue, Value, ValueData},
},
environment::lexical_environment::{new_declarative_environment, VariableScope},
realm::Realm,
@ -22,7 +22,6 @@ use crate::{
op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp},
},
};
use gc::Gc;
use std::{
borrow::{Borrow, BorrowMut},
ops::Deref,
@ -44,8 +43,8 @@ pub struct Interpreter {
pub realm: Realm,
}
fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value {
Gc::new(match *op {
fn exec_assign_op(op: &AssignOp, v_a: Value, v_b: Value) -> Value {
match *op {
AssignOp::Add => v_a + v_b,
AssignOp::Sub => v_a - v_b,
AssignOp::Mul => v_a * v_b,
@ -57,7 +56,7 @@ fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value {
AssignOp::Xor => v_a ^ v_b,
AssignOp::Shl => v_a << v_b,
AssignOp::Shr => v_a << v_b,
})
}
}
impl Executor for Interpreter {
@ -71,15 +70,15 @@ impl Executor for Interpreter {
#[allow(clippy::match_same_arms)]
fn run(&mut self, node: &Node) -> ResultValue {
match *node {
Node::Const(Const::Null) => Ok(to_value(None::<()>)),
Node::Const(Const::Undefined) => Ok(Gc::new(ValueData::Undefined)),
Node::Const(Const::Num(num)) => Ok(to_value(num)),
Node::Const(Const::Int(num)) => Ok(to_value(num)),
Node::Const(Const::Null) => Ok(Value::null()),
Node::Const(Const::Undefined) => Ok(Value::undefined()),
Node::Const(Const::Num(num)) => Ok(Value::rational(num)),
Node::Const(Const::Int(num)) => Ok(Value::integer(num)),
// we can't move String from Const into value, because const is a garbage collected value
// Which means Drop() get's called on Const, but str will be gone at that point.
// Do Const values need to be garbage collected? We no longer need them once we've generated Values
Node::Const(Const::String(ref str)) => Ok(to_value(str.to_owned())),
Node::Const(Const::Bool(val)) => Ok(to_value(val)),
Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())),
Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)),
Node::Block(ref es) => {
{
let env = &mut self.realm.environment;
@ -88,7 +87,7 @@ impl Executor for Interpreter {
)));
}
let mut obj = to_value(None::<()>);
let mut obj = Value::null();
for e in es.iter() {
let val = self.run(e)?;
// early return
@ -160,7 +159,7 @@ impl Executor for Interpreter {
fnct_result
}
Node::WhileLoop(ref cond, ref expr) => {
let mut result = Gc::new(ValueData::Undefined);
let mut result = Value::undefined();
while self.run(cond)?.borrow().is_true() {
result = self.run(expr)?;
}
@ -189,12 +188,12 @@ impl Executor for Interpreter {
}
}
Ok(Gc::new(ValueData::Undefined))
Ok(Value::undefined())
}
Node::If(ref cond, ref expr, None) => Ok(if self.run(cond)?.borrow().is_true() {
self.run(expr)?
} else {
Gc::new(ValueData::Undefined)
Value::undefined()
}),
Node::If(ref cond, ref expr, Some(ref else_e)) => {
Ok(if self.run(cond)?.borrow().is_true() {
@ -205,7 +204,7 @@ impl Executor for Interpreter {
}
Node::Switch(ref val_e, ref vals, ref default) => {
let val = self.run(val_e)?;
let mut result = Gc::new(ValueData::Null);
let mut result = Value::null();
let mut matched = false;
for tup in vals.iter() {
let cond = &tup.0;
@ -236,7 +235,7 @@ impl Executor for Interpreter {
.environment
.get_global_object()
.expect("Could not get the global object");
let obj = ValueData::new_obj(Some(global_val));
let obj = Value::new_object(Some(global_val));
// TODO: Implement the rest of the property types.
for property in properties.iter() {
@ -260,7 +259,7 @@ impl Executor for Interpreter {
}
Node::ArrayDecl(ref arr) => {
let array = array::new_array(self)?;
let mut elements: Vec<Value> = vec![];
let mut elements = Vec::new();
for elem in arr.iter() {
if let Node::Spread(ref x) = elem.deref() {
let val = self.run(x)?;
@ -293,11 +292,11 @@ impl Executor for Interpreter {
let mut new_func = Object::function();
new_func.set_call(func);
let val = to_value(new_func);
val.set_field_slice("length", to_value(args.len()));
let val = Value::from(new_func);
val.set_field_slice("length", Value::from(args.len()));
// Set the name and assign it in the current environment
val.set_field_slice("name", to_value(name.clone()));
val.set_field_slice("name", Value::from(name.clone()));
self.realm.environment.create_mutable_binding(
name.clone(),
false,
@ -328,11 +327,11 @@ impl Executor for Interpreter {
let mut new_func = Object::function();
new_func.set_call(func);
let val = to_value(new_func);
val.set_field_slice("length", to_value(args.len()));
let val = Value::from(new_func);
val.set_field_slice("length", Value::from(args.len()));
if let Some(name) = name {
val.set_field_slice("name", to_value(name.clone()));
val.set_field_slice("name", Value::from(name.clone()));
}
Ok(val)
@ -356,50 +355,49 @@ impl Executor for Interpreter {
let mut new_func = Object::function();
new_func.set_call(func);
let val = to_value(new_func);
val.set_field_slice("length", to_value(args.len()));
let val = Value::from(new_func);
val.set_field_slice("length", Value::from(args.len()));
Ok(val)
}
Node::BinOp(BinOp::Num(ref op), ref a, ref b) => {
let v_r_a = self.run(a)?;
let v_r_b = self.run(b)?;
let v_a = (*v_r_a).clone();
let v_b = (*v_r_b).clone();
Ok(Gc::new(match *op {
let v_a = self.run(a)?;
let v_b = self.run(b)?;
Ok(match *op {
NumOp::Add => v_a + v_b,
NumOp::Sub => v_a - v_b,
NumOp::Mul => v_a * v_b,
NumOp::Exp => v_a.as_num_to_power(v_b),
NumOp::Div => v_a / v_b,
NumOp::Mod => v_a % v_b,
}))
})
}
Node::UnaryOp(ref op, ref a) => {
let v_r_a = self.run(a)?;
let v_a = (*v_r_a).clone();
Ok(match op {
UnaryOp::Minus => to_value(-v_a.to_number()),
UnaryOp::Plus => to_value(v_a.to_number()),
let v_a = self.run(a)?;
Ok(match *op {
UnaryOp::Minus => Value::from(-v_a.to_number()),
UnaryOp::Plus => Value::from(v_a.to_number()),
UnaryOp::IncrementPost => {
self.set_value(a.deref(), to_value(v_a.to_number() + 1.0))?;
v_r_a
let ret = v_a.clone();
self.set_value(a, Value::from(v_a.to_number() + 1.0))?;
ret
}
UnaryOp::IncrementPre => {
self.set_value(a.deref(), to_value(v_a.to_number() + 1.0))?
self.set_value(a, Value::from(v_a.to_number() + 1.0))?
}
UnaryOp::DecrementPost => {
self.set_value(a.deref(), to_value(v_a.to_number() - 1.0))?;
v_r_a
let ret = v_a.clone();
self.set_value(a, Value::from(v_a.to_number() - 1.0))?;
ret
}
UnaryOp::DecrementPre => {
self.set_value(a.deref(), to_value(v_a.to_number() - 1.0))?
self.set_value(a, Value::from(v_a.to_number() - 1.0))?
}
UnaryOp::Not => Gc::new(!v_a),
UnaryOp::Not => !v_a,
UnaryOp::Tilde => {
let num_v_a = v_a.to_number();
// NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184
to_value(if num_v_a.is_nan() {
Value::from(if num_v_a.is_nan() {
-1
} else {
!(num_v_a as i32)
@ -409,11 +407,9 @@ impl Executor for Interpreter {
})
}
Node::BinOp(BinOp::Bit(ref op), ref a, ref b) => {
let v_r_a = self.run(a)?;
let v_r_b = self.run(b)?;
let v_a = (*v_r_a).clone();
let v_b = (*v_r_b).clone();
Ok(Gc::new(match *op {
let v_a = self.run(a)?;
let v_b = self.run(b)?;
Ok(match *op {
BitOp::And => v_a & v_b,
BitOp::Or => v_a | v_b,
BitOp::Xor => v_a ^ v_b,
@ -421,14 +417,14 @@ impl Executor for Interpreter {
BitOp::Shr => v_a >> v_b,
// TODO Fix
BitOp::UShr => v_a >> v_b,
}))
})
}
Node::BinOp(BinOp::Comp(ref op), ref a, ref b) => {
let mut v_r_a = self.run(a)?;
let mut v_r_b = self.run(b)?;
let mut v_a = v_r_a.borrow_mut();
let mut v_b = v_r_b.borrow_mut();
Ok(to_value(match *op {
Ok(Value::from(match *op {
CompOp::Equal if v_a.is_object() => v_r_a == v_r_b,
CompOp::Equal => v_a == v_b,
CompOp::NotEqual if v_a.is_object() => v_r_a != v_r_b,
@ -452,17 +448,16 @@ impl Executor for Interpreter {
}
Node::BinOp(BinOp::Log(ref op), ref a, ref b) => {
// turn a `Value` into a `bool`
let to_bool =
|val| from_value::<bool>(val).expect("Could not convert JS value to bool");
let to_bool = |value| bool::from(&value);
Ok(match *op {
LogOp::And => to_value(to_bool(self.run(a)?) && to_bool(self.run(b)?)),
LogOp::Or => to_value(to_bool(self.run(a)?) || to_bool(self.run(b)?)),
LogOp::And => Value::from(to_bool(self.run(a)?) && to_bool(self.run(b)?)),
LogOp::Or => Value::from(to_bool(self.run(a)?) || to_bool(self.run(b)?)),
})
}
Node::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.deref() {
Node::Local(ref name) => {
let v_a = (*self.realm.environment.get_binding_value(&name)).clone();
let v_b = (*self.run(b)?).clone();
let v_a = self.realm.environment.get_binding_value(&name);
let v_b = self.run(b)?;
let value = exec_assign_op(op, v_a, v_b);
self.realm
.environment
@ -471,15 +466,15 @@ impl Executor for Interpreter {
}
Node::GetConstField(ref obj, ref field) => {
let v_r_a = self.run(obj)?;
let v_a = (*v_r_a.borrow().get_field_slice(field)).clone();
let v_b = (*self.run(b)?).clone();
let v_a = v_r_a.get_field_slice(field);
let v_b = self.run(b)?;
let value = exec_assign_op(op, v_a, v_b);
v_r_a
.borrow()
.set_field_slice(&field.clone(), value.clone());
Ok(value)
}
_ => Ok(Gc::new(ValueData::Undefined)),
_ => Ok(Value::undefined()),
},
Node::New(ref call) => {
let (callee, args) = match call.as_ref() {
@ -492,7 +487,7 @@ impl Executor for Interpreter {
for arg in args.iter() {
v_args.push(self.run(arg)?);
}
let mut this = ValueData::new_obj(None);
let mut this = Value::new_object(None);
// Create a blank object, then set its __proto__ property to the [Constructor].prototype
this.borrow().set_internal_slot(
INSTANCE_PROTOTYPE,
@ -505,13 +500,13 @@ impl Executor for Interpreter {
.as_ref()
.unwrap()
.construct(&mut func_object.clone(), &v_args, self, &mut this),
_ => Ok(Gc::new(ValueData::Undefined)),
_ => Ok(Value::undefined()),
}
}
Node::Return(ref ret) => {
let result = match *ret {
Some(ref v) => self.run(v),
None => Ok(Gc::new(ValueData::Undefined)),
None => Ok(Value::undefined()),
};
// Set flag for return
self.is_return = true;
@ -556,7 +551,7 @@ impl Executor for Interpreter {
let (name, value) = var.clone();
let val = match value {
Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Undefined),
None => Value::undefined(),
};
self.realm.environment.create_mutable_binding(
name.clone(),
@ -565,14 +560,14 @@ impl Executor for Interpreter {
);
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
Ok(Value::undefined())
}
Node::LetDecl(ref vars) => {
for var in vars.iter() {
let (name, value) = var.clone();
let val = match value {
Some(v) => self.run(&v)?,
None => Gc::new(ValueData::Undefined),
None => Value::undefined(),
};
self.realm.environment.create_mutable_binding(
name.clone(),
@ -581,7 +576,7 @@ impl Executor for Interpreter {
);
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
Ok(Value::undefined())
}
Node::ConstDecl(ref vars) => {
for (name, value) in vars.iter() {
@ -593,11 +588,11 @@ impl Executor for Interpreter {
let val = self.run(&value)?;
self.realm.environment.initialize_binding(&name, val);
}
Ok(Gc::new(ValueData::Undefined))
Ok(Value::undefined())
}
Node::TypeOf(ref val_e) => {
let val = self.run(val_e)?;
Ok(to_value(match *val {
Ok(Value::from(match *val {
ValueData::Undefined => "undefined",
ValueData::Symbol(_) => "symbol",
ValueData::Null => "object",
@ -621,7 +616,7 @@ impl Executor for Interpreter {
)));
}
let mut obj = to_value(None::<()>);
let mut obj = Value::null();
for (i, item) in list.iter().enumerate() {
let val = self.run(item)?;
// early return
@ -641,7 +636,7 @@ impl Executor for Interpreter {
}
Node::Spread(ref node) => {
// TODO: for now we can do nothing but return the value as-is
Ok(Gc::new((*self.run(node)?).clone()))
self.run(node)
}
ref i => unimplemented!("{}", i),
}
@ -668,7 +663,7 @@ impl Interpreter {
Some(ref func) => func.call(&mut f.clone(), arguments_list, self, this),
None => panic!("Expected function"),
},
_ => Err(Gc::new(ValueData::Undefined)),
_ => Err(Value::undefined()),
}
}
@ -699,7 +694,7 @@ impl Interpreter {
}
}
Gc::new(ValueData::Undefined)
Value::undefined()
}
/// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType.
@ -734,17 +729,17 @@ impl Interpreter {
#[allow(clippy::wrong_self_convention)]
pub fn to_string(&mut self, value: &Value) -> Value {
match *value.deref().borrow() {
ValueData::Undefined => to_value("undefined"),
ValueData::Null => to_value("null"),
ValueData::Boolean(ref boolean) => to_value(boolean.to_string()),
ValueData::Rational(ref num) => to_value(num.to_string()),
ValueData::Integer(ref num) => to_value(num.to_string()),
ValueData::String(ref string) => to_value(string.clone()),
ValueData::Undefined => Value::from("undefined"),
ValueData::Null => Value::from("null"),
ValueData::Boolean(ref boolean) => Value::from(boolean.to_string()),
ValueData::Rational(ref num) => Value::from(num.to_string()),
ValueData::Integer(ref num) => Value::from(num.to_string()),
ValueData::String(ref string) => Value::from(string.clone()),
ValueData::Object(_) => {
let prim_value = self.to_primitive(&mut (value.clone()), Some("string"));
self.to_string(&prim_value)
}
_ => to_value("function(){...}"),
_ => Value::from("function(){...}"),
}
}
@ -779,7 +774,7 @@ impl Interpreter {
pub fn to_object(&mut self, value: &Value) -> ResultValue {
match *value.deref().borrow() {
ValueData::Undefined | ValueData::Integer(_) | ValueData::Null => {
Err(Gc::new(ValueData::Undefined))
Err(Value::undefined())
}
ValueData::Boolean(_) => {
let proto = self
@ -788,7 +783,7 @@ impl Interpreter {
.get_binding_value("Boolean")
.get_field_slice(PROTOTYPE);
let bool_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::Boolean);
let bool_obj = Value::new_object_from_prototype(proto, ObjectKind::Boolean);
bool_obj.set_internal_slot("BooleanData", value.clone());
Ok(bool_obj)
}
@ -798,7 +793,7 @@ impl Interpreter {
.environment
.get_binding_value("Number")
.get_field_slice(PROTOTYPE);
let number_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::Number);
let number_obj = Value::new_object_from_prototype(proto, ObjectKind::Number);
number_obj.set_internal_slot("NumberData", value.clone());
Ok(number_obj)
}
@ -808,7 +803,7 @@ impl Interpreter {
.environment
.get_binding_value("String")
.get_field_slice(PROTOTYPE);
let string_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::String);
let string_obj = Value::new_object_from_prototype(proto, ObjectKind::String);
string_obj.set_internal_slot("StringData", value.clone());
Ok(string_obj)
}
@ -861,15 +856,15 @@ impl Interpreter {
/// `extract_array_properties` converts an array object into a rust vector of Values.
/// This is useful for the spread operator, for any other object an `Err` is returned
fn extract_array_properties(&mut self, value: &Value) -> Result<Vec<Gc<ValueData>>, ()> {
fn extract_array_properties(&mut self, value: &Value) -> Result<Vec<Value>, ()> {
if let ValueData::Object(ref x) = *value.deref().borrow() {
// Check if object is array
if x.deref().borrow().kind == ObjectKind::Array {
let length: i32 =
self.value_to_rust_number(&value.get_field_slice("length")) as i32;
let values: Vec<Gc<ValueData>> = (0..length)
let values: Vec<Value> = (0..length)
.map(|idx| value.get_field_slice(&idx.to_string()))
.collect::<Vec<Value>>();
.collect();
return Ok(values);
}

6
boa/src/realm.rs

@ -8,7 +8,7 @@ use crate::{
builtins::{
self,
function::NativeFunctionData,
value::{ToValue, Value, ValueData},
value::{Value, ValueData},
},
environment::{
declarative_environment_record::DeclarativeEnvironmentRecord,
@ -34,7 +34,7 @@ impl Realm {
pub fn create() -> Self {
// Create brand new global object
// Global has no prototype to pass None to new_obj
let global = ValueData::new_obj(None);
let global = Value::new_object(None);
// We need to clone the global here because its referenced from separate places (only pointer is cloned)
let global_env = new_global_environment(global.clone(), global.clone());
@ -65,7 +65,7 @@ impl Realm {
crate::builtins::function::FunctionBody::BuiltIn(func),
);
self.global_obj
.set_field(func_name.to_value(), ValueData::from_func(func));
.set_field(Value::from(func_name), ValueData::from_func(func));
self
}

Loading…
Cancel
Save