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::{ builtins::{
object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE},
property::Property, property::Property,
value::{from_value, to_value, undefined, ResultValue, Value, ValueData}, value::{ResultValue, Value, ValueData},
}, },
exec::Interpreter, exec::Interpreter,
}; };
use gc::Gc;
use std::borrow::Borrow; use std::borrow::Borrow;
use std::cmp::{max, min}; use std::cmp::{max, min};
use std::ops::Deref; use std::ops::Deref;
/// Creates a new `Array` instance. /// Creates a new `Array` instance.
pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue { pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
let array = ValueData::new_obj(Some( let array = Value::new_object(Some(
&interpreter &interpreter
.get_realm() .get_realm()
.environment .environment
@ -44,7 +43,7 @@ pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue {
.borrow() .borrow()
.get_field_slice(PROTOTYPE), .get_field_slice(PROTOTYPE),
); );
array.borrow().set_field_slice("length", to_value(0)); array.borrow().set_field_slice("length", Value::from(0));
Ok(array) Ok(array)
} }
@ -56,20 +55,19 @@ pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultVal
let array_obj_ptr = array_obj.clone(); let array_obj_ptr = array_obj.clone();
// Wipe existing contents of the array object // Wipe existing contents of the array object
let orig_length: i32 = let orig_length = i32::from(&array_obj.get_field_slice("length"));
from_value(array_obj.get_field_slice("length")).expect("failed to convert length to i32");
for n in 0..orig_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 // Create length
let length = Property::new() let length = Property::new()
.value(to_value(array_contents.len() as i32)) .value(Value::from(array_contents.len() as i32))
.writable(true) .writable(true)
.configurable(false) .configurable(false)
.enumerable(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() { for (n, value) in array_contents.iter().enumerate() {
array_obj_ptr.set_field_slice(&n.to_string(), value.clone()); 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 /// Utility function which takes an existing array object and puts additional
/// values on the end, correctly rewriting the length /// values on the end, correctly rewriting the length
pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> ResultValue { pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> ResultValue {
let orig_length: i32 = let orig_length = i32::from(&array_ptr.get_field_slice("length"));
from_value(array_ptr.get_field_slice("length")).expect("failed to conveert lenth to i32");
for (n, value) in add_values.iter().enumerate() { for (n, value) in add_values.iter().enumerate() {
let new_index = orig_length.wrapping_add(n as i32); 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( array_ptr.set_field_slice(
"length", "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()) 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; let mut length = args.len() as i32;
match args.len() { match args.len() {
1 if args[0].is_integer() => { 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`. // 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 { 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() => { 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 // finally create length property
let length = Property::new() let length = Property::new()
.value(to_value(length)) .value(Value::from(length))
.writable(true) .writable(true)
.configurable(false) .configurable(false)
.enumerable(false); .enumerable(false);
this.set_prop("length".to_string(), length); this.set_property("length".to_string(), length);
Ok(this.clone()) 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 /// [spec]: https://tc39.es/ecma262/#sec-array.isarray
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray
pub fn is_array(_this: &mut Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue { pub fn is_array(_this: &mut Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue {
let value_true = Gc::new(ValueData::Boolean(true)); let value_true = Value::boolean(true);
let value_false = Gc::new(ValueData::Boolean(false)); let value_false = Value::boolean(false);
match args.get(0) { match args.get(0) {
Some(arg) => { Some(arg) => {
match *(*arg).clone() { match arg.data() {
// 1. // 1.
ValueData::Object(ref obj) => { ValueData::Object(ref obj) => {
// 2. // 2.
@ -202,15 +199,13 @@ pub fn concat(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa
// one) // one)
let mut new_values: Vec<Value> = Vec::new(); let mut new_values: Vec<Value> = Vec::new();
let this_length: i32 = let this_length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
for n in 0..this_length { for n in 0..this_length {
new_values.push(this.get_field_slice(&n.to_string())); new_values.push(this.get_field_slice(&n.to_string()));
} }
for concat_array in args { for concat_array in args {
let concat_length: i32 = from_value(concat_array.get_field_slice("length")) let concat_length = i32::from(&concat_array.get_field_slice("length"));
.expect("Could not convert argument to i32");
for n in 0..concat_length { for n in 0..concat_length {
new_values.push(concat_array.get_field_slice(&n.to_string())); 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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop
pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let curr_length: i32 = let curr_length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
if curr_length < 1 { if curr_length < 1 {
return Ok(Gc::new(ValueData::Undefined)); return Ok(Value::undefined());
} }
let pop_index = curr_length.wrapping_sub(1); let pop_index = curr_length.wrapping_sub(1);
let pop_value: Value = this.get_field_slice(&pop_index.to_string()); let pop_value: Value = this.get_field_slice(&pop_index.to_string());
this.remove_prop(&pop_index.to_string()); this.remove_property(&pop_index.to_string());
this.set_field_slice("length", to_value(pop_index)); this.set_field_slice("length", Value::from(pop_index));
Ok(pop_value) 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 /// [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 { pub fn for_each(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(Value::from("Missing argument for Array.prototype.forEach"));
"Missing argument for Array.prototype.forEach".to_string(),
));
} }
let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); let callback_arg = args.get(0).expect("Could not get `callbackFn` argument.");
let mut this_arg = args.get(1).cloned().unwrap_or_else(undefined); let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length: i32 = let length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
for i in 0..length { for i in 0..length {
let element = this.get_field_slice(&i.to_string()); 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)?; interpreter.call(callback_arg, &mut this_arg, &arguments)?;
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Value::undefined())
} }
/// `Array.prototype.join( separator )` /// `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 mut elem_strs: Vec<String> = Vec::new();
let length: i32 = let length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
for n in 0..length { for n in 0..length {
let elem_str: String = this.get_field_slice(&n.to_string()).to_string(); let elem_str: String = this.get_field_slice(&n.to_string()).to_string();
elem_strs.push(elem_str); elem_strs.push(elem_str);
} }
Ok(to_value(elem_strs.join(&separator))) Ok(Value::from(elem_strs.join(&separator)))
} }
/// `Array.prototype.toString( 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 /// [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 { pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue {
let method_name = "join"; let method_name = "join";
let mut arguments = vec![to_value(",")]; let mut arguments = vec![Value::from(",")];
// 2. // 2.
let mut method: Value = let mut method = this.get_field_slice(method_name);
from_value(this.get_field_slice(method_name)).expect("failed to get Array.prototype.join");
// 3. // 3.
if !method.is_function() { if !method.is_function() {
method = _ctx 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(PROTOTYPE)
.get_field_slice("toString"); .get_field_slice("toString");
method = from_value(method).expect("failed to get Object.prototype.toString");
arguments = Vec::new(); arguments = Vec::new();
} }
// 4. // 4.
@ -361,7 +349,7 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R
}, },
Err(v) => format!("error: {}", v), Err(v) => format!("error: {}", v),
}; };
Ok(to_value(match_string)) Ok(Value::from(match_string))
} }
/// `Array.prototype.reverse()` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse
#[allow(clippy::else_if_without_else)] #[allow(clippy::else_if_without_else)]
pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = let len = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let middle: i32 = len.wrapping_div(2); let middle: i32 = len.wrapping_div(2);
for lower in 0..middle { 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); this.set_field_slice(&lower.to_string(), upper_value);
} else if upper_exists { } else if upper_exists {
this.set_field_slice(&lower.to_string(), upper_value); 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 { } else if lower_exists {
this.set_field_slice(&upper.to_string(), lower_value); 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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift
pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = let len = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
if len == 0 { 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 // Since length is 0, this will be an Undefined value
return Ok(this.get_field_slice(&0.to_string())); 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 to = (k.wrapping_sub(1)).to_string();
let from_value = this.get_field_slice(&from); let from_value = this.get_field_slice(&from);
if from_value == Gc::new(ValueData::Undefined) { if from_value == Value::undefined() {
this.remove_prop(&to); this.remove_property(&to);
} else { } else {
this.set_field_slice(&to, from_value); this.set_field_slice(&to, from_value);
} }
} }
let final_index = len.wrapping_sub(1); let final_index = len.wrapping_sub(1);
this.remove_prop(&(final_index).to_string()); this.remove_property(&(final_index).to_string());
this.set_field_slice("length", to_value(final_index)); this.set_field_slice("length", Value::from(final_index));
Ok(first) 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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift
pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let len: i32 = let len = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let arg_c: i32 = args.len() as i32; let arg_c: i32 = args.len() as i32;
if arg_c > 0 { 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 to = (k.wrapping_add(arg_c).wrapping_sub(1)).to_string();
let from_value = this.get_field_slice(&from); let from_value = this.get_field_slice(&from);
if from_value == Gc::new(ValueData::Undefined) { if from_value == Value::undefined() {
this.remove_prop(&to); this.remove_property(&to);
} else { } else {
this.set_field_slice(&to, from_value); 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); let temp = len.wrapping_add(arg_c);
this.set_field_slice("length", to_value(temp)); this.set_field_slice("length", Value::from(temp));
Ok(to_value(temp)) Ok(Value::from(temp))
} }
/// `Array.prototype.every( callback, [ thisArg ] )` /// `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 /// [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 { pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(Value::from(
"missing callback when calling function Array.prototype.every".to_string(), "missing callback when calling function Array.prototype.every",
)); ));
} }
let callback = &args[0]; let callback = &args[0];
let mut this_arg = if args.len() > 1 { let mut this_arg = if args.len() > 1 {
args[1].clone() args[1].clone()
} else { } else {
Gc::new(ValueData::Undefined) Value::undefined()
}; };
let mut i = 0; 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; let mut len = max_len;
while i < len { while i < len {
let element = this.get_field_slice(&i.to_string()); 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 let result = interpreter
.call(callback, &mut this_arg, &arguments)? .call(callback, &mut this_arg, &arguments)?
.is_true(); .is_true();
if !result { 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; i += 1;
} }
Ok(to_value(true)) Ok(Value::from(true))
} }
/// `Array.prototype.map( callback, [ thisArg ] )` /// `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 /// [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 { pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(Value::from(
"missing argument 0 when calling function Array.prototype.map", "missing argument 0 when calling function Array.prototype.map",
)); ));
} }
let callback = args.get(0).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(undefined); let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length: i32 = let length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
let new = new_array(&interpreter)?; let new = new_array(&interpreter)?;
let values = (0..length) let values: Vec<Value> = (0..length)
.map(|idx| { .map(|idx| {
let element = this.get_field_slice(&idx.to_string()); 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 interpreter
.call(&callback, &mut this_val, &args) .call(&callback, &mut this_val, &args)
.unwrap_or_else(|_| undefined()) .unwrap_or_else(|_| Value::undefined())
}) })
.collect::<Vec<Value>>(); .collect();
construct_array(&new, &values) 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 { 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 no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
return Ok(to_value(-1)); return Ok(Value::from(-1));
} }
let search_element = args[0].clone(); let search_element = args[0].clone();
let len: i32 = from_value(this.get_field_slice("length")) let len = i32::from(&this.get_field_slice("length"));
.expect("Expected array property \"length\" is not set.");
let mut idx = match args.get(1) { let mut idx = match args.get(1) {
Some(from_idx_ptr) => { Some(from_idx_ptr) => {
let from_idx = from_value(from_idx_ptr.clone()) let from_idx = i32::from(from_idx_ptr);
.expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument");
if from_idx < 0 { if from_idx < 0 {
len + from_idx 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(); let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element { if check_element == search_element {
return Ok(to_value(idx)); return Ok(Value::from(idx));
} }
idx += 1; idx += 1;
} }
Ok(to_value(-1)) Ok(Value::from(-1))
} }
/// `Array.prototype.lastIndexOf( searchElement[, fromIndex ] )` /// `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 { 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 no arguments, return -1. Not described in spec, but is what chrome does.
if args.is_empty() { if args.is_empty() {
return Ok(to_value(-1)); return Ok(Value::from(-1));
} }
let search_element = args[0].clone(); let search_element = args[0].clone();
let len: i32 = from_value(this.get_field_slice("length")) let len = i32::from(&this.get_field_slice("length"));
.expect("Expected array property \"length\" is not set.");
let mut idx = match args.get(1) { let mut idx = match args.get(1) {
Some(from_idx_ptr) => { Some(from_idx_ptr) => {
let from_idx = from_value(from_idx_ptr.clone()) let from_idx = i32::from(from_idx_ptr);
.expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument");
if from_idx >= 0 { if from_idx >= 0 {
min(from_idx, len - 1) 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(); let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element { if check_element == search_element {
return Ok(to_value(idx)); return Ok(Value::from(idx));
} }
idx -= 1; idx -= 1;
} }
Ok(to_value(-1)) Ok(Value::from(-1))
} }
/// `Array.prototype.find( callback, [thisArg] )` /// `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 /// [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 { pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(Value::from(
"missing callback when calling function Array.prototype.find".to_string(), "missing callback when calling function Array.prototype.find",
)); ));
} }
let callback = &args[0]; let callback = &args[0];
let mut this_arg = if args.len() > 1 { let mut this_arg = if args.len() > 1 {
args[1].clone() args[1].clone()
} else { } 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 { for i in 0..len {
let element = this.get_field_slice(&i.to_string()); 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)?; let result = interpreter.call(callback, &mut this_arg, &arguments)?;
if result.is_true() { if result.is_true() {
return Ok(element); return Ok(element);
} }
} }
Ok(Gc::new(ValueData::Undefined)) Ok(Value::undefined())
} }
/// `Array.prototype.findIndex( predicate [ , thisArg ] )` /// `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 /// [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 { pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(Value::from(
"Missing argument for Array.prototype.findIndex".to_string(), "Missing argument for Array.prototype.findIndex",
)); ));
} }
let predicate_arg = args.get(0).expect("Could not get `predicate` argument."); let predicate_arg = args.get(0).expect("Could not get `predicate` argument.");
let mut this_arg = args let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined);
.get(1)
.cloned()
.unwrap_or_else(|| Gc::new(ValueData::Undefined));
let length: i32 = let length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
for i in 0..length { for i in 0..length {
let element = this.get_field_slice(&i.to_string()); 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)?; let result = interpreter.call(predicate_arg, &mut this_arg, &arguments)?;
if result.is_true() { 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]] )` /// `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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill
pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 len: i32 = i32::from(&this.get_field_slice("length"));
let default_value = undefined(); let default_value = Value::undefined();
let value = args.get(0).unwrap_or(&default_value); let value = args.get(0).unwrap_or(&default_value);
let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32; let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32;
let relative_end_val = args.get(2).unwrap_or(&default_value); let relative_end_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 /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes
pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let search_element = args let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined);
.get(0)
.cloned()
.unwrap_or_else(|| Gc::new(ValueData::Undefined));
let length: i32 = let length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
for idx in 0..length { for idx in 0..length {
let check_element = this.get_field_slice(&idx.to_string()).clone(); let check_element = this.get_field_slice(&idx.to_string()).clone();
if check_element == search_element { 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]] )` /// `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 /// [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 { pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
let new_array = new_array(interpreter)?; let new_array = new_array(interpreter)?;
let len: i32 = let len = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not convert argument to i32");
let start = match args.get(0) { 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, None => 0,
}; };
let end = match args.get(1) { 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, 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_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) 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 /// [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 { pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(Value::from(
"missing argument 0 when calling function Array.prototype.filter", "missing argument 0 when calling function Array.prototype.filter",
)); ));
} }
let callback = args.get(0).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(undefined); let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined);
let length: i32 = let length = i32::from(&this.get_field_slice("length"));
from_value(this.get_field_slice("length")).expect("Could not get `length` property.");
let new = new_array(&interpreter)?; let new = new_array(&interpreter)?;
@ -915,11 +885,11 @@ pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -
.filter_map(|idx| { .filter_map(|idx| {
let element = this.get_field_slice(&idx.to_string()); 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 let callback_result = interpreter
.call(&callback, &mut this_val, &args) .call(&callback, &mut this_val, &args)
.unwrap_or_else(|_| undefined()); .unwrap_or_else(|_| Value::undefined());
if callback_result.is_true() { if callback_result.is_true() {
Some(element) 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 /// [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 { pub fn some(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue {
if args.is_empty() { if args.is_empty() {
return Err(to_value( return Err(Value::from(
"missing callback when calling function Array.prototype.some".to_string(), "missing callback when calling function Array.prototype.some",
)); ));
} }
let callback = &args[0]; let callback = &args[0];
let mut this_arg = if args.len() > 1 { let mut this_arg = if args.len() > 1 {
args[1].clone() args[1].clone()
} else { } else {
Gc::new(ValueData::Undefined) Value::undefined()
}; };
let mut i = 0; 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; let mut len = max_len;
while i < len { while i < len {
let element = this.get_field_slice(&i.to_string()); 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 let result = interpreter
.call(callback, &mut this_arg, &arguments)? .call(callback, &mut this_arg, &arguments)?
.is_true(); .is_true();
if result { 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. // 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; i += 1;
} }
Ok(to_value(false)) Ok(Value::from(false))
} }
/// Create a new `Array` object. /// Create a new `Array` object.
pub fn create(global: &Value) -> Value { pub fn create(global: &Value) -> Value {
// Create prototype // Create prototype
let prototype = ValueData::new_obj(None); let prototype = Value::new_object(None);
let length = Property::default().value(to_value(0_i32)); 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!(concat, named "concat", with length 1, of prototype);
make_builtin_fn!(push, named "push", 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::{ use crate::{
builtins::{ builtins::{
object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE}, object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
value::{to_value, ResultValue, Value, ValueData}, value::{ResultValue, Value, ValueData},
}, },
exec::Interpreter, 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) { if let Some(ref value) = args.get(0) {
this.set_internal_slot("BooleanData", to_boolean(value)); this.set_internal_slot("BooleanData", to_boolean(value));
} else { } 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 // 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 // Get the argument, if any
match args.get(0) { match args.get(0) {
Some(ref value) => Ok(to_boolean(value)), 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 /// [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 { pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let b = this_boolean_value(this); 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. /// 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 /// Creates a new boolean value from the input
pub fn to_boolean(value: &Value) -> Value { pub fn to_boolean(value: &Value) -> Value {
match *value.deref().borrow() { match *value.deref().borrow() {
ValueData::Object(_) => to_value(true), ValueData::Object(_) => Value::from(true),
ValueData::String(ref s) if !s.is_empty() => to_value(true), ValueData::String(ref s) if !s.is_empty() => Value::from(true),
ValueData::Rational(n) if n != 0.0 && !n.is_nan() => to_value(true), ValueData::Rational(n) if n != 0.0 && !n.is_nan() => Value::from(true),
ValueData::Integer(n) if n != 0 => to_value(true), ValueData::Integer(n) if n != 0 => Value::from(true),
ValueData::Boolean(v) => to_value(v), ValueData::Boolean(v) => Value::from(v),
_ => to_value(false), _ => Value::from(false),
} }
} }
@ -92,9 +92,9 @@ pub fn to_boolean(value: &Value) -> Value {
/// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue
pub fn this_boolean_value(value: &Value) -> Value { pub fn this_boolean_value(value: &Value) -> Value {
match *value.deref().borrow() { 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"), 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 { pub fn create(global: &Value) -> Value {
// Create Prototype // Create Prototype
// https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object // https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object
let prototype = ValueData::new_obj(Some(global)); let prototype = Value::new_object(Some(global));
prototype.set_internal_slot("BooleanData", to_boolean(&to_value(false))); prototype.set_internal_slot("BooleanData", to_boolean(&Value::from(false)));
make_builtin_fn!(to_string, named "toString", of prototype); make_builtin_fn!(to_string, named "toString", of prototype);
make_builtin_fn!(value_of, named "valueOf", 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] #[test]
fn check_boolean_constructor_is_function() { fn check_boolean_constructor_is_function() {
let global = ValueData::new_obj(None); let global = Value::new_object(None);
let boolean_constructor = create(&global); let boolean_constructor = create(&global);
assert_eq!(boolean_constructor.is_function(), true); assert_eq!(boolean_constructor.is_function(), true);
} }

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

@ -19,9 +19,7 @@ mod tests;
use crate::{ use crate::{
builtins::{ builtins::{
object::InternalState, object::InternalState,
value::{ value::{display_obj, ResultValue, Value},
display_obj, from_value, to_value, undefined, FromValue, ResultValue, Value, ValueData,
},
}, },
exec::Interpreter, exec::Interpreter,
}; };
@ -48,10 +46,11 @@ pub enum LogMessage {
} }
/// Helper function that returns the argument at a specified index. /// Helper function that returns the argument at a specified index.
fn get_arg_at_index<T: FromValue + Default>(args: &[Value], index: usize) -> Option<T> { fn get_arg_at_index<'a, T>(args: &'a [Value], index: usize) -> Option<T>
args.get(index) where
.cloned() T: From<&'a Value> + Default,
.map(|s| from_value::<T>(s).expect("Convert error")) {
args.get(index).map(|s| T::from(s))
} }
/// Helper function for logging messages. /// 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 mut args: Vec<Value> = args.iter().skip(1).cloned().collect();
let message = "Assertion failed".to_string(); let message = "Assertion failed".to_string();
if args.is_empty() { if args.is_empty() {
args.push(to_value::<String>(message)); args.push(Value::from(message));
} else if !args[0].is_string() { } else if !args[0].is_string() {
args.insert(0, to_value::<String>(message)); args.insert(0, Value::from(message));
} else { } else {
let concat = format!("{}: {}", message, args[0]); let concat = format!("{}: {}", message, args[0]);
args[0] = to_value::<String>(concat); args[0] = Value::from(concat);
} }
this.with_internal_state_ref(|state| { 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()` /// `console.clear()`
@ -178,7 +177,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue
state.groups.clear(); state.groups.clear();
}); });
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.debug(...data)` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug
pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.error(...data)` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error
pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state));
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.info(...data)` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info
pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state));
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.log(...data)` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log
pub fn log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state));
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.trace(...data)` /// `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)` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn
pub fn warn(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn warn(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state)); this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state));
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.count(label)` /// `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); logger(LogMessage::Info(format!("{} {}", msg, c)), state);
}); });
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.countReset(label)` /// `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); logger(LogMessage::Warn(format!("countReset {}", label)), state);
}); });
Ok(undefined()) Ok(Value::undefined())
} }
/// Returns current system time in ms. /// 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)` /// `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)` /// `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)` /// `console.group(...data)`
@ -445,7 +444,7 @@ pub fn group(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal
state.groups.push(group_label); state.groups.push(group_label);
}); });
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.groupEnd(label)` /// `console.groupEnd(label)`
@ -463,7 +462,7 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa
state.groups.pop(); state.groups.pop();
}); });
Ok(undefined()) Ok(Value::undefined())
} }
/// `console.dir(item, options)` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir
pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
this.with_internal_state_mut(|state: &mut ConsoleState| { this.with_internal_state_mut(|state: &mut ConsoleState| {
let undefined = Value::undefined();
logger( logger(
LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined()), true)), LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined), true)),
state, state,
); );
}); });
Ok(undefined()) Ok(Value::undefined())
} }
/// Create a new `console` object /// Create a new `console` object
pub fn create(global: &Value) -> Value { 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!(assert, named "assert", of console);
make_builtin_fn!(clear, named "clear", 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 crate::builtins::{console::formatter, value::Value};
use gc::Gc;
#[test] #[test]
fn formatter_no_args_is_empty_string() { fn formatter_no_args_is_empty_string() {
@ -8,14 +7,14 @@ fn formatter_no_args_is_empty_string() {
#[test] #[test]
fn formatter_empty_format_string_is_empty_string() { 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]); let res = formatter(&[val]);
assert_eq!(res, ""); assert_eq!(res, "");
} }
#[test] #[test]
fn formatter_format_without_args_renders_verbatim() { 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); let res = formatter(&val);
assert_eq!(res, "%d %s %% %f"); assert_eq!(res, "%d %s %% %f");
} }
@ -23,9 +22,9 @@ fn formatter_format_without_args_renders_verbatim() {
#[test] #[test]
fn formatter_empty_format_string_concatenates_rest_of_args() { fn formatter_empty_format_string_concatenates_rest_of_args() {
let val = [ let val = [
Gc::new(ValueData::String("".to_string())), Value::string("".to_string()),
Gc::new(ValueData::String("to powinno zostać".to_string())), Value::string("to powinno zostać".to_string()),
Gc::new(ValueData::String("połączone".to_string())), Value::string("połączone".to_string()),
]; ];
let res = formatter(&val); let res = formatter(&val);
assert_eq!(res, " to powinno zostać połączone"); assert_eq!(res, " to powinno zostać połączone");
@ -34,12 +33,10 @@ fn formatter_empty_format_string_concatenates_rest_of_args() {
#[test] #[test]
fn formatter_utf_8_checks() { fn formatter_utf_8_checks() {
let val = [ let val = [
Gc::new(ValueData::String( Value::string("Są takie chwile %dą %są tu%sów %привет%ź".to_string()),
"Są takie chwile %dą %są tu%sów %привет%ź".to_string(), Value::integer(123),
)), Value::rational(1.23),
Gc::new(ValueData::Integer(123)), Value::string("ł".to_string()),
Gc::new(ValueData::Rational(1.23)),
Gc::new(ValueData::String("ł".to_string())),
]; ];
let res = formatter(&val); let res = formatter(&val);
assert_eq!(res, "Są takie chwile 123ą 1.23ą tułów %привет%ź"); assert_eq!(res, "Są takie chwile 123ą 1.23ą tułów %привет%ź");
@ -48,8 +45,8 @@ fn formatter_utf_8_checks() {
#[test] #[test]
fn formatter_trailing_format_leader_renders() { fn formatter_trailing_format_leader_renders() {
let val = [ let val = [
Gc::new(ValueData::String("%%%%%".to_string())), Value::string("%%%%%".to_string()),
Gc::new(ValueData::String("|".to_string())), Value::string("|".to_string()),
]; ];
let res = formatter(&val); let res = formatter(&val);
assert_eq!(res, "%%% |") assert_eq!(res, "%%% |")
@ -58,10 +55,7 @@ fn formatter_trailing_format_leader_renders() {
#[test] #[test]
#[allow(clippy::approx_constant)] #[allow(clippy::approx_constant)]
fn formatter_float_format_works() { fn formatter_float_format_works() {
let val = [ let val = [Value::string("%f".to_string()), Value::rational(3.1415)];
Gc::new(ValueData::String("%f".to_string())),
Gc::new(ValueData::Rational(3.1415)),
];
let res = formatter(&val); let res = formatter(&val);
assert_eq!(res, "3.141500") assert_eq!(res, "3.141500")
} }

14
boa/src/builtins/error.rs

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

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

@ -16,7 +16,7 @@ use crate::{
array, array,
object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE}, object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE},
property::Property, property::Property,
value::{to_value, undefined, ResultValue, Value, ValueData}, value::{ResultValue, Value},
}, },
environment::lexical_environment::{new_function_environment, Environment}, environment::lexical_environment::{new_function_environment, Environment},
exec::Executor, exec::Executor,
@ -329,10 +329,10 @@ pub fn create_function_prototype() {
pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
let len = arguments_list.len(); let len = arguments_list.len();
let mut obj = Object::default(); let mut obj = Object::default();
obj.set_internal_slot("ParameterMap", undefined()); obj.set_internal_slot("ParameterMap", Value::undefined());
// Set length // Set length
let mut length = Property::default(); 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 // Define length as a property
obj.define_own_property("length".to_string(), length); obj.define_own_property("length".to_string(), length);
let mut index: usize = 0; let mut index: usize = 0;
@ -349,7 +349,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value {
index += 1; index += 1;
} }
to_value(obj) Value::from(obj)
} }
/// Create new function `[[Construct]]` /// 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 { 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) 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 //! [json]: https://www.json.org/json-en.html
//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON //! [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 crate::exec::Interpreter;
use serde_json::{self, Value as JSONValue}; use serde_json::{self, Value as JSONValue};
@ -41,8 +41,8 @@ pub fn parse(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
.clone() .clone()
.to_string(), .to_string(),
) { ) {
Ok(json) => Ok(to_value(json)), Ok(json) => Ok(Value::from(json)),
Err(err) => Err(to_value(err.to_string())), 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 { pub fn stringify(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("cannot get argument for JSON.stringify"); let obj = args.get(0).expect("cannot get argument for JSON.stringify");
let json = obj.to_json().to_string(); let json = obj.to_json().to_string();
Ok(to_value(json)) Ok(Value::from(json))
} }
/// Create a new `JSON` object. /// Create a new `JSON` object.
pub fn create(global: &Value) -> Value { 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!(parse, named "parse", with length 2, of json);
make_builtin_fn!(stringify, named "stringify", with length 3, 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. /// 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 //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math
use crate::{ use crate::{
builtins::value::{from_value, to_value, ResultValue, Value, ValueData}, builtins::value::{ResultValue, Value},
exec::Interpreter, exec::Interpreter,
}; };
use rand::random; use rand::random;
@ -30,12 +30,10 @@ mod tests;
/// [spec]: https://tc39.es/ecma262/#sec-math.abs /// [spec]: https://tc39.es/ecma262/#sec-math.abs
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs
pub fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).abs()
.expect("Could not convert argument to f64")
.abs()
})) }))
} }
@ -48,12 +46,10 @@ pub fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.acos /// [spec]: https://tc39.es/ecma262/#sec-math.acos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos
pub fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).acos()
.expect("Could not convert argument to f64")
.acos()
})) }))
} }
@ -66,12 +62,10 @@ pub fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.acosh /// [spec]: https://tc39.es/ecma262/#sec-math.acosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh
pub fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).acosh()
.expect("Could not convert argument to f64")
.acosh()
})) }))
} }
@ -84,12 +78,10 @@ pub fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.asin /// [spec]: https://tc39.es/ecma262/#sec-math.asin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin
pub fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).asin()
.expect("Could not convert argument to f64")
.asin()
})) }))
} }
@ -102,12 +94,10 @@ pub fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.asinh /// [spec]: https://tc39.es/ecma262/#sec-math.asinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh
pub fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).asinh()
.expect("Could not convert argument to f64")
.asinh()
})) }))
} }
@ -120,12 +110,10 @@ pub fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.atan /// [spec]: https://tc39.es/ecma262/#sec-math.atan
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan
pub fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).atan()
.expect("Could not convert argument to f64")
.atan()
})) }))
} }
@ -138,12 +126,10 @@ pub fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.atanh /// [spec]: https://tc39.es/ecma262/#sec-math.atanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh
pub fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).atanh()
.expect("Could not convert argument to f64")
.atanh()
})) }))
} }
@ -156,11 +142,10 @@ pub fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.atan2 /// [spec]: https://tc39.es/ecma262/#sec-math.atan2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2
pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument"))
.expect("Could not convert argument to f64")
.atan2(args.get(1).expect("Could not get argument").to_number()) .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 /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt
pub fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).cbrt()
.expect("Could not convert argument to f64")
.cbrt()
})) }))
} }
@ -192,12 +175,10 @@ pub fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.ceil /// [spec]: https://tc39.es/ecma262/#sec-math.ceil
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil
pub fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).ceil()
.expect("Could not convert argument to f64")
.ceil()
})) }))
} }
@ -210,12 +191,10 @@ pub fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.cos /// [spec]: https://tc39.es/ecma262/#sec-math.cos
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos
pub fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).cos()
.expect("Could not convert argument to f64")
.cos()
})) }))
} }
@ -228,12 +207,10 @@ pub fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.cosh /// [spec]: https://tc39.es/ecma262/#sec-math.cosh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh
pub fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).cosh()
.expect("Could not convert argument to f64")
.cosh()
})) }))
} }
@ -246,12 +223,10 @@ pub fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.exp /// [spec]: https://tc39.es/ecma262/#sec-math.exp
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp
pub fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).exp()
.expect("Could not convert argument to f64")
.exp()
})) }))
} }
@ -264,12 +239,10 @@ pub fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.floor /// [spec]: https://tc39.es/ecma262/#sec-math.floor
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor
pub fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).floor()
.expect("Could not convert argument to f64")
.floor()
})) }))
} }
@ -282,11 +255,10 @@ pub fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.log /// [spec]: https://tc39.es/ecma262/#sec-math.log
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log
pub fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone()) let value = f64::from(args.get(0).expect("Could not get argument"));
.expect("Could not convert argument to f64");
if value <= 0.0 { if value <= 0.0 {
f64::NAN f64::NAN
@ -305,11 +277,10 @@ pub fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.log10 /// [spec]: https://tc39.es/ecma262/#sec-math.log10
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10
pub fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone()) let value = f64::from(args.get(0).expect("Could not get argument"));
.expect("Could not convert argument to f64");
if value <= 0.0 { if value <= 0.0 {
f64::NAN f64::NAN
@ -328,11 +299,10 @@ pub fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.log2 /// [spec]: https://tc39.es/ecma262/#sec-math.log2
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2
pub fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone()) let value = f64::from(args.get(0).expect("Could not get argument"));
.expect("Could not convert argument to f64");
if value <= 0.0 { if value <= 0.0 {
f64::NAN 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 { pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::NEG_INFINITY; let mut max = f64::NEG_INFINITY;
for arg in args { for arg in args {
let num = arg.to_number(); let num = f64::from(arg);
max = max.max(num); max = max.max(num);
} }
Ok(to_value(max)) Ok(Value::from(max))
} }
/// Get the minimum of several numbers. /// 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 { pub fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let mut max = f64::INFINITY; let mut max = f64::INFINITY;
for arg in args { for arg in args {
let num = arg.to_number(); let num = f64::from(arg);
max = max.min(num); max = max.min(num);
} }
Ok(to_value(max)) Ok(Value::from(max))
} }
/// Raise a number to a power. /// 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 /// [spec]: https://tc39.es/ecma262/#sec-math.pow
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow
pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(if args.len() >= 2 { Ok(Value::from(if args.len() >= 2 {
let num: f64 = from_value(args.get(0).expect("Could not get argument").clone()) let num = f64::from(args.get(0).expect("Could not get argument"));
.expect("Could not convert argument to f64"); let power = f64::from(args.get(1).expect("Could not get argument"));
let power: f64 = from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert argument to f64");
num.powf(power) num.powf(power)
} else { } else {
f64::NAN f64::NAN
@ -405,7 +373,7 @@ pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.random /// [spec]: https://tc39.es/ecma262/#sec-math.random
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random
pub fn _random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { 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. /// 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 /// [spec]: https://tc39.es/ecma262/#sec-math.round
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round
pub fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).round()
.expect("Could not convert argument to f64")
.round()
})) }))
} }
@ -435,11 +401,10 @@ pub fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue
/// [spec]: https://tc39.es/ecma262/#sec-math.sign /// [spec]: https://tc39.es/ecma262/#sec-math.sign
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign
pub fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
let value = from_value::<f64>(args.get(0).expect("Could not get argument").clone()) let value = f64::from(args.get(0).expect("Could not get argument"));
.expect("Could not convert argument to f64");
if value == 0.0 || value == -0.0 { if value == 0.0 || value == -0.0 {
value value
@ -458,12 +423,10 @@ pub fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.sin /// [spec]: https://tc39.es/ecma262/#sec-math.sin
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin
pub fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).sin()
.expect("Could not convert argument to f64")
.sin()
})) }))
} }
@ -476,12 +439,10 @@ pub fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.sinh /// [spec]: https://tc39.es/ecma262/#sec-math.sinh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh
pub fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).sinh()
.expect("Could not convert argument to f64")
.sinh()
})) }))
} }
@ -494,22 +455,18 @@ pub fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.sqrt /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt
pub fn sqrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).sqrt()
.expect("Could not convert argument to f64")
.sqrt()
})) }))
} }
/// Get the tangent of a number /// Get the tangent of a number
pub fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).tan()
.expect("Could not convert argument to f64")
.tan()
})) }))
} }
@ -522,12 +479,10 @@ pub fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.tanh /// [spec]: https://tc39.es/ecma262/#sec-math.tanh
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh
pub fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).tanh()
.expect("Could not convert argument to f64")
.tanh()
})) }))
} }
@ -540,27 +495,25 @@ pub fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
/// [spec]: https://tc39.es/ecma262/#sec-math.trunc /// [spec]: https://tc39.es/ecma262/#sec-math.trunc
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc
pub fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { 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 f64::NAN
} else { } else {
from_value::<f64>(args.get(0).expect("Could not get argument").clone()) f64::from(args.get(0).expect("Could not get argument")).trunc()
.expect("Could not convert argument to f64")
.trunc()
})) }))
} }
/// Create a new `Math` object /// Create a new `Math` object
pub fn create(global: &Value) -> Value { pub fn create(global: &Value) -> Value {
let math = ValueData::new_obj(Some(global)); let math = Value::new_object(Some(global));
math.set_field_slice("E", to_value(f64::consts::E)); math.set_field_slice("E", Value::from(f64::consts::E));
math.set_field_slice("LN2", to_value(f64::consts::LN_2)); math.set_field_slice("LN2", Value::from(f64::consts::LN_2));
math.set_field_slice("LN10", to_value(f64::consts::LN_10)); math.set_field_slice("LN10", Value::from(f64::consts::LN_10));
math.set_field_slice("LOG2E", to_value(f64::consts::LOG2_E)); math.set_field_slice("LOG2E", Value::from(f64::consts::LOG2_E));
math.set_field_slice("LOG10E", to_value(f64::consts::LOG10_E)); math.set_field_slice("LOG10E", Value::from(f64::consts::LOG10_E));
math.set_field_slice("SQRT1_2", to_value(0.5_f64.sqrt())); math.set_field_slice("SQRT1_2", Value::from(0.5_f64.sqrt()));
math.set_field_slice("SQRT2", to_value(f64::consts::SQRT_2)); math.set_field_slice("SQRT2", Value::from(f64::consts::SQRT_2));
math.set_field_slice("PI", to_value(f64::consts::PI)); math.set_field_slice("PI", Value::from(f64::consts::PI));
make_builtin_fn!(abs, named "abs", with length 1, of math); 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!(acos, named "acos", with length 1, of math);
make_builtin_fn!(acosh, named "acosh", 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(); let mut new_func = crate::builtins::object::Object::function();
new_func.set_call(func); new_func.set_call(func);
let new_func_obj = to_value(new_func); let new_func_obj = Value::from(new_func);
new_func_obj.set_field_slice("length", to_value($l)); new_func_obj.set_field_slice("length", Value::from($l));
$p.set_field_slice($name, new_func_obj); $p.set_field_slice($name, new_func_obj);
}; };
($fn:ident, named $name:expr, of $p:ident) => { ($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_construct(constructor_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype); 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 // Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone()); $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_construct(construct_fn);
constructor_obj.set_call(call_fn); constructor_obj.set_call(call_fn);
constructor_obj.set_internal_slot("__proto__", func_prototype); 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 // Set proto.constructor -> constructor_obj
$proto.set_field_slice("constructor", constructor_val.clone()); $proto.set_field_slice("constructor", constructor_val.clone());

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

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

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

@ -1,10 +1,10 @@
#![allow(clippy::float_cmp)] #![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] #[test]
fn check_number_constructor_is_function() { fn check_number_constructor_is_function() {
let global = ValueData::new_obj(None); let global = Value::new_object(None);
let number_constructor = super::create(&global); let number_constructor = super::create(&global);
assert_eq!(number_constructor.is_function(), true); assert_eq!(number_constructor.is_function(), true);
} }

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

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

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

@ -17,11 +17,11 @@ use crate::{
builtins::{ builtins::{
function::Function, function::Function,
property::Property, property::Property,
value::{from_value, same_value, to_value, undefined, ResultValue, Value, ValueData}, value::{same_value, ResultValue, Value, ValueData},
}, },
exec::Interpreter, exec::Interpreter,
}; };
use gc::{unsafe_empty_trace, Finalize, Gc, GcCell, Trace}; use gc::{unsafe_empty_trace, Finalize, Trace};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::{ use std::{
borrow::Borrow, borrow::Borrow,
@ -103,7 +103,7 @@ impl ObjectInternalMethods for Object {
while !done { while !done {
if p.is_null() { if p.is_null() {
done = true done = true
} else if same_value(&to_value(self.clone()), &p, false) { } else if same_value(&Value::from(self.clone()), &p, false) {
return false; return false;
} else { } else {
p = p.get_internal_slot(PROTOTYPE); p = p.get_internal_slot(PROTOTYPE);
@ -132,7 +132,7 @@ impl ObjectInternalMethods for Object {
fn get_internal_slot(&self, name: &str) -> Value { fn get_internal_slot(&self, name: &str) -> Value {
match self.internal_slots.get(name) { match self.internal_slots.get(name) {
Some(v) => v.clone(), 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 /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc
#[allow(clippy::option_unwrap_used)] #[allow(clippy::option_unwrap_used)]
fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { 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(); let extensible = self.is_extensible();
// https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor
@ -346,7 +346,7 @@ impl Object {
construct: None, construct: None,
}; };
object.set_internal_slot("extensible", to_value(true)); object.set_internal_slot("extensible", Value::from(true));
object object
} }
@ -362,7 +362,7 @@ impl Object {
construct: None, construct: None,
}; };
object.set_internal_slot("extensible", to_value(true)); object.set_internal_slot("extensible", Value::from(true));
object object
} }
@ -378,7 +378,7 @@ impl Object {
obj.internal_slots obj.internal_slots
.insert(INSTANCE_PROTOTYPE.to_string(), proto); .insert(INSTANCE_PROTOTYPE.to_string(), proto);
obj.internal_slots obj.internal_slots
.insert("extensible".to_string(), to_value(true)); .insert("extensible".to_string(), Value::from(true));
obj obj
} }
@ -528,14 +528,12 @@ unsafe impl Trace for ObjectKind {
pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
if let Some(arg) = args.get(0) { if let Some(arg) = args.get(0) {
if !arg.is_null_or_undefined() { if !arg.is_null_or_undefined() {
return Ok(Gc::new(ValueData::Object(Box::new(GcCell::new( return Ok(Value::object(Object::from(arg).unwrap()));
Object::from(arg).unwrap(),
)))));
} }
} }
let global = &ctx.realm.global_obj; let global = &ctx.realm.global_obj;
let object = ValueData::new_obj(Some(global)); let object = Value::new_object(Some(global));
Ok(object) Ok(object)
} }
@ -557,12 +555,10 @@ pub fn set_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> R
/// Define a property in an object /// Define a property in an object
pub fn define_property(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn define_property(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let obj = args.get(0).expect("Cannot get object"); let obj = args.get(0).expect("Cannot get object");
let prop = from_value::<String>(args.get(1).expect("Cannot get object").clone()) let prop = String::from(args.get(1).expect("Cannot get object"));
.expect("Cannot get object"); let desc = Property::from(args.get(2).expect("Cannot get object"));
let desc = from_value::<Property>(args.get(2).expect("Cannot get object").clone()) obj.set_property(prop, desc);
.expect("Cannot get object"); Ok(Value::undefined())
obj.set_prop(prop, desc);
Ok(undefined())
} }
/// `Object.prototype.toString()` /// `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 /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring
/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
Ok(to_value(this.to_string())) Ok(Value::from(this.to_string()))
} }
/// `Object.prototype.hasOwnPrototype( property )` /// `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() { let prop = if args.is_empty() {
None None
} else { } 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( Ok(Value::from(
prop.is_some() && this.get_prop(&prop.expect("Cannot get object")).is_some(), prop.is_some()
&& this
.get_property(&prop.expect("Cannot get object"))
.is_some(),
)) ))
} }
/// Create a new `Object` object. /// Create a new `Object` object.
pub fn create(global: &Value) -> Value { 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!(has_own_property, named "hasOwnProperty", of prototype);
make_builtin_fn!(to_string, named "toString", of prototype); make_builtin_fn!(to_string, named "toString", of prototype);
let object = make_constructor_fn!(make_object, make_object, global, 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!(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!(get_prototype_of, named "getPrototypeOf", with length 1, of object);
make_builtin_fn!(define_property, named "defineProperty", with length 3, 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 //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty
//! [section]: https://tc39.es/ecma262/#sec-property-attributes //! [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}; use gc::{Finalize, Trace};
/// This represents a Javascript Property AKA The Property Descriptor. /// This represents a Javascript Property AKA The Property Descriptor.
@ -170,59 +170,33 @@ impl Default for Property {
} }
} }
impl ToValue for Property { impl From<&Property> for Value {
fn to_value(&self) -> Value { fn from(value: &Property) -> Value {
let prop = ValueData::new_obj(None); let property = Value::new_object(None);
prop.set_field_slice("configurable", to_value(self.configurable)); property.set_field_slice("configurable", Value::from(value.configurable));
prop.set_field_slice("enumerable", to_value(self.enumerable)); property.set_field_slice("enumerable", Value::from(value.enumerable));
prop.set_field_slice("writable", to_value(self.writable)); property.set_field_slice("writable", Value::from(value.writable));
prop.set_field_slice("value", to_value(self.value.clone())); property.set_field_slice("value", value.value.clone().unwrap_or_else(Value::null));
prop.set_field_slice("get", to_value(self.get.clone())); property.set_field_slice("get", value.get.clone().unwrap_or_else(Value::null));
prop.set_field_slice("set", to_value(self.set.clone())); property.set_field_slice("set", value.set.clone().unwrap_or_else(Value::null));
prop property
} }
} }
impl FromValue for Property { impl<'a> From<&'a Value> for Property {
/// Attempt to fetch values "configurable", "enumerable", "writable" from the value, /// Attempt to fetch values "configurable", "enumerable", "writable" from the value,
/// if they're not there default to false /// if they're not there default to false
fn from_value(v: Value) -> Result<Self, &'static str> { fn from(value: &Value) -> Self {
Ok(Self { Self {
configurable: { configurable: { Some(bool::from(&value.get_field_slice("configurable"))) },
match from_value::<bool>(v.get_field_slice("configurable")) { enumerable: { Some(bool::from(&value.get_field_slice("enumerable"))) },
Ok(v) => Some(v), writable: { Some(bool::from(&value.get_field_slice("writable"))) },
Err(_) => Some(false), value: Some(value.get_field_slice("value")),
} get: Some(value.get_field_slice("get")),
}, set: Some(value.get_field_slice("set")),
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")),
})
} }
} }
#[cfg(test)] #[cfg(test)]
mod test { mod tests;
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));
}
}

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

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

@ -21,7 +21,7 @@ fn constructors() {
#[test] #[test]
fn check_regexp_constructor_is_function() { fn check_regexp_constructor_is_function() {
let global = ValueData::new_obj(None); let global = Value::new_object(None);
let regexp_constructor = create(&global); let regexp_constructor = create(&global);
assert_eq!(regexp_constructor.is_function(), true); 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}, object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE},
property::Property, property::Property,
regexp::{make_regexp, match_all as regexp_match_all, r#match as regexp_match}, 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, exec::Interpreter,
}; };
use gc::Gc;
use regex::Regex; use regex::Regex;
use std::{ use std::{
cmp::{max, min}, 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 { pub fn call_string(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue {
let arg = match args.get(0) { let arg = match args.get(0) {
Some(v) => v.clone(), Some(v) => v.clone(),
None => undefined(), None => Value::undefined(),
}; };
if arg.is_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 /// Get the string value to a primitive string
pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
// Get String from String Object and send it back as a new value // Get String from String Object and send it back as a new value
let primitive_val = this.get_internal_slot("StringData"); let primitive_val = this.get_internal_slot("StringData");
Ok(to_value(format!("{}", primitive_val))) Ok(Value::from(format!("{}", primitive_val)))
} }
/// `String.prototype.charAt( index )` /// `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. // First we get it the actual string a private field stored on the object only the engine has access to.
// Then we convert it into a Rust String by wrapping it in from_value // Then we convert it into a Rust String by wrapping it in from_value
let primitive_val = ctx.value_to_rust_string(this); let primitive_val = ctx.value_to_rust_string(this);
let pos: i32 = from_value( let pos = i32::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
// Calling .len() on a string would give the wrong result, as they are bytes not the number of // Calling .len() on a string would give the wrong result, as they are bytes not the number of
// unicode code points // 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 // We should return an empty string is pos is out of range
if pos >= length as i32 || pos < 0 { 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 primitive_val
.chars() .chars()
.nth(pos as usize) .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 // 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. // 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 length = primitive_val.chars().count();
let pos: i32 = from_value( let pos = i32::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
if pos >= length as i32 || pos < 0 { if pos >= length as i32 || pos < 0 {
return Ok(to_value(NAN)); return Ok(Value::from(NAN));
} }
let utf16_val = primitive_val 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"); .expect("failed to get utf16 value");
// If there is no element at that index, the result is NaN // If there is no element at that index, the result is NaN
// TODO: We currently don't have 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] )` /// `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); let mut new_str = ctx.value_to_rust_string(this);
for arg in args { 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); new_str.push_str(&concat_str);
} }
Ok(to_value(new_str)) Ok(Value::from(new_str))
} }
/// `String.prototype.repeat( count )` /// `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 // 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 primitive_val: String = ctx.value_to_rust_string(this);
let repeat_times: usize = from_value( let repeat_times = usize::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method"); Ok(Value::from(primitive_val.repeat(repeat_times)))
Ok(to_value(primitive_val.repeat(repeat_times)))
} }
/// `String.prototype.slice( beginIndex [, endIndex] )` /// `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 // 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 primitive_val: String = ctx.value_to_rust_string(this);
let start: i32 = from_value( let start = i32::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method"); let end = i32::from(args.get(1).expect("failed to get argument in slice"));
let end: i32 = from_value(
args.get(1)
.expect("failed to get argument in slice")
.clone(),
)
.expect("failed to parse argument");
// Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // 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. // 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"), .expect("Could not get nth char"),
); );
} }
Ok(to_value(new_str)) Ok(Value::from(new_str))
} }
/// `String.prototype.startWith( searchString[, position] )` /// `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); let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if pattern is regular expression // TODO: Should throw TypeError if pattern is regular expression
let search_string: String = from_value( let search_string = String::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
let length: i32 = primitive_val.chars().count() as i32; let length: i32 = primitive_val.chars().count() as i32;
let search_length: i32 = search_string.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 { let position: i32 = if args.len() < 2 {
0 0
} else { } 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 start = min(max(position, 0), length);
let end = start.wrapping_add(search_length); let end = start.wrapping_add(search_length);
if end > length { if end > length {
Ok(to_value(false)) Ok(Value::from(false))
} else { } else {
// Only use the part of the string from "start" // Only use the part of the string from "start"
let this_string: String = primitive_val.chars().skip(start as usize).collect(); 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); let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value( let search_string = String::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
let length: i32 = primitive_val.chars().count() as i32; let length: i32 = primitive_val.chars().count() as i32;
let search_length: i32 = search_string.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 { let end_position: i32 = if args.len() < 2 {
length length
} else { } else {
from_value(args.get(1).expect("Could not get argumetn").clone()) i32::from(args.get(1).expect("Could not get argumetn"))
.expect("Could not convert value to i32")
}; };
let end = min(max(end_position, 0), length); let end = min(max(end_position, 0), length);
let start = end.wrapping_sub(search_length); let start = end.wrapping_sub(search_length);
if start < 0 { if start < 0 {
Ok(to_value(false)) Ok(Value::from(false))
} else { } else {
// Only use the part of the string up to "end" // Only use the part of the string up to "end"
let this_string: String = primitive_val.chars().take(end as usize).collect(); 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); let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value( let search_string = String::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
let length: i32 = primitive_val.chars().count() as i32; 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 { let position: i32 = if args.len() < 2 {
0 0
} else { } else {
from_value(args.get(1).expect("Could not get argument").clone()) i32::from(args.get(1).expect("Could not get argument"))
.expect("Could not convert value to i32")
}; };
let start = min(max(position, 0), length); 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" // 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(); 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 /// 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() { if slots.get("RegExpMatcher").is_some() {
// first argument is another `RegExp` object, so copy its pattern and flags // first argument is another `RegExp` object, so copy its pattern and flags
if let Some(body) = slots.get("OriginalSource") { if let Some(body) = slots.get("OriginalSource") {
return from_value(r#body.clone()) return String::from(r#body);
.expect("unable to get body from regex value");
} }
} }
"undefined".to_string() "undefined".to_string()
@ -440,7 +419,7 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul
// TODO: Support Symbol replacer // TODO: Support Symbol replacer
let primitive_val: String = ctx.value_to_rust_string(this); let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() { 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")); 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 // This will return the matched substring first, then captured parenthesized groups later
let mut results: Vec<Value> = caps let mut results: Vec<Value> = caps
.iter() .iter()
.map(|capture| to_value(capture.unwrap().as_str())) .map(|capture| Value::from(capture.unwrap().as_str()))
.collect(); .collect();
// Returns the starting byte offset of the match // 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) .get(0)
.expect("Unable to get Byte offset from string for match") .expect("Unable to get Byte offset from string for match")
.start(); .start();
results.push(to_value(start)); results.push(Value::from(start));
// Push the whole string being examined // 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(); 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() "undefined".to_string()
}; };
Ok(to_value(primitive_val.replacen( Ok(Value::from(primitive_val.replacen(
&mat.as_str(), &mat.as_str(),
&replace_value, &replace_value,
1, 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); let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value( let search_string = String::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
let length: i32 = primitive_val.chars().count() as i32; 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 { let position: i32 = if args.len() < 2 {
0 0
} else { } else {
from_value(args.get(1).expect("Could not get argument").clone()) i32::from(args.get(1).expect("Could not get argument"))
.expect("Could not convert value to i32")
}; };
let start = min(max(position, 0), length); 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(); let this_string: String = primitive_val.chars().skip(index as usize).collect();
if this_string.starts_with(&search_string) { if this_string.starts_with(&search_string) {
// Explicitly return early with the index value // 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 // Didn't find a match, so return -1
Ok(to_value(-1)) Ok(Value::from(-1))
} }
/// `String.prototype.lastIndexOf( searchValue[, fromIndex] )` /// `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); let primitive_val: String = ctx.value_to_rust_string(this);
// TODO: Should throw TypeError if search_string is regular expression // TODO: Should throw TypeError if search_string is regular expression
let search_string: String = from_value( let search_string = String::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
let length: i32 = primitive_val.chars().count() as i32; 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 { let position: i32 = if args.len() < 2 {
0 0
} else { } else {
from_value(args.get(1).expect("Could not get argument").clone()) i32::from(args.get(1).expect("Could not get argument"))
.expect("Could not convert value to i32")
}; };
let start = min(max(position, 0), length); 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 // 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 )` /// `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 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match
/// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions
pub fn r#match(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { 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) 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; let primitive_length = primitive.len() as i32;
if max_length <= primitive_length { if max_length <= primitive_length {
return Ok(to_value(primitive)); return Ok(Value::from(primitive));
} }
let filler = match fill_string { let filler = match fill_string {
@ -669,7 +642,7 @@ fn string_pad(
}; };
if filler == "" { if filler == "" {
return Ok(to_value(primitive)); return Ok(Value::from(primitive));
} }
let fill_len = max_length.wrapping_sub(primitive_length); 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(); let concat_fill_str: String = fill_str.chars().take(fill_len as usize).collect();
if at_start { if at_start {
Ok(to_value(format!("{}{}", concat_fill_str, &primitive))) Ok(Value::from(format!("{}{}", concat_fill_str, &primitive)))
} else { } 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 { pub fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this); let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() { 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) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
let fill_string: Option<String> = match args.len() { let fill_string: Option<String> = match args.len() {
1 => None, 1 => None,
_ => Some( _ => Some(String::from(args.get(1).expect("Could not get argument"))),
from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert value to Option<String>"),
),
}; };
string_pad(primitive_val, max_length, fill_string, false) 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 { pub fn pad_start(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let primitive_val: String = ctx.value_to_rust_string(this); let primitive_val: String = ctx.value_to_rust_string(this);
if args.is_empty() { 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) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(), );
)
.expect("failed to parse argument for String method");
let fill_string: Option<String> = match args.len() { let fill_string: Option<String> = match args.len() {
1 => None, 1 => None,
_ => Some( _ => Some(String::from(args.get(1).expect("Could not get argument"))),
from_value(args.get(1).expect("Could not get argument").clone())
.expect("Could not convert value to Option<String>"),
),
}; };
string_pad(primitive_val, max_length, fill_string, true) 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 /// [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 { pub fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this); 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()` /// `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 /// [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 { pub fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this); let this_str: String = ctx.value_to_rust_string(this);
Ok(to_value( Ok(Value::from(
this_str.trim_start_matches(is_trimmable_whitespace), 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 /// [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 { pub fn trim_end(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue {
let this_str: String = ctx.value_to_rust_string(this); 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()` /// `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); let this_str: String = ctx.value_to_rust_string(this);
// The Rust String is mapped to uppercase using the builtin .to_lowercase(). // 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 // 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()` /// `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); let this_str: String = ctx.value_to_rust_string(this);
// The Rust String is mapped to uppercase using the builtin .to_uppercase(). // 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 // 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] )` /// `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() { let start = if args.is_empty() {
0 0
} else { } else {
from_value( i32::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(),
) )
.expect("failed to parse argument for String method")
}; };
let length: i32 = primitive_val.chars().count() as i32; 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 // If less than 2 args specified, end is the length of the this object converted to a String
let end = if args.len() < 2 { let end = if args.len() < 2 {
length length
} else { } else {
from_value(args.get(1).expect("Could not get argument").clone()) i32::from(args.get(1).expect("Could not get argument"))
.expect("failed to parse argument for String method")
}; };
// Both start and end args replaced by 0 if they were negative // Both start and end args replaced by 0 if they were negative
// or by the length of the String if they were greater // 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) .skip(from)
.take(to.wrapping_sub(from)) .take(to.wrapping_sub(from))
.collect(); .collect();
Ok(to_value(extracted_string)) Ok(Value::from(extracted_string))
} }
/// `String.prototype.substr( start[, length] )` /// `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() { let mut start = if args.is_empty() {
0 0
} else { } else {
from_value( i32::from(
args.get(0) args.get(0)
.expect("failed to get argument for String method") .expect("failed to get argument for String method"),
.clone(),
) )
.expect("failed to parse argument for String method")
}; };
let length: i32 = primitive_val.chars().count() as i32; let length: i32 = primitive_val.chars().count() as i32;
// If less than 2 args specified, end is +infinity, the maximum number value. // 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 { let end = if args.len() < 2 {
i32::max_value() i32::max_value()
} else { } else {
from_value(args.get(1).expect("Could not get argument").clone()) i32::from(args.get(1).expect("Could not get argument"))
.expect("failed to parse argument for String method")
}; };
// If start is negative it become the number of code units from the end of the string // If start is negative it become the number of code units from the end of the string
if start < 0 { 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 // 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 // otherwise we extract the part of the string from start and is length code units long
if result_length <= 0 { if result_length <= 0 {
Ok(to_value("".to_string())) Ok(Value::from("".to_string()))
} else { } else {
let extracted_string: String = primitive_val let extracted_string: String = primitive_val
.chars() .chars()
.skip(start as usize) .skip(start as usize)
.take(result_length as usize) .take(result_length as usize)
.collect(); .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 { pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue {
let mut re: Value = match args.get(0) { let mut re: Value = match args.get(0) {
Some(arg) => { Some(arg) => {
if arg == &Gc::new(ValueData::Null) { if arg == &Value::null() {
make_regexp( make_regexp(
&mut to_value(Object::default()), &mut Value::from(Object::default()),
&[ &[
to_value(ctx.value_to_rust_string(arg)), Value::from(ctx.value_to_rust_string(arg)),
to_value(String::from("g")), Value::from(String::from("g")),
], ],
ctx, ctx,
) )
} else if arg == &undefined() { } else if arg == &Value::undefined() {
make_regexp( make_regexp(
&mut to_value(Object::default()), &mut Value::from(Object::default()),
&[undefined(), to_value(String::from("g"))], &[Value::undefined(), Value::from(String::from("g"))],
ctx, ctx,
) )
} else { } else {
from_value(arg.clone()).map_err(to_value) Ok(arg.clone())
} }
} }
None => make_regexp( None => make_regexp(
&mut to_value(Object::default()), &mut Value::from(Object::default()),
&[to_value(String::new()), to_value(String::from("g"))], &[Value::from(String::new()), Value::from("g")],
ctx, ctx,
), ),
}?; }?;
@ -1039,10 +1001,10 @@ pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res
/// Create a new `String` object. /// Create a new `String` object.
pub fn create(global: &Value) -> Value { pub fn create(global: &Value) -> Value {
// Create prototype // Create prototype
let prototype = ValueData::new_obj(Some(global)); let prototype = Value::new_object(Some(global));
let length = Property::default().value(to_value(0_i32)); 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_at, named "charAt", with length 1, of prototype);
make_builtin_fn!(char_code_at, named "charCodeAt", with length 1, of prototype); make_builtin_fn!(char_code_at, named "charCodeAt", with length 1, of prototype);
make_builtin_fn!(to_string, named "toString", of prototype); make_builtin_fn!(to_string, named "toString", of prototype);

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

@ -5,7 +5,7 @@ use crate::{forward, forward_val};
#[test] #[test]
fn check_string_constructor_is_function() { fn check_string_constructor_is_function() {
let global = ValueData::new_obj(None); let global = Value::new_object(None);
let string_constructor = create(&global); let string_constructor = create(&global);
assert_eq!(string_constructor.is_function(), true); 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, internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE,
PROTOTYPE, PROTOTYPE,
}, },
value::{to_value, undefined, ResultValue, Value, ValueData}, value::{ResultValue, Value, ValueData},
}, },
exec::Interpreter, 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 // Set description which should either be undefined or a string
let desc_string = match args.get(0) { let desc_string = match args.get(0) {
Some(value) => to_value(value.to_string()), Some(value) => Value::from(value.to_string()),
None => undefined(), None => Value::undefined(),
}; };
sym_instance.set_internal_slot("Description", desc_string); 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 // Set __proto__ internal slot
let proto = ctx let proto = ctx
@ -66,9 +66,9 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu
.get_field_slice(PROTOTYPE); .get_field_slice(PROTOTYPE);
sym_instance.set_internal_slot(INSTANCE_PROTOTYPE, proto); 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, sym_instance,
))))) ))))))
} }
/// `Symbol.prototype.toString()` /// `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 { pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue {
let s: Value = this.get_internal_slot("Description"); let s: Value = this.get_internal_slot("Description");
let full_string = format!(r#"Symbol({})"#, s.to_string()); let full_string = format!(r#"Symbol({})"#, s.to_string());
Ok(to_value(full_string)) Ok(Value::from(full_string))
} }
/// Create a new `Symbol` object. /// Create a new `Symbol` object.
pub fn create(global: &Value) -> Value { pub fn create(global: &Value) -> Value {
// Create prototype object // 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_builtin_fn!(to_string, named "toString", of prototype);
make_constructor_fn!(call_symbol, call_symbol, global, 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] #[test]
fn check_symbol_constructor_is_function() { 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); let symbol_constructor = create(&global);
assert_eq!(symbol_constructor.is_function(), true); assert_eq!(symbol_constructor.is_function(), true);
} }

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

@ -1,202 +1,187 @@
use super::*; use super::*;
use std::convert::TryFrom;
/// Conversion to Javascript values from Rust values impl From<&Value> for Value {
pub trait ToValue { fn from(value: &Value) -> Self {
/// Convert this value to a Rust value value.clone()
fn to_value(&self) -> Value;
}
/// Conversion to Rust values from Javascript values
pub trait FromValue {
/// Convert this value to a Javascript value
fn from_value(value: Value) -> Result<Self, &'static str>
where
Self: Sized;
}
impl ToValue for Value {
fn to_value(&self) -> Value {
self.clone()
} }
} }
impl FromValue for Value { impl From<String> for Value {
fn from_value(value: Value) -> Result<Self, &'static str> { fn from(value: String) -> Self {
Ok(value) Self::string(value)
} }
} }
impl ToValue for String { impl From<&Value> for String {
fn to_value(&self) -> Value { fn from(value: &Value) -> Self {
Gc::new(ValueData::String(self.clone())) value.to_string()
} }
} }
impl FromValue for String { impl From<&str> for Value {
fn from_value(v: Value) -> Result<Self, &'static str> { fn from(value: &str) -> Value {
Ok(v.to_string()) Value::string(value)
} }
} }
impl<'s> ToValue for &'s str { impl From<char> for Value {
fn to_value(&self) -> Value { fn from(value: char) -> Self {
Gc::new(ValueData::String( Value::string(value.to_string())
String::from_str(*self).expect("Could not convert string to self to String"),
))
} }
} }
impl ToValue for char { #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
fn to_value(&self) -> Value { pub struct TryFromCharError;
Gc::new(ValueData::String(self.to_string()))
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> { impl TryFrom<&Value> for char {
Ok(v.to_string() type Error = TryFromCharError;
.chars()
.next() fn try_from(value: &Value) -> Result<Self, Self::Error> {
.expect("Could not get next char")) if let Some(c) = value.to_string().chars().next() {
Ok(c)
} else {
Err(TryFromCharError)
}
} }
} }
impl ToValue for f64 { impl From<f64> for Value {
fn to_value(&self) -> Value { fn from(value: f64) -> Self {
Gc::new(ValueData::Rational(*self)) Self::rational(value)
} }
} }
impl FromValue for f64 {
fn from_value(v: Value) -> Result<Self, &'static str> { impl From<&Value> for f64 {
Ok(v.to_number()) fn from(value: &Value) -> Self {
value.to_number()
} }
} }
impl ToValue for i32 { impl From<i32> for Value {
fn to_value(&self) -> Value { fn from(value: i32) -> Value {
Gc::new(ValueData::Integer(*self)) Value::integer(value)
} }
} }
impl FromValue for i32 {
fn from_value(v: Value) -> Result<Self, &'static str> { impl From<&Value> for i32 {
Ok(v.to_integer()) fn from(value: &Value) -> i32 {
value.to_integer()
} }
} }
impl ToValue for usize { impl From<usize> for Value {
fn to_value(&self) -> Value { fn from(value: usize) -> Value {
Gc::new(ValueData::Integer(*self as i32)) Value::integer(value as i32)
} }
} }
impl FromValue for usize { impl From<&Value> for usize {
fn from_value(v: Value) -> Result<Self, &'static str> { fn from(value: &Value) -> usize {
Ok(v.to_integer() as Self) value.to_integer() as Self
} }
} }
impl ToValue for bool { impl From<bool> for Value {
fn to_value(&self) -> Value { fn from(value: bool) -> Self {
Gc::new(ValueData::Boolean(*self)) Value::boolean(value)
} }
} }
impl FromValue for bool {
fn from_value(v: Value) -> Result<Self, &'static str> { impl From<&Value> for bool {
Ok(v.is_true()) fn from(value: &Value) -> Self {
value.is_true()
} }
} }
impl<'s, T: ToValue> ToValue for &'s [T] { impl<T> From<&[T]> for Value
fn to_value(&self) -> Value { where
let mut arr = Object::default(); T: Clone + Into<Value>,
for (i, item) in self.iter().enumerate() { {
arr.properties fn from(value: &[T]) -> Self {
.insert(i.to_string(), Property::default().value(item.to_value())); 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 { impl<T> From<Vec<T>> for Value
let mut arr = Object::default(); where
for (i, item) in self.iter().enumerate() { T: Into<Value>,
arr.properties {
.insert(i.to_string(), Property::default().value(item.to_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> { impl From<Object> for Value {
fn from_value(v: Value) -> Result<Self, &'static str> { fn from(object: Object) -> Self {
let len = v.get_field_slice("length").to_integer(); Value::object(object)
let mut vec = Self::with_capacity(len as usize);
for i in 0..len {
vec.push(from_value(v.get_field_slice(&i.to_string()))?)
}
Ok(vec)
} }
} }
impl ToValue for Object { #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
fn to_value(&self) -> Value { pub struct TryFromObjectError;
Gc::new(ValueData::Object(Box::new(GcCell::new(self.clone()))))
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 { impl TryFrom<&Value> for Object {
fn from_value(v: Value) -> Result<Self, &'static str> { type Error = TryFromObjectError;
match *v {
ValueData::Object(ref obj) => Ok(obj.clone().into_inner()), fn try_from(value: &Value) -> Result<Self, Self::Error> {
_ => Err("Value is not a valid object"), match value.data() {
ValueData::Object(ref object) => Ok(object.clone().into_inner()),
_ => Err(TryFromObjectError),
} }
} }
} }
impl ToValue for JSONValue { impl From<JSONValue> for Value {
fn to_value(&self) -> Value { fn from(value: JSONValue) -> Self {
Gc::new(ValueData::from_json(self.clone())) Self(Gc::new(ValueData::from_json(value)))
} }
} }
impl FromValue for JSONValue { impl From<&Value> for JSONValue {
fn from_value(v: Value) -> Result<Self, &'static str> { fn from(value: &Value) -> Self {
Ok(v.to_json()) value.to_json()
} }
} }
impl ToValue for () { impl From<()> for Value {
fn to_value(&self) -> Value { fn from(_: ()) -> Self {
Gc::new(ValueData::Null) Value::null()
}
}
impl FromValue for () {
fn from_value(_: Value) -> Result<(), &'static str> {
Ok(())
} }
} }
impl<T: ToValue> ToValue for Option<T> { impl<T> From<Option<T>> for Value
fn to_value(&self) -> Value { where
match *self { T: Into<Value>,
Some(ref v) => v.to_value(), {
None => Gc::new(ValueData::Null), 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>; pub type ResultValue = Result<Value, Value>;
/// A Garbage-collected Javascript value as represented in the interpreter. /// 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] impl Value {
pub fn undefined() -> Value { /// Creates a new `undefined` value.
Gc::new(ValueData::Undefined) #[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 /// A Javascript value
@ -63,31 +170,6 @@ pub enum ValueData {
} }
impl 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 /// This will tell us if we can exten an object or not, not properly implemented yet
/// ///
/// For now always returns true. /// For now always returns true.
@ -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 /// 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 { match *self {
Self::Object(ref obj) => obj.borrow_mut().deref_mut().properties.remove(field), Self::Object(ref obj) => obj.borrow_mut().deref_mut().properties.remove(field),
_ => None, _ => None,
@ -262,12 +344,12 @@ impl ValueData {
/// Resolve the property in the object. /// Resolve the property in the object.
/// ///
/// A copy of the Property is returned. /// 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 // 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 // This is only for primitive strings, String() objects have their lengths calculated in string.rs
if self.is_string() && field == "length" { if self.is_string() && field == "length" {
if let Self::String(ref s) = *self { 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) { match obj.properties.get(field) {
Some(val) => Some(val.clone()), Some(val) => Some(val.clone()),
None => match obj.internal_slots.get(&INSTANCE_PROTOTYPE.to_string()) { None => match obj.internal_slots.get(&INSTANCE_PROTOTYPE.to_string()) {
Some(value) => value.get_prop(field), Some(value) => value.get_property(field),
None => None, None => None,
}, },
} }
@ -301,7 +383,7 @@ impl ValueData {
/// update_prop will overwrite individual [Property] fields, unlike /// update_prop will overwrite individual [Property] fields, unlike
/// Set_prop, which will overwrite prop with a new Property /// Set_prop, which will overwrite prop with a new Property
/// Mostly used internally for now /// Mostly used internally for now
pub fn update_prop( pub fn update_property(
&self, &self,
field: &str, field: &str,
value: Option<Value>, value: Option<Value>,
@ -338,12 +420,12 @@ impl ValueData {
let hash = obj.clone(); let hash = obj.clone();
hash.into_inner() hash.into_inner()
} }
_ => return Gc::new(Self::Undefined), _ => return Value::undefined(),
}; };
match obj.internal_slots.get(field) { match obj.internal_slots.get(field) {
Some(val) => val.clone(), Some(val) => val.clone(),
None => Gc::new(Self::Undefined), None => Value::undefined(),
} }
} }
@ -354,7 +436,7 @@ impl ValueData {
match *field { match *field {
// Our field will either be a String or a Symbol // Our field will either be a String or a Symbol
Self::String(ref s) => { Self::String(ref s) => {
match self.get_prop(s) { match self.get_property(s) {
Some(prop) => { Some(prop) => {
// If the Property has [[Get]] set to a function, we should run that and return the Value // If the Property has [[Get]] set to a function, we should run that and return the Value
let prop_getter = match prop.get { let prop_getter = match prop.get {
@ -373,11 +455,11 @@ impl ValueData {
val.clone() val.clone()
} }
} }
None => Gc::new(Self::Undefined), None => Value::undefined(),
} }
} }
Self::Symbol(_) => unimplemented!(), 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 /// Check to see if the Value has the field, mainly used by environment records
pub fn has_field(&self, field: &str) -> bool { 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 /// 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 { 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 // 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) // 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) self.get_field(f)
} }
@ -467,10 +549,9 @@ impl ValueData {
if obj.borrow().kind == ObjectKind::Array { if obj.borrow().kind == ObjectKind::Array {
if let Ok(num) = field.to_string().parse::<usize>() { if let Ok(num) = field.to_string().parse::<usize>() {
if num > 0 { if num > 0 {
let len: i32 = from_value(self.get_field_slice("length")) let len = i32::from(&self.get_field_slice("length"));
.expect("Could not convert argument to i32");
if len < (num + 1) as i32 { 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()); obj.borrow_mut().set(field, val.clone());
} else { } else {
obj.borrow_mut() 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 { 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 // 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) // 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) self.set_field(f, val)
} }
@ -514,7 +595,7 @@ impl ValueData {
} }
/// Set the property in the value /// 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 { if let Self::Object(ref obj) = *self {
obj.borrow_mut().properties.insert(field, prop.clone()); obj.borrow_mut().properties.insert(field, prop.clone());
} }
@ -522,8 +603,8 @@ impl ValueData {
} }
/// Set the property in the value /// Set the property in the value
pub fn set_prop_slice(&self, field: &str, prop: Property) -> Property { pub fn set_property_slice(&self, field: &str, prop: Property) -> Property {
self.set_prop(field.to_string(), prop) self.set_property(field.to_string(), prop)
} }
/// Set internal state of an Object. Discards the previous state if it was set. /// Set internal state of an Object. Discards the previous state if it was set.
@ -544,9 +625,9 @@ impl ValueData {
// Set [[Call]] internal slot // Set [[Call]] internal slot
new_func.set_call(native_func); new_func.set_call(native_func);
// Wrap Object in GC'd Value // 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 // 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 new_func_val
} }
@ -563,12 +644,12 @@ impl ValueData {
for (idx, json) in vs.iter().enumerate() { for (idx, json) in vs.iter().enumerate() {
new_obj.properties.insert( new_obj.properties.insert(
idx.to_string(), idx.to_string(),
Property::default().value(to_value(json.clone())), Property::default().value(Value::from(json.clone())),
); );
} }
new_obj.properties.insert( new_obj.properties.insert(
"length".to_string(), "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))) Self::Object(Box::new(GcCell::new(new_obj)))
} }
@ -577,7 +658,7 @@ impl ValueData {
for (key, json) in obj.iter() { for (key, json) in obj.iter() {
new_obj.properties.insert( new_obj.properties.insert(
key.clone(), 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 { 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 // Can use the private "type" field of an Object to match on
// which type of Object it represents for special printing // which type of Object it represents for special printing
match v.borrow().kind { match v.borrow().kind {
ObjectKind::String => from_value( ObjectKind::String => String::from(
v.borrow() v.borrow()
.internal_slots .internal_slots
.get("StringData") .get("StringData")
.expect("Cannot get primitive value from String") .expect("Cannot get primitive value from String"),
.clone(), ),
)
.expect("Cannot clone primitive value from String"),
ObjectKind::Boolean => { ObjectKind::Boolean => {
let bool_data = v.borrow().get_internal_slot("BooleanData").to_string(); let bool_data = v.borrow().get_internal_slot("BooleanData").to_string();
format!("Boolean {{ {} }}", bool_data) format!("Boolean {{ {} }}", bool_data)
} }
ObjectKind::Array => { ObjectKind::Array => {
let len: i32 = from_value( let len = i32::from(
v.borrow() &v.borrow()
.properties .properties
.get("length") .get("length")
.unwrap() .unwrap()
.value .value
.clone() .clone()
.expect("Could not borrow value"), .expect("Could not borrow value"),
) );
.expect("Could not convert JS value to i32");
if len == 0 { if len == 0 {
return String::from("[]"); return String::from("[]");

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

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

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

@ -2,14 +2,14 @@ use super::*;
#[test] #[test]
fn check_is_object() { fn check_is_object() {
let val = ValueData::new_obj(None); let val = Value::new_object(None);
assert_eq!(val.is_object(), true); assert_eq!(val.is_object(), true);
} }
#[test] #[test]
fn check_string_to_value() { fn check_string_to_value() {
let s = String::from("Hello"); 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_string(), true);
assert_eq!(v.is_null(), false); assert_eq!(v.is_null(), false);
} }
@ -23,26 +23,26 @@ fn check_undefined() {
#[test] #[test]
fn check_get_set_field() { 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 // 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); obj.set_field_slice("foo", s);
assert_eq!(obj.get_field_slice("foo").to_string(), "bar"); assert_eq!(obj.get_field_slice("foo").to_string(), "bar");
} }
#[test] #[test]
fn check_integer_is_true() { fn check_integer_is_true() {
assert_eq!(1.to_value().is_true(), true); assert_eq!(Value::from(1).is_true(), true);
assert_eq!(0.to_value().is_true(), false); assert_eq!(Value::from(0).is_true(), false);
assert_eq!((-1).to_value().is_true(), true); assert_eq!(Value::from(-1).is_true(), true);
} }
#[test] #[test]
fn check_number_is_true() { fn check_number_is_true() {
assert_eq!(1.0.to_value().is_true(), true); assert_eq!(Value::from(1.0).is_true(), true);
assert_eq!(0.1.to_value().is_true(), true); assert_eq!(Value::from(0.1).is_true(), true);
assert_eq!(0.0.to_value().is_true(), false); assert_eq!(Value::from(0.0).is_true(), false);
assert_eq!((-0.0).to_value().is_true(), false); assert_eq!(Value::from(-0.0).is_true(), false);
assert_eq!((-1.0).to_value().is_true(), true); assert_eq!(Value::from(-1.0).is_true(), true);
assert_eq!(NAN.to_value().is_true(), false); 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) //! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.es/ecma262/#sec-declarative-environment-records)
use crate::{ use crate::{
builtins::value::{Value, ValueData}, builtins::value::Value,
environment::{ environment::{
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType}, lexical_environment::{Environment, EnvironmentType},
}, },
}; };
use gc::{Finalize, Gc, Trace}; use gc::{Finalize, Trace};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
/// Declarative Bindings have a few properties for book keeping purposes, such as mutability (const vs let). /// 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 { fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined) Value::undefined()
} }
fn get_outer_environment(&self) -> Option<Environment> { 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> //! More info: <https://tc39.es/ecma262/#sec-function-environment-records>
use crate::{ use crate::{
builtins::value::{Value, ValueData}, builtins::value::Value,
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecordBinding, declarative_environment_record::DeclarativeEnvironmentRecordBinding,
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType}, lexical_environment::{Environment, EnvironmentType},
}, },
}; };
use gc::{Finalize, Gc, Trace}; use gc::{Finalize, Trace};
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
/// Different binding status for `this`. /// Different binding status for `this`.
@ -220,7 +220,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord {
} }
fn with_base_object(&self) -> Value { fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined) Value::undefined()
} }
fn get_outer_environment(&self) -> Option<Environment> { 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> //! More info: <https://tc39.es/ecma262/#sec-global-environment-records>
use crate::{ use crate::{
builtins::value::{Value, ValueData}, builtins::value::Value,
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
@ -16,7 +16,7 @@ use crate::{
object_environment_record::ObjectEnvironmentRecord, object_environment_record::ObjectEnvironmentRecord,
}, },
}; };
use gc::{Finalize, Gc, Trace}; use gc::{Finalize, Trace};
use rustc_hash::FxHashSet; use rustc_hash::FxHashSet;
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
@ -42,7 +42,7 @@ impl GlobalEnvironmentRecord {
pub fn has_restricted_global_property(&self, name: &str) -> bool { pub fn has_restricted_global_property(&self, name: &str) -> bool {
let global_object = &self.object_record.bindings; 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 { match existing_prop {
Some(prop) => { Some(prop) => {
if prop.value.is_none() || prop.configurable.unwrap_or(false) { if prop.value.is_none() || prop.configurable.unwrap_or(false) {
@ -61,7 +61,7 @@ impl GlobalEnvironmentRecord {
let extensible = global_object.is_extensible(); let extensible = global_object.is_extensible();
if !has_property && extensible { if !has_property && extensible {
obj_rec.create_mutable_binding(name.clone(), deletion); 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; 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) { pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) {
let global_object = &mut self.object_record.bindings; 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 let Some(prop) = existing_prop {
if prop.value.is_none() || prop.configurable.unwrap_or(false) { if prop.value.is_none() || prop.configurable.unwrap_or(false) {
global_object.update_prop( global_object.update_property(
name, name,
Some(value), Some(value),
Some(true), Some(true),
@ -84,7 +84,13 @@ impl GlobalEnvironmentRecord {
); );
} }
} else { } 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 { fn with_base_object(&self) -> Value {
Gc::new(ValueData::Undefined) Value::undefined()
} }
fn get_outer_environment(&self) -> Option<Environment> { 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. //! This is the entrypoint to lexical environments.
use crate::{ use crate::{
builtins::value::{Value, ValueData}, builtins::value::Value,
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
@ -211,7 +211,7 @@ impl LexicalEnvironment {
self.environments() self.environments()
.find(|env| env.borrow().has_binding(name)) .find(|env| env.borrow().has_binding(name))
.map(|env| env.borrow().get_binding_value(name, false)) .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(), env_rec: FxHashMap::default(),
function: f, function: f,
this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported 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, 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 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) //! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records)
use crate::{ use crate::{
builtins::{ builtins::{property::Property, value::Value},
property::Property,
value::{Value, ValueData},
},
environment::{ environment::{
environment_record_trait::EnvironmentRecordTrait, environment_record_trait::EnvironmentRecordTrait,
lexical_environment::{Environment, EnvironmentType}, lexical_environment::{Environment, EnvironmentType},
}, },
}; };
use gc::{Finalize, Gc, Trace}; use gc::{Finalize, Trace};
#[derive(Debug, Trace, Finalize, Clone)] #[derive(Debug, Trace, Finalize, Clone)]
pub struct ObjectEnvironmentRecord { 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 // 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 bindings = &mut self.bindings;
let prop = Property::default() let prop = Property::default()
.value(Gc::new(ValueData::Undefined)) .value(Value::undefined())
.writable(true) .writable(true)
.enumerable(true) .enumerable(true)
.configurable(deletion); .configurable(deletion);
bindings.set_prop(name, prop); bindings.set_property(name, prop);
} }
fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> bool { 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()); debug_assert!(value.is_object() || value.is_function());
let bindings = &mut self.bindings; 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 { fn get_binding_value(&self, name: &str, strict: bool) -> Value {
@ -77,12 +74,12 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
// TODO: throw error here // TODO: throw error here
// Error handling not implemented yet // Error handling not implemented yet
} }
Gc::new(ValueData::Undefined) Value::undefined()
} }
} }
fn delete_binding(&mut self, name: &str) -> bool { fn delete_binding(&mut self, name: &str) -> bool {
self.bindings.remove_prop(name); self.bindings.remove_property(name);
true true
} }
@ -101,7 +98,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord {
return self.bindings.clone(); return self.bindings.clone();
} }
Gc::new(ValueData::Undefined) Value::undefined()
} }
fn get_outer_environment(&self) -> Option<Environment> { fn get_outer_environment(&self) -> Option<Environment> {

171
boa/src/exec/mod.rs

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

6
boa/src/realm.rs

@ -8,7 +8,7 @@ use crate::{
builtins::{ builtins::{
self, self,
function::NativeFunctionData, function::NativeFunctionData,
value::{ToValue, Value, ValueData}, value::{Value, ValueData},
}, },
environment::{ environment::{
declarative_environment_record::DeclarativeEnvironmentRecord, declarative_environment_record::DeclarativeEnvironmentRecord,
@ -34,7 +34,7 @@ impl Realm {
pub fn create() -> Self { pub fn create() -> Self {
// Create brand new global object // Create brand new global object
// Global has no prototype to pass None to new_obj // 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) // 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()); let global_env = new_global_environment(global.clone(), global.clone());
@ -65,7 +65,7 @@ impl Realm {
crate::builtins::function::FunctionBody::BuiltIn(func), crate::builtins::function::FunctionBody::BuiltIn(func),
); );
self.global_obj self.global_obj
.set_field(func_name.to_value(), ValueData::from_func(func)); .set_field(Value::from(func_name), ValueData::from_func(func));
self self
} }

Loading…
Cancel
Save