From 1e18cb02d0682627d6d0171464eb0d868de7fcce Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 9 May 2020 16:06:30 +0200 Subject: [PATCH] Value refactor (#383) --- boa/src/builtins/array/mod.rs | 254 ++++++++--------- boa/src/builtins/boolean/mod.rs | 28 +- boa/src/builtins/boolean/tests.rs | 2 +- boa/src/builtins/console/mod.rs | 56 ++-- boa/src/builtins/console/tests.rs | 32 +-- boa/src/builtins/error.rs | 14 +- boa/src/builtins/function/mod.rs | 10 +- boa/src/builtins/json/mod.rs | 12 +- boa/src/builtins/math/mod.rs | 185 +++++-------- boa/src/builtins/mod.rs | 8 +- boa/src/builtins/number/mod.rs | 34 +-- boa/src/builtins/number/tests.rs | 4 +- .../builtins/object/internal_methods_trait.rs | 13 +- boa/src/builtins/object/mod.rs | 47 ++-- .../builtins/{property.rs => property/mod.rs} | 70 ++--- boa/src/builtins/property/tests.rs | 10 + boa/src/builtins/regexp/mod.rs | 103 ++++--- boa/src/builtins/regexp/tests.rs | 2 +- boa/src/builtins/string/mod.rs | 256 ++++++++---------- boa/src/builtins/string/tests.rs | 2 +- boa/src/builtins/symbol/mod.rs | 16 +- boa/src/builtins/symbol/tests.rs | 2 +- boa/src/builtins/value/conversions.rs | 247 ++++++++--------- boa/src/builtins/value/mod.rs | 210 +++++++++----- boa/src/builtins/value/operations.rs | 87 +++--- boa/src/builtins/value/tests.rs | 26 +- .../declarative_environment_record.rs | 6 +- .../function_environment_record.rs | 6 +- .../environment/global_environment_record.rs | 22 +- boa/src/environment/lexical_environment.rs | 8 +- .../environment/object_environment_record.rs | 19 +- boa/src/exec/mod.rs | 171 ++++++------ boa/src/realm.rs | 6 +- 33 files changed, 939 insertions(+), 1029 deletions(-) rename boa/src/builtins/{property.rs => property/mod.rs} (75%) create mode 100644 boa/src/builtins/property/tests.rs diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index acc1d3f860..110e7547d2 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -16,18 +16,17 @@ use crate::{ builtins::{ object::{Object, ObjectInternalMethods, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE}, property::Property, - value::{from_value, to_value, undefined, ResultValue, Value, ValueData}, + value::{ResultValue, Value, ValueData}, }, exec::Interpreter, }; -use gc::Gc; use std::borrow::Borrow; use std::cmp::{max, min}; use std::ops::Deref; /// Creates a new `Array` instance. pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue { - let array = ValueData::new_obj(Some( + let array = Value::new_object(Some( &interpreter .get_realm() .environment @@ -44,7 +43,7 @@ pub(crate) fn new_array(interpreter: &Interpreter) -> ResultValue { .borrow() .get_field_slice(PROTOTYPE), ); - array.borrow().set_field_slice("length", to_value(0)); + array.borrow().set_field_slice("length", Value::from(0)); Ok(array) } @@ -56,20 +55,19 @@ pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultVal let array_obj_ptr = array_obj.clone(); // Wipe existing contents of the array object - let orig_length: i32 = - from_value(array_obj.get_field_slice("length")).expect("failed to convert length to i32"); + let orig_length = i32::from(&array_obj.get_field_slice("length")); for n in 0..orig_length { - array_obj_ptr.remove_prop(&n.to_string()); + array_obj_ptr.remove_property(&n.to_string()); } // Create length let length = Property::new() - .value(to_value(array_contents.len() as i32)) + .value(Value::from(array_contents.len() as i32)) .writable(true) .configurable(false) .enumerable(false); - array_obj_ptr.set_prop("length".to_string(), length); + array_obj_ptr.set_property("length".to_string(), length); for (n, value) in array_contents.iter().enumerate() { array_obj_ptr.set_field_slice(&n.to_string(), value.clone()); @@ -80,8 +78,7 @@ pub fn construct_array(array_obj: &Value, array_contents: &[Value]) -> ResultVal /// Utility function which takes an existing array object and puts additional /// values on the end, correctly rewriting the length pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> ResultValue { - let orig_length: i32 = - from_value(array_ptr.get_field_slice("length")).expect("failed to conveert lenth to i32"); + let orig_length = i32::from(&array_ptr.get_field_slice("length")); for (n, value) in add_values.iter().enumerate() { let new_index = orig_length.wrapping_add(n as i32); @@ -90,7 +87,7 @@ pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> Re array_ptr.set_field_slice( "length", - to_value(orig_length.wrapping_add(add_values.len() as i32)), + Value::from(orig_length.wrapping_add(add_values.len() as i32)), ); Ok(array_ptr.clone()) @@ -117,10 +114,10 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re let mut length = args.len() as i32; match args.len() { 1 if args[0].is_integer() => { - length = from_value::(args[0].clone()).expect("Could not convert argument to i32"); + length = i32::from(&args[0]); // TODO: It should not create an array of undefineds, but an empty array ("holy" array in V8) with length `n`. for n in 0..length { - this.set_field_slice(&n.to_string(), Gc::new(ValueData::Undefined)); + this.set_field_slice(&n.to_string(), Value::undefined()); } } 1 if args[0].is_double() => { @@ -136,12 +133,12 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re // finally create length property let length = Property::new() - .value(to_value(length)) + .value(Value::from(length)) .writable(true) .configurable(false) .enumerable(false); - this.set_prop("length".to_string(), length); + this.set_property("length".to_string(), length); Ok(this.clone()) } @@ -158,12 +155,12 @@ pub fn make_array(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Re /// [spec]: https://tc39.es/ecma262/#sec-array.isarray /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray pub fn is_array(_this: &mut Value, args: &[Value], _interpreter: &mut Interpreter) -> ResultValue { - let value_true = Gc::new(ValueData::Boolean(true)); - let value_false = Gc::new(ValueData::Boolean(false)); + let value_true = Value::boolean(true); + let value_false = Value::boolean(false); match args.get(0) { Some(arg) => { - match *(*arg).clone() { + match arg.data() { // 1. ValueData::Object(ref obj) => { // 2. @@ -202,15 +199,13 @@ pub fn concat(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa // one) let mut new_values: Vec = Vec::new(); - let this_length: i32 = - from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); + let this_length = i32::from(&this.get_field_slice("length")); for n in 0..this_length { new_values.push(this.get_field_slice(&n.to_string())); } for concat_array in args { - let concat_length: i32 = from_value(concat_array.get_field_slice("length")) - .expect("Could not convert argument to i32"); + let concat_length = i32::from(&concat_array.get_field_slice("length")); for n in 0..concat_length { new_values.push(concat_array.get_field_slice(&n.to_string())); } @@ -247,15 +242,14 @@ pub fn push(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let curr_length: i32 = - from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); + let curr_length = i32::from(&this.get_field_slice("length")); if curr_length < 1 { - return Ok(Gc::new(ValueData::Undefined)); + return Ok(Value::undefined()); } let pop_index = curr_length.wrapping_sub(1); let pop_value: Value = this.get_field_slice(&pop_index.to_string()); - this.remove_prop(&pop_index.to_string()); - this.set_field_slice("length", to_value(pop_index)); + this.remove_property(&pop_index.to_string()); + this.set_field_slice("length", Value::from(pop_index)); Ok(pop_value) } @@ -271,25 +265,22 @@ pub fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach pub fn for_each(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(to_value( - "Missing argument for Array.prototype.forEach".to_string(), - )); + return Err(Value::from("Missing argument for Array.prototype.forEach")); } let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); - let mut this_arg = args.get(1).cloned().unwrap_or_else(undefined); + let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length: i32 = - from_value(this.get_field_slice("length")).expect("Could not get `length` property."); + let length = i32::from(&this.get_field_slice("length")); for i in 0..length { let element = this.get_field_slice(&i.to_string()); - let arguments = [element, to_value(i), this.clone()]; + let arguments = [element, Value::from(i), this.clone()]; interpreter.call(callback_arg, &mut this_arg, &arguments)?; } - Ok(Gc::new(ValueData::Undefined)) + Ok(Value::undefined()) } /// `Array.prototype.join( separator )` @@ -312,14 +303,13 @@ pub fn join(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu }; let mut elem_strs: Vec = Vec::new(); - let length: i32 = - from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); + let length = i32::from(&this.get_field_slice("length")); for n in 0..length { let elem_str: String = this.get_field_slice(&n.to_string()).to_string(); elem_strs.push(elem_str); } - Ok(to_value(elem_strs.join(&separator))) + Ok(Value::from(elem_strs.join(&separator))) } /// `Array.prototype.toString( separator )` @@ -336,10 +326,9 @@ pub fn join(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let method_name = "join"; - let mut arguments = vec![to_value(",")]; + let mut arguments = vec![Value::from(",")]; // 2. - let mut method: Value = - from_value(this.get_field_slice(method_name)).expect("failed to get Array.prototype.join"); + let mut method = this.get_field_slice(method_name); // 3. if !method.is_function() { method = _ctx @@ -349,7 +338,6 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R .get_field_slice(PROTOTYPE) .get_field_slice("toString"); - method = from_value(method).expect("failed to get Object.prototype.toString"); arguments = Vec::new(); } // 4. @@ -361,7 +349,7 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R }, Err(v) => format!("error: {}", v), }; - Ok(to_value(match_string)) + Ok(Value::from(match_string)) } /// `Array.prototype.reverse()` @@ -377,8 +365,7 @@ pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> R /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse #[allow(clippy::else_if_without_else)] pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let len: i32 = - from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); + let len = i32::from(&this.get_field_slice("length")); let middle: i32 = len.wrapping_div(2); for lower in 0..middle { @@ -395,10 +382,10 @@ pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu this.set_field_slice(&lower.to_string(), upper_value); } else if upper_exists { this.set_field_slice(&lower.to_string(), upper_value); - this.remove_prop(&upper.to_string()); + this.remove_property(&upper.to_string()); } else if lower_exists { this.set_field_slice(&upper.to_string(), lower_value); - this.remove_prop(&lower.to_string()); + this.remove_property(&lower.to_string()); } } @@ -416,11 +403,10 @@ pub fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let len: i32 = - from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); + let len = i32::from(&this.get_field_slice("length")); if len == 0 { - this.set_field_slice("length", to_value(0_i32)); + this.set_field_slice("length", Value::from(0)); // Since length is 0, this will be an Undefined value return Ok(this.get_field_slice(&0.to_string())); } @@ -432,16 +418,16 @@ pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue let to = (k.wrapping_sub(1)).to_string(); let from_value = this.get_field_slice(&from); - if from_value == Gc::new(ValueData::Undefined) { - this.remove_prop(&to); + if from_value == Value::undefined() { + this.remove_property(&to); } else { this.set_field_slice(&to, from_value); } } let final_index = len.wrapping_sub(1); - this.remove_prop(&(final_index).to_string()); - this.set_field_slice("length", to_value(final_index)); + this.remove_property(&(final_index).to_string()); + this.set_field_slice("length", Value::from(final_index)); Ok(first) } @@ -459,8 +445,7 @@ pub fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - let len: i32 = - from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); + let len = i32::from(&this.get_field_slice("length")); let arg_c: i32 = args.len() as i32; if arg_c > 0 { @@ -469,8 +454,8 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV let to = (k.wrapping_add(arg_c).wrapping_sub(1)).to_string(); let from_value = this.get_field_slice(&from); - if from_value == Gc::new(ValueData::Undefined) { - this.remove_prop(&to); + if from_value == Value::undefined() { + this.remove_property(&to); } else { this.set_field_slice(&to, from_value); } @@ -486,8 +471,8 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV } let temp = len.wrapping_add(arg_c); - this.set_field_slice("length", to_value(temp)); - Ok(to_value(temp)) + this.set_field_slice("length", Value::from(temp)); + Ok(Value::from(temp)) } /// `Array.prototype.every( callback, [ thisArg ] )` @@ -505,32 +490,32 @@ pub fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultV /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(to_value( - "missing callback when calling function Array.prototype.every".to_string(), + return Err(Value::from( + "missing callback when calling function Array.prototype.every", )); } let callback = &args[0]; let mut this_arg = if args.len() > 1 { args[1].clone() } else { - Gc::new(ValueData::Undefined) + Value::undefined() }; let mut i = 0; - let max_len: i32 = from_value(this.get_field_slice("length")).unwrap(); + let max_len = i32::from(&this.get_field_slice("length")); let mut len = max_len; while i < len { let element = this.get_field_slice(&i.to_string()); - let arguments = [element, to_value(i), this.clone()]; + let arguments = [element, Value::from(i), this.clone()]; let result = interpreter .call(callback, &mut this_arg, &arguments)? .is_true(); if !result { - return Ok(to_value(false)); + return Ok(Value::from(false)); } - len = min(max_len, from_value(this.get_field_slice("length")).unwrap()); + len = min(max_len, i32::from(&this.get_field_slice("length"))); i += 1; } - Ok(to_value(true)) + Ok(Value::from(true)) } /// `Array.prototype.map( callback, [ thisArg ] )` @@ -546,29 +531,28 @@ pub fn every(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(to_value( + return Err(Value::from( "missing argument 0 when calling function Array.prototype.map", )); } - let callback = args.get(0).cloned().unwrap_or_else(undefined); - let mut this_val = args.get(1).cloned().unwrap_or_else(undefined); + let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); + let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length: i32 = - from_value(this.get_field_slice("length")).expect("Could not get `length` property."); + let length = i32::from(&this.get_field_slice("length")); let new = new_array(&interpreter)?; - let values = (0..length) + let values: Vec = (0..length) .map(|idx| { let element = this.get_field_slice(&idx.to_string()); - let args = [element, to_value(idx), new.clone()]; + let args = [element, Value::from(idx), new.clone()]; interpreter .call(&callback, &mut this_val, &args) - .unwrap_or_else(|_| undefined()) + .unwrap_or_else(|_| Value::undefined()) }) - .collect::>(); + .collect(); construct_array(&new, &values) } @@ -595,17 +579,15 @@ pub fn map(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> R pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { // If no arguments, return -1. Not described in spec, but is what chrome does. if args.is_empty() { - return Ok(to_value(-1)); + return Ok(Value::from(-1)); } let search_element = args[0].clone(); - let len: i32 = from_value(this.get_field_slice("length")) - .expect("Expected array property \"length\" is not set."); + let len = i32::from(&this.get_field_slice("length")); let mut idx = match args.get(1) { Some(from_idx_ptr) => { - let from_idx = from_value(from_idx_ptr.clone()) - .expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument"); + let from_idx = i32::from(from_idx_ptr); if from_idx < 0 { len + from_idx @@ -620,13 +602,13 @@ pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result let check_element = this.get_field_slice(&idx.to_string()).clone(); if check_element == search_element { - return Ok(to_value(idx)); + return Ok(Value::from(idx)); } idx += 1; } - Ok(to_value(-1)) + Ok(Value::from(-1)) } /// `Array.prototype.lastIndexOf( searchElement[, fromIndex ] )` @@ -650,17 +632,15 @@ pub fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { // If no arguments, return -1. Not described in spec, but is what chrome does. if args.is_empty() { - return Ok(to_value(-1)); + return Ok(Value::from(-1)); } let search_element = args[0].clone(); - let len: i32 = from_value(this.get_field_slice("length")) - .expect("Expected array property \"length\" is not set."); + let len = i32::from(&this.get_field_slice("length")); let mut idx = match args.get(1) { Some(from_idx_ptr) => { - let from_idx = from_value(from_idx_ptr.clone()) - .expect("Error parsing \"Array.prototype.indexOf - fromIndex\" argument"); + let from_idx = i32::from(from_idx_ptr); if from_idx >= 0 { min(from_idx, len - 1) @@ -675,13 +655,13 @@ pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> R let check_element = this.get_field_slice(&idx.to_string()).clone(); if check_element == search_element { - return Ok(to_value(idx)); + return Ok(Value::from(idx)); } idx -= 1; } - Ok(to_value(-1)) + Ok(Value::from(-1)) } /// `Array.prototype.find( callback, [thisArg] )` @@ -698,26 +678,26 @@ pub fn last_index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> R /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(to_value( - "missing callback when calling function Array.prototype.find".to_string(), + return Err(Value::from( + "missing callback when calling function Array.prototype.find", )); } let callback = &args[0]; let mut this_arg = if args.len() > 1 { args[1].clone() } else { - Gc::new(ValueData::Undefined) + Value::undefined() }; - let len: i32 = from_value(this.get_field_slice("length")).unwrap(); + let len = i32::from(&this.get_field_slice("length")); for i in 0..len { let element = this.get_field_slice(&i.to_string()); - let arguments = [element.clone(), to_value(i), this.clone()]; + let arguments = [element.clone(), Value::from(i), this.clone()]; let result = interpreter.call(callback, &mut this_arg, &arguments)?; if result.is_true() { return Ok(element); } } - Ok(Gc::new(ValueData::Undefined)) + Ok(Value::undefined()) } /// `Array.prototype.findIndex( predicate [ , thisArg ] )` @@ -734,33 +714,29 @@ pub fn find(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(to_value( - "Missing argument for Array.prototype.findIndex".to_string(), + return Err(Value::from( + "Missing argument for Array.prototype.findIndex", )); } let predicate_arg = args.get(0).expect("Could not get `predicate` argument."); - let mut this_arg = args - .get(1) - .cloned() - .unwrap_or_else(|| Gc::new(ValueData::Undefined)); + let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length: i32 = - from_value(this.get_field_slice("length")).expect("Could not get `length` property."); + let length = i32::from(&this.get_field_slice("length")); for i in 0..length { let element = this.get_field_slice(&i.to_string()); - let arguments = [element, to_value(i), this.clone()]; + let arguments = [element, Value::from(i), this.clone()]; let result = interpreter.call(predicate_arg, &mut this_arg, &arguments)?; if result.is_true() { - return Ok(Gc::new(ValueData::Rational(f64::from(i)))); + return Ok(Value::rational(f64::from(i))); } } - Ok(Gc::new(ValueData::Rational(f64::from(-1)))) + Ok(Value::rational(-1_f64)) } /// `Array.prototype.fill( value[, start[, end]] )` @@ -775,8 +751,8 @@ pub fn find_index(this: &mut Value, args: &[Value], interpreter: &mut Interprete /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - let len: i32 = from_value(this.get_field_slice("length")).expect("Could not get argument"); - let default_value = undefined(); + let len: i32 = i32::from(&this.get_field_slice("length")); + let default_value = Value::undefined(); let value = args.get(0).unwrap_or(&default_value); let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32; let relative_end_val = args.get(2).unwrap_or(&default_value); @@ -814,23 +790,19 @@ pub fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - let search_element = args - .get(0) - .cloned() - .unwrap_or_else(|| Gc::new(ValueData::Undefined)); + let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined); - let length: i32 = - from_value(this.get_field_slice("length")).expect("Could not get `length` property."); + let length = i32::from(&this.get_field_slice("length")); for idx in 0..length { let check_element = this.get_field_slice(&idx.to_string()).clone(); if check_element == search_element { - return Ok(to_value(true)); + return Ok(Value::from(true)); } } - Ok(to_value(false)) + Ok(Value::from(false)) } /// `Array.prototype.slice( [begin[, end]] )` @@ -849,15 +821,14 @@ pub fn includes_value(this: &mut Value, args: &[Value], _: &mut Interpreter) -> /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { let new_array = new_array(interpreter)?; - let len: i32 = - from_value(this.get_field_slice("length")).expect("Could not convert argument to i32"); + let len = i32::from(&this.get_field_slice("length")); let start = match args.get(0) { - Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"), + Some(v) => i32::from(v), None => 0, }; let end = match args.get(1) { - Some(v) => from_value(v.clone()).expect("failed to parse argument for Array method"), + Some(v) => i32::from(v), None => len, }; @@ -881,7 +852,7 @@ pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ); new_array_len = new_array_len.wrapping_add(1); } - new_array.set_field_slice("length", to_value(new_array_len)); + new_array.set_field_slice("length", Value::from(new_array_len)); Ok(new_array) } @@ -898,16 +869,15 @@ pub fn slice(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(to_value( + return Err(Value::from( "missing argument 0 when calling function Array.prototype.filter", )); } - let callback = args.get(0).cloned().unwrap_or_else(undefined); - let mut this_val = args.get(1).cloned().unwrap_or_else(undefined); + let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); + let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length: i32 = - from_value(this.get_field_slice("length")).expect("Could not get `length` property."); + let length = i32::from(&this.get_field_slice("length")); let new = new_array(&interpreter)?; @@ -915,11 +885,11 @@ pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) - .filter_map(|idx| { let element = this.get_field_slice(&idx.to_string()); - let args = [element.clone(), to_value(idx), new.clone()]; + let args = [element.clone(), Value::from(idx), new.clone()]; let callback_result = interpreter .call(&callback, &mut this_val, &args) - .unwrap_or_else(|_| undefined()); + .unwrap_or_else(|_| Value::undefined()); if callback_result.is_true() { Some(element) @@ -949,42 +919,42 @@ pub fn filter(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) - /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some pub fn some(this: &mut Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(to_value( - "missing callback when calling function Array.prototype.some".to_string(), + return Err(Value::from( + "missing callback when calling function Array.prototype.some", )); } let callback = &args[0]; let mut this_arg = if args.len() > 1 { args[1].clone() } else { - Gc::new(ValueData::Undefined) + Value::undefined() }; let mut i = 0; - let max_len: i32 = from_value(this.get_field_slice("length")).unwrap(); + let max_len = i32::from(&this.get_field_slice("length")); let mut len = max_len; while i < len { let element = this.get_field_slice(&i.to_string()); - let arguments = [element, to_value(i), this.clone()]; + let arguments = [element, Value::from(i), this.clone()]; let result = interpreter .call(callback, &mut this_arg, &arguments)? .is_true(); if result { - return Ok(to_value(true)); + return Ok(Value::from(true)); } // the length of the array must be updated because the callback can mutate it. - len = min(max_len, from_value(this.get_field_slice("length")).unwrap()); + len = min(max_len, i32::from(&this.get_field_slice("length"))); i += 1; } - Ok(to_value(false)) + Ok(Value::from(false)) } /// Create a new `Array` object. pub fn create(global: &Value) -> Value { // Create prototype - let prototype = ValueData::new_obj(None); - let length = Property::default().value(to_value(0_i32)); + let prototype = Value::new_object(None); + let length = Property::default().value(Value::from(0)); - prototype.set_prop_slice("length", length); + prototype.set_property_slice("length", length); make_builtin_fn!(concat, named "concat", with length 1, of prototype); make_builtin_fn!(push, named "push", with length 1, of prototype); diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index 5b01fedda5..4e92e11be3 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -15,7 +15,7 @@ mod tests; use crate::{ builtins::{ object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE}, - value::{to_value, ResultValue, Value, ValueData}, + value::{ResultValue, Value, ValueData}, }, exec::Interpreter, }; @@ -29,7 +29,7 @@ pub fn construct_boolean(this: &mut Value, args: &[Value], _: &mut Interpreter) if let Some(ref value) = args.get(0) { this.set_internal_slot("BooleanData", to_boolean(value)); } else { - this.set_internal_slot("BooleanData", to_boolean(&to_value(false))); + this.set_internal_slot("BooleanData", to_boolean(&Value::from(false))); } // no need to return `this` as its passed by reference @@ -41,7 +41,7 @@ pub fn call_boolean(_: &mut Value, args: &[Value], _: &mut Interpreter) -> Resul // Get the argument, if any match args.get(0) { Some(ref value) => Ok(to_boolean(value)), - None => Ok(to_boolean(&to_value(false))), + None => Ok(to_boolean(&Value::from(false))), } } @@ -55,7 +55,7 @@ pub fn call_boolean(_: &mut Value, args: &[Value], _: &mut Interpreter) -> Resul /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let b = this_boolean_value(this); - Ok(to_value(b.to_string())) + Ok(Value::from(b.to_string())) } /// The valueOf() method returns the primitive value of a `Boolean` object. @@ -75,12 +75,12 @@ pub fn value_of(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVal /// Creates a new boolean value from the input pub fn to_boolean(value: &Value) -> Value { match *value.deref().borrow() { - ValueData::Object(_) => to_value(true), - ValueData::String(ref s) if !s.is_empty() => to_value(true), - ValueData::Rational(n) if n != 0.0 && !n.is_nan() => to_value(true), - ValueData::Integer(n) if n != 0 => to_value(true), - ValueData::Boolean(v) => to_value(v), - _ => to_value(false), + ValueData::Object(_) => Value::from(true), + ValueData::String(ref s) if !s.is_empty() => Value::from(true), + ValueData::Rational(n) if n != 0.0 && !n.is_nan() => Value::from(true), + ValueData::Integer(n) if n != 0 => Value::from(true), + ValueData::Boolean(v) => Value::from(v), + _ => Value::from(false), } } @@ -92,9 +92,9 @@ pub fn to_boolean(value: &Value) -> Value { /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue pub fn this_boolean_value(value: &Value) -> Value { match *value.deref().borrow() { - ValueData::Boolean(v) => to_value(v), + ValueData::Boolean(v) => Value::from(v), ValueData::Object(ref v) => (v).deref().borrow().get_internal_slot("BooleanData"), - _ => to_value(false), + _ => Value::from(false), } } @@ -102,8 +102,8 @@ pub fn this_boolean_value(value: &Value) -> Value { pub fn create(global: &Value) -> Value { // Create Prototype // https://tc39.es/ecma262/#sec-properties-of-the-boolean-prototype-object - let prototype = ValueData::new_obj(Some(global)); - prototype.set_internal_slot("BooleanData", to_boolean(&to_value(false))); + let prototype = Value::new_object(Some(global)); + prototype.set_internal_slot("BooleanData", to_boolean(&Value::from(false))); make_builtin_fn!(to_string, named "toString", of prototype); make_builtin_fn!(value_of, named "valueOf", of prototype); diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index 2d625016f7..69c3599a5b 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -5,7 +5,7 @@ use crate::{builtins::value::same_value, forward, forward_val}; #[test] fn check_boolean_constructor_is_function() { - let global = ValueData::new_obj(None); + let global = Value::new_object(None); let boolean_constructor = create(&global); assert_eq!(boolean_constructor.is_function(), true); } diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 6783b57d06..bd5cf26cea 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -19,9 +19,7 @@ mod tests; use crate::{ builtins::{ object::InternalState, - value::{ - display_obj, from_value, to_value, undefined, FromValue, ResultValue, Value, ValueData, - }, + value::{display_obj, ResultValue, Value}, }, exec::Interpreter, }; @@ -48,10 +46,11 @@ pub enum LogMessage { } /// Helper function that returns the argument at a specified index. -fn get_arg_at_index(args: &[Value], index: usize) -> Option { - args.get(index) - .cloned() - .map(|s| from_value::(s).expect("Convert error")) +fn get_arg_at_index<'a, T>(args: &'a [Value], index: usize) -> Option +where + T: From<&'a Value> + Default, +{ + args.get(index).map(|s| T::from(s)) } /// Helper function for logging messages. @@ -147,12 +146,12 @@ pub fn assert(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa let mut args: Vec = args.iter().skip(1).cloned().collect(); let message = "Assertion failed".to_string(); if args.is_empty() { - args.push(to_value::(message)); + args.push(Value::from(message)); } else if !args[0].is_string() { - args.insert(0, to_value::(message)); + args.insert(0, Value::from(message)); } else { let concat = format!("{}: {}", message, args[0]); - args[0] = to_value::(concat); + args[0] = Value::from(concat); } this.with_internal_state_ref(|state| { @@ -160,7 +159,7 @@ pub fn assert(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVa }); } - Ok(undefined()) + Ok(Value::undefined()) } /// `console.clear()` @@ -178,7 +177,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue state.groups.clear(); }); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.debug(...data)` @@ -193,7 +192,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.error(...data)` @@ -208,7 +207,7 @@ pub fn debug(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Error(formatter(&args[..])), state)); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.info(...data)` @@ -223,7 +222,7 @@ pub fn error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Info(formatter(&args[..])), state)); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.log(...data)` @@ -238,7 +237,7 @@ pub fn info(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log pub fn log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Log(formatter(&args[..])), state)); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.trace(...data)` @@ -264,7 +263,7 @@ pub fn trace(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal }); } - Ok(undefined()) + Ok(Value::undefined()) } /// `console.warn(...data)` @@ -279,7 +278,7 @@ pub fn trace(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn pub fn warn(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|state| logger(LogMessage::Warn(formatter(&args[..])), state)); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.count(label)` @@ -303,7 +302,7 @@ pub fn count(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal logger(LogMessage::Info(format!("{} {}", msg, c)), state); }); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.countReset(label)` @@ -325,7 +324,7 @@ pub fn count_reset(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res logger(LogMessage::Warn(format!("countReset {}", label)), state); }); - Ok(undefined()) + Ok(Value::undefined()) } /// Returns current system time in ms. @@ -361,7 +360,7 @@ pub fn time(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu } }); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.timeLog(label, ...data)` @@ -393,7 +392,7 @@ pub fn time_log(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result } }); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.timeEnd(label)` @@ -424,7 +423,7 @@ pub fn time_end(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Result } }); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.group(...data)` @@ -445,7 +444,7 @@ pub fn group(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultVal state.groups.push(group_label); }); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.groupEnd(label)` @@ -463,7 +462,7 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa state.groups.pop(); }); - Ok(undefined()) + Ok(Value::undefined()) } /// `console.dir(item, options)` @@ -478,18 +477,19 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_mut(|state: &mut ConsoleState| { + let undefined = Value::undefined(); logger( - LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined()), true)), + LogMessage::Info(display_obj(args.get(0).unwrap_or(&undefined), true)), state, ); }); - Ok(undefined()) + Ok(Value::undefined()) } /// Create a new `console` object pub fn create(global: &Value) -> Value { - let console = ValueData::new_obj(Some(global)); + let console = Value::new_object(Some(global)); make_builtin_fn!(assert, named "assert", of console); make_builtin_fn!(clear, named "clear", of console); diff --git a/boa/src/builtins/console/tests.rs b/boa/src/builtins/console/tests.rs index 9a3a59fc9c..88b88913bc 100644 --- a/boa/src/builtins/console/tests.rs +++ b/boa/src/builtins/console/tests.rs @@ -1,5 +1,4 @@ -use crate::builtins::{console::formatter, value::ValueData}; -use gc::Gc; +use crate::builtins::{console::formatter, value::Value}; #[test] fn formatter_no_args_is_empty_string() { @@ -8,14 +7,14 @@ fn formatter_no_args_is_empty_string() { #[test] fn formatter_empty_format_string_is_empty_string() { - let val = Gc::new(ValueData::String("".to_string())); + let val = Value::string("".to_string()); let res = formatter(&[val]); assert_eq!(res, ""); } #[test] fn formatter_format_without_args_renders_verbatim() { - let val = [Gc::new(ValueData::String("%d %s %% %f".to_string()))]; + let val = [Value::string("%d %s %% %f".to_string())]; let res = formatter(&val); assert_eq!(res, "%d %s %% %f"); } @@ -23,9 +22,9 @@ fn formatter_format_without_args_renders_verbatim() { #[test] fn formatter_empty_format_string_concatenates_rest_of_args() { let val = [ - Gc::new(ValueData::String("".to_string())), - Gc::new(ValueData::String("to powinno zostać".to_string())), - Gc::new(ValueData::String("połączone".to_string())), + Value::string("".to_string()), + Value::string("to powinno zostać".to_string()), + Value::string("połączone".to_string()), ]; let res = formatter(&val); assert_eq!(res, " to powinno zostać połączone"); @@ -34,12 +33,10 @@ fn formatter_empty_format_string_concatenates_rest_of_args() { #[test] fn formatter_utf_8_checks() { let val = [ - Gc::new(ValueData::String( - "Są takie chwile %dą %są tu%sów %привет%ź".to_string(), - )), - Gc::new(ValueData::Integer(123)), - Gc::new(ValueData::Rational(1.23)), - Gc::new(ValueData::String("ł".to_string())), + Value::string("Są takie chwile %dą %są tu%sów %привет%ź".to_string()), + Value::integer(123), + Value::rational(1.23), + Value::string("ł".to_string()), ]; let res = formatter(&val); assert_eq!(res, "Są takie chwile 123ą 1.23ą tułów %привет%ź"); @@ -48,8 +45,8 @@ fn formatter_utf_8_checks() { #[test] fn formatter_trailing_format_leader_renders() { let val = [ - Gc::new(ValueData::String("%%%%%".to_string())), - Gc::new(ValueData::String("|".to_string())), + Value::string("%%%%%".to_string()), + Value::string("|".to_string()), ]; let res = formatter(&val); assert_eq!(res, "%%% |") @@ -58,10 +55,7 @@ fn formatter_trailing_format_leader_renders() { #[test] #[allow(clippy::approx_constant)] fn formatter_float_format_works() { - let val = [ - Gc::new(ValueData::String("%f".to_string())), - Gc::new(ValueData::Rational(3.1415)), - ]; + let val = [Value::string("%f".to_string()), Value::rational(3.1415)]; let res = formatter(&val); assert_eq!(res, "3.141500") } diff --git a/boa/src/builtins/error.rs b/boa/src/builtins/error.rs index 949d28ba15..1ca7d5f7e6 100644 --- a/boa/src/builtins/error.rs +++ b/boa/src/builtins/error.rs @@ -13,7 +13,7 @@ use crate::{ builtins::{ object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE}, - value::{to_value, undefined, ResultValue, Value, ValueData}, + value::{ResultValue, Value}, }, exec::Interpreter, }; @@ -23,7 +23,7 @@ pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Resu if !args.is_empty() { this.set_field_slice( "message", - to_value( + Value::from( args.get(0) .expect("failed getting error message") .to_string(), @@ -33,7 +33,7 @@ pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Resu // This value is used by console.log and other routines to match Object type // to its Javascript Identifier (global constructor method name) this.set_kind(ObjectKind::Error); - Ok(undefined()) + Ok(Value::undefined()) } /// `Error.prototype.toString()` @@ -49,14 +49,14 @@ pub fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Resu pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let name = this.get_field_slice("name"); let message = this.get_field_slice("message"); - Ok(to_value(format!("{}: {}", name, message))) + Ok(Value::from(format!("{}: {}", name, message))) } /// Create a new `Error` object. pub fn create(global: &Value) -> Value { - let prototype = ValueData::new_obj(Some(global)); - prototype.set_field_slice("message", to_value("")); - prototype.set_field_slice("name", to_value("Error")); + let prototype = Value::new_object(Some(global)); + prototype.set_field_slice("message", Value::from("")); + prototype.set_field_slice("name", Value::from("Error")); make_builtin_fn!(to_string, named "toString", of prototype); make_constructor_fn!(make_error, global, prototype) } diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 3071c79fad..e0c7a346bc 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -16,7 +16,7 @@ use crate::{ array, object::{Object, ObjectInternalMethods, ObjectKind, PROTOTYPE}, property::Property, - value::{to_value, undefined, ResultValue, Value, ValueData}, + value::{ResultValue, Value}, }, environment::lexical_environment::{new_function_environment, Environment}, exec::Executor, @@ -329,10 +329,10 @@ pub fn create_function_prototype() { pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { let len = arguments_list.len(); let mut obj = Object::default(); - obj.set_internal_slot("ParameterMap", undefined()); + obj.set_internal_slot("ParameterMap", Value::undefined()); // Set length let mut length = Property::default(); - length = length.writable(true).value(to_value(len)); + length = length.writable(true).value(Value::from(len)); // Define length as a property obj.define_own_property("length".to_string(), length); let mut index: usize = 0; @@ -349,7 +349,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { index += 1; } - to_value(obj) + Value::from(obj) } /// Create new function `[[Construct]]` @@ -361,7 +361,7 @@ pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> Resu } pub fn create(global: &Value) -> Value { - let prototype = ValueData::new_obj(Some(global)); + let prototype = Value::new_object(Some(global)); make_constructor_fn!(make_function, make_function, global, prototype) } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index b0dce66d49..592a58fad5 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -13,7 +13,7 @@ //! [json]: https://www.json.org/json-en.html //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON -use crate::builtins::value::{to_value, ResultValue, Value, ValueData}; +use crate::builtins::value::{ResultValue, Value}; use crate::exec::Interpreter; use serde_json::{self, Value as JSONValue}; @@ -41,8 +41,8 @@ pub fn parse(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue .clone() .to_string(), ) { - Ok(json) => Ok(to_value(json)), - Err(err) => Err(to_value(err.to_string())), + Ok(json) => Ok(Value::from(json)), + Err(err) => Err(Value::from(err.to_string())), } } @@ -65,17 +65,17 @@ pub fn parse(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue pub fn stringify(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("cannot get argument for JSON.stringify"); let json = obj.to_json().to_string(); - Ok(to_value(json)) + Ok(Value::from(json)) } /// Create a new `JSON` object. pub fn create(global: &Value) -> Value { - let json = ValueData::new_obj(Some(global)); + let json = Value::new_object(Some(global)); make_builtin_fn!(parse, named "parse", with length 2, of json); make_builtin_fn!(stringify, named "stringify", with length 3, of json); - to_value(json) + json } /// Initialise the `JSON` object on the global object. diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index c2ec4b5ca4..2419bcfcf9 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -12,7 +12,7 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math use crate::{ - builtins::value::{from_value, to_value, ResultValue, Value, ValueData}, + builtins::value::{ResultValue, Value}, exec::Interpreter, }; use rand::random; @@ -30,12 +30,10 @@ mod tests; /// [spec]: https://tc39.es/ecma262/#sec-math.abs /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs pub fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .abs() + f64::from(args.get(0).expect("Could not get argument")).abs() })) } @@ -48,12 +46,10 @@ pub fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.acos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos pub fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .acos() + f64::from(args.get(0).expect("Could not get argument")).acos() })) } @@ -66,12 +62,10 @@ pub fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.acosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh pub fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .acosh() + f64::from(args.get(0).expect("Could not get argument")).acosh() })) } @@ -84,12 +78,10 @@ pub fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-math.asin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin pub fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .asin() + f64::from(args.get(0).expect("Could not get argument")).asin() })) } @@ -102,12 +94,10 @@ pub fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.asinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh pub fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .asinh() + f64::from(args.get(0).expect("Could not get argument")).asinh() })) } @@ -120,12 +110,10 @@ pub fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-math.atan /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan pub fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .atan() + f64::from(args.get(0).expect("Could not get argument")).atan() })) } @@ -138,12 +126,10 @@ pub fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.atanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh pub fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .atanh() + f64::from(args.get(0).expect("Could not get argument")).atanh() })) } @@ -156,11 +142,10 @@ pub fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-math.atan2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") + f64::from(args.get(0).expect("Could not get argument")) .atan2(args.get(1).expect("Could not get argument").to_number()) })) } @@ -174,12 +159,10 @@ pub fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt pub fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .cbrt() + f64::from(args.get(0).expect("Could not get argument")).cbrt() })) } @@ -192,12 +175,10 @@ pub fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.ceil /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil pub fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .ceil() + f64::from(args.get(0).expect("Could not get argument")).ceil() })) } @@ -210,12 +191,10 @@ pub fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.cos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos pub fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .cos() + f64::from(args.get(0).expect("Could not get argument")).cos() })) } @@ -228,12 +207,10 @@ pub fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.cosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh pub fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .cosh() + f64::from(args.get(0).expect("Could not get argument")).cosh() })) } @@ -246,12 +223,10 @@ pub fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.exp /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp pub fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .exp() + f64::from(args.get(0).expect("Could not get argument")).exp() })) } @@ -264,12 +239,10 @@ pub fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.floor /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor pub fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .floor() + f64::from(args.get(0).expect("Could not get argument")).floor() })) } @@ -282,11 +255,10 @@ pub fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-math.log /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log pub fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - let value = from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64"); + let value = f64::from(args.get(0).expect("Could not get argument")); if value <= 0.0 { f64::NAN @@ -305,11 +277,10 @@ pub fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.log10 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10 pub fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - let value = from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64"); + let value = f64::from(args.get(0).expect("Could not get argument")); if value <= 0.0 { f64::NAN @@ -328,11 +299,10 @@ pub fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-math.log2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2 pub fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - let value = from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64"); + let value = f64::from(args.get(0).expect("Could not get argument")); if value <= 0.0 { f64::NAN @@ -353,10 +323,10 @@ pub fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let mut max = f64::NEG_INFINITY; for arg in args { - let num = arg.to_number(); + let num = f64::from(arg); max = max.max(num); } - Ok(to_value(max)) + Ok(Value::from(max)) } /// Get the minimum of several numbers. @@ -370,10 +340,10 @@ pub fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { pub fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let mut max = f64::INFINITY; for arg in args { - let num = arg.to_number(); + let num = f64::from(arg); max = max.min(num); } - Ok(to_value(max)) + Ok(Value::from(max)) } /// Raise a number to a power. @@ -385,11 +355,9 @@ pub fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.pow /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.len() >= 2 { - let num: f64 = from_value(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64"); - let power: f64 = from_value(args.get(1).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64"); + Ok(Value::from(if args.len() >= 2 { + let num = f64::from(args.get(0).expect("Could not get argument")); + let power = f64::from(args.get(1).expect("Could not get argument")); num.powf(power) } else { f64::NAN @@ -405,7 +373,7 @@ pub fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.random /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random pub fn _random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(random::())) + Ok(Value::from(random::())) } /// Round a number to the nearest integer. @@ -417,12 +385,10 @@ pub fn _random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.round /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round pub fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .round() + f64::from(args.get(0).expect("Could not get argument")).round() })) } @@ -435,11 +401,10 @@ pub fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-math.sign /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign pub fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - let value = from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64"); + let value = f64::from(args.get(0).expect("Could not get argument")); if value == 0.0 || value == -0.0 { value @@ -458,12 +423,10 @@ pub fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.sin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin pub fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .sin() + f64::from(args.get(0).expect("Could not get argument")).sin() })) } @@ -476,12 +439,10 @@ pub fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.sinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh pub fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .sinh() + f64::from(args.get(0).expect("Could not get argument")).sinh() })) } @@ -494,22 +455,18 @@ pub fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt pub fn sqrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .sqrt() + f64::from(args.get(0).expect("Could not get argument")).sqrt() })) } /// Get the tangent of a number pub fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .tan() + f64::from(args.get(0).expect("Could not get argument")).tan() })) } @@ -522,12 +479,10 @@ pub fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.tanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh pub fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .tanh() + f64::from(args.get(0).expect("Could not get argument")).tanh() })) } @@ -540,27 +495,25 @@ pub fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { /// [spec]: https://tc39.es/ecma262/#sec-math.trunc /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc pub fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(if args.is_empty() { + Ok(Value::from(if args.is_empty() { f64::NAN } else { - from_value::(args.get(0).expect("Could not get argument").clone()) - .expect("Could not convert argument to f64") - .trunc() + f64::from(args.get(0).expect("Could not get argument")).trunc() })) } /// Create a new `Math` object pub fn create(global: &Value) -> Value { - let math = ValueData::new_obj(Some(global)); - - math.set_field_slice("E", to_value(f64::consts::E)); - math.set_field_slice("LN2", to_value(f64::consts::LN_2)); - math.set_field_slice("LN10", to_value(f64::consts::LN_10)); - math.set_field_slice("LOG2E", to_value(f64::consts::LOG2_E)); - math.set_field_slice("LOG10E", to_value(f64::consts::LOG10_E)); - math.set_field_slice("SQRT1_2", to_value(0.5_f64.sqrt())); - math.set_field_slice("SQRT2", to_value(f64::consts::SQRT_2)); - math.set_field_slice("PI", to_value(f64::consts::PI)); + let math = Value::new_object(Some(global)); + + math.set_field_slice("E", Value::from(f64::consts::E)); + math.set_field_slice("LN2", Value::from(f64::consts::LN_2)); + math.set_field_slice("LN10", Value::from(f64::consts::LN_10)); + math.set_field_slice("LOG2E", Value::from(f64::consts::LOG2_E)); + math.set_field_slice("LOG10E", Value::from(f64::consts::LOG10_E)); + math.set_field_slice("SQRT1_2", Value::from(0.5_f64.sqrt())); + math.set_field_slice("SQRT2", Value::from(f64::consts::SQRT_2)); + math.set_field_slice("PI", Value::from(f64::consts::PI)); make_builtin_fn!(abs, named "abs", with length 1, of math); make_builtin_fn!(acos, named "acos", with length 1, of math); make_builtin_fn!(acosh, named "acosh", with length 1, of math); diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 5b50104fcc..b1aaec67db 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -12,8 +12,8 @@ macro_rules! make_builtin_fn { let mut new_func = crate::builtins::object::Object::function(); new_func.set_call(func); - let new_func_obj = to_value(new_func); - new_func_obj.set_field_slice("length", to_value($l)); + let new_func_obj = Value::from(new_func); + new_func_obj.set_field_slice("length", Value::from($l)); $p.set_field_slice($name, new_func_obj); }; ($fn:ident, named $name:expr, of $p:ident) => { @@ -42,7 +42,7 @@ macro_rules! make_constructor_fn { constructor_obj.set_construct(constructor_fn); constructor_obj.set_internal_slot("__proto__", func_prototype); - let constructor_val = to_value(constructor_obj); + let constructor_val = Value::from(constructor_obj); // Set proto.constructor -> constructor_obj $proto.set_field_slice("constructor", constructor_val.clone()); @@ -71,7 +71,7 @@ macro_rules! make_constructor_fn { constructor_obj.set_construct(construct_fn); constructor_obj.set_call(call_fn); constructor_obj.set_internal_slot("__proto__", func_prototype); - let constructor_val = to_value(constructor_obj); + let constructor_val = Value::from(constructor_obj); // Set proto.constructor -> constructor_obj $proto.set_field_slice("constructor", constructor_val.clone()); diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 2439082153..dac495980e 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -19,7 +19,7 @@ mod tests; use crate::{ builtins::{ object::{internal_methods_trait::ObjectInternalMethods, Object, PROTOTYPE}, - value::{to_value, ResultValue, Value, ValueData}, + value::{ResultValue, Value, ValueData}, }, exec::Interpreter, }; @@ -30,19 +30,19 @@ fn to_number(value: &Value) -> Value { match *value.deref().borrow() { ValueData::Boolean(b) => { if b { - to_value(1) + Value::from(1) } else { - to_value(0) + Value::from(0) } } - ValueData::Symbol(_) | ValueData::Undefined => to_value(f64::NAN), - ValueData::Integer(i) => to_value(f64::from(i)), + ValueData::Symbol(_) | ValueData::Undefined => Value::from(f64::NAN), + ValueData::Integer(i) => Value::from(f64::from(i)), ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"), - ValueData::Null => to_value(0), - ValueData::Rational(n) => to_value(n), + ValueData::Null => Value::from(0), + ValueData::Rational(n) => Value::from(n), ValueData::String(ref s) => match s.parse::() { - Ok(n) => to_value(n), - Err(_) => to_value(f64::NAN), + Ok(n) => Value::from(n), + Err(_) => Value::from(f64::NAN), }, } } @@ -60,7 +60,7 @@ fn num_to_exponential(n: f64) -> String { pub fn make_number(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let data = match args.get(0) { Some(ref value) => to_number(value), - None => to_number(&to_value(0)), + None => to_number(&Value::from(0)), }; this.set_internal_slot("NumberData", data); Ok(this.clone()) @@ -72,7 +72,7 @@ pub fn make_number(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> pub fn call_number(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let data = match args.get(0) { Some(ref value) => to_number(value), - None => to_number(&to_value(0)), + None => to_number(&Value::from(0)), }; Ok(data) } @@ -90,7 +90,7 @@ pub fn call_number(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> pub fn to_exponential(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let this_num = to_number(this).to_number(); let this_str_num = num_to_exponential(this_num); - Ok(to_value(this_str_num)) + Ok(Value::from(this_str_num)) } /// `Number.prototype.toFixed( [digits] )` @@ -113,7 +113,7 @@ pub fn to_fixed(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> Res None => 0, }; let this_fixed_num = format!("{:.*}", precision, this_num); - Ok(to_value(this_fixed_num)) + Ok(Value::from(this_fixed_num)) } /// `Number.prototype.toLocaleString( [locales [, options]] )` @@ -132,7 +132,7 @@ pub fn to_fixed(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> Res pub fn to_locale_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let this_num = to_number(this).to_number(); let this_str_num = format!("{}", this_num); - Ok(to_value(this_str_num)) + Ok(Value::from(this_str_num)) } /// `Number.prototype.toPrecision( [precision] )` @@ -170,7 +170,7 @@ pub fn to_precision(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString pub fn to_string(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - Ok(to_value(format!("{}", to_number(this).to_number()))) + Ok(Value::from(format!("{}", to_number(this).to_number()))) } /// `Number.prototype.toString()` @@ -189,8 +189,8 @@ pub fn value_of(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> Re /// Create a new `Number` object pub fn create(global: &Value) -> Value { - let prototype = ValueData::new_obj(Some(global)); - prototype.set_internal_slot("NumberData", to_value(0)); + let prototype = Value::new_object(Some(global)); + prototype.set_internal_slot("NumberData", Value::from(0)); make_builtin_fn!(to_exponential, named "toExponential", with length 1, of prototype); make_builtin_fn!(to_fixed, named "toFixed", with length 1, of prototype); diff --git a/boa/src/builtins/number/tests.rs b/boa/src/builtins/number/tests.rs index c66fe0be34..fc9aea1cbc 100644 --- a/boa/src/builtins/number/tests.rs +++ b/boa/src/builtins/number/tests.rs @@ -1,10 +1,10 @@ #![allow(clippy::float_cmp)] -use crate::{builtins::value::ValueData, exec::Executor, forward, forward_val, realm::Realm}; +use crate::{builtins::value::Value, exec::Executor, forward, forward_val, realm::Realm}; #[test] fn check_number_constructor_is_function() { - let global = ValueData::new_obj(None); + let global = Value::new_object(None); let number_constructor = super::create(&global); assert_eq!(number_constructor.is_function(), true); } diff --git a/boa/src/builtins/object/internal_methods_trait.rs b/boa/src/builtins/object/internal_methods_trait.rs index a41ec5d9c7..d99531bed8 100644 --- a/boa/src/builtins/object/internal_methods_trait.rs +++ b/boa/src/builtins/object/internal_methods_trait.rs @@ -8,9 +8,8 @@ use crate::builtins::{ object::{Object, INSTANCE_PROTOTYPE}, property::Property, - value::{same_value, to_value, Value, ValueData}, + value::{same_value, Value, ValueData}, }; -use gc::Gc; use std::borrow::Borrow; use std::ops::Deref; @@ -70,7 +69,7 @@ pub trait ObjectInternalMethods { /// /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions fn prevent_extensions(&mut self) -> bool { - self.set_internal_slot("extensible", to_value(false)); + self.set_internal_slot("extensible", Value::from(false)); true } @@ -108,7 +107,7 @@ pub trait ObjectInternalMethods { // parent will either be null or an Object let parent = self.get_prototype_of(); if parent.is_null() { - return Gc::new(ValueData::Undefined); + return Value::undefined(); } let parent_obj = Object::from(&parent).expect("Failed to get object"); @@ -122,11 +121,11 @@ pub trait ObjectInternalMethods { let getter = desc.get.clone(); if getter.is_none() || getter.expect("Failed to get object").is_undefined() { - return Gc::new(ValueData::Undefined); + return Value::undefined(); } // TODO!!!!! Call getter from here - Gc::new(ValueData::Undefined) + Value::undefined() } /// [[Set]] @@ -169,7 +168,7 @@ pub trait ObjectInternalMethods { } fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { - let mut current = self.get_own_property(&to_value(property_key.to_string())); + let mut current = self.get_own_property(&Value::from(property_key.to_string())); let extensible = self.is_extensible(); // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 2c23f1a9fd..d7b6ed6f08 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -17,11 +17,11 @@ use crate::{ builtins::{ function::Function, property::Property, - value::{from_value, same_value, to_value, undefined, ResultValue, Value, ValueData}, + value::{same_value, ResultValue, Value, ValueData}, }, exec::Interpreter, }; -use gc::{unsafe_empty_trace, Finalize, Gc, GcCell, Trace}; +use gc::{unsafe_empty_trace, Finalize, Trace}; use rustc_hash::FxHashMap; use std::{ borrow::Borrow, @@ -103,7 +103,7 @@ impl ObjectInternalMethods for Object { while !done { if p.is_null() { done = true - } else if same_value(&to_value(self.clone()), &p, false) { + } else if same_value(&Value::from(self.clone()), &p, false) { return false; } else { p = p.get_internal_slot(PROTOTYPE); @@ -132,7 +132,7 @@ impl ObjectInternalMethods for Object { fn get_internal_slot(&self, name: &str) -> Value { match self.internal_slots.get(name) { Some(v) => v.clone(), - None => Gc::new(ValueData::Null), + None => Value::null(), } } @@ -208,7 +208,7 @@ impl ObjectInternalMethods for Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-defineownproperty-p-desc #[allow(clippy::option_unwrap_used)] fn define_own_property(&mut self, property_key: String, desc: Property) -> bool { - let mut current = self.get_own_property(&to_value(property_key.to_string())); + let mut current = self.get_own_property(&Value::from(property_key.to_string())); let extensible = self.is_extensible(); // https://tc39.es/ecma262/#sec-validateandapplypropertydescriptor @@ -346,7 +346,7 @@ impl Object { construct: None, }; - object.set_internal_slot("extensible", to_value(true)); + object.set_internal_slot("extensible", Value::from(true)); object } @@ -362,7 +362,7 @@ impl Object { construct: None, }; - object.set_internal_slot("extensible", to_value(true)); + object.set_internal_slot("extensible", Value::from(true)); object } @@ -378,7 +378,7 @@ impl Object { obj.internal_slots .insert(INSTANCE_PROTOTYPE.to_string(), proto); obj.internal_slots - .insert("extensible".to_string(), to_value(true)); + .insert("extensible".to_string(), Value::from(true)); obj } @@ -528,14 +528,12 @@ unsafe impl Trace for ObjectKind { pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(arg) = args.get(0) { if !arg.is_null_or_undefined() { - return Ok(Gc::new(ValueData::Object(Box::new(GcCell::new( - Object::from(arg).unwrap(), - ))))); + return Ok(Value::object(Object::from(arg).unwrap())); } } let global = &ctx.realm.global_obj; - let object = ValueData::new_obj(Some(global)); + let object = Value::new_object(Some(global)); Ok(object) } @@ -557,12 +555,10 @@ pub fn set_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> R /// Define a property in an object pub fn define_property(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object"); - let prop = from_value::(args.get(1).expect("Cannot get object").clone()) - .expect("Cannot get object"); - let desc = from_value::(args.get(2).expect("Cannot get object").clone()) - .expect("Cannot get object"); - obj.set_prop(prop, desc); - Ok(undefined()) + let prop = String::from(args.get(1).expect("Cannot get object")); + let desc = Property::from(args.get(2).expect("Cannot get object")); + obj.set_property(prop, desc); + Ok(Value::undefined()) } /// `Object.prototype.toString()` @@ -576,7 +572,7 @@ pub fn define_property(_: &mut Value, args: &[Value], _: &mut Interpreter) -> Re /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(to_value(this.to_string())) + Ok(Value::from(this.to_string())) } /// `Object.prototype.hasOwnPrototype( property )` @@ -594,23 +590,26 @@ pub fn has_own_property(this: &mut Value, args: &[Value], _: &mut Interpreter) - let prop = if args.is_empty() { None } else { - from_value::(args.get(0).expect("Cannot get object").clone()).ok() + Some(String::from(args.get(0).expect("Cannot get object"))) }; - Ok(to_value( - prop.is_some() && this.get_prop(&prop.expect("Cannot get object")).is_some(), + Ok(Value::from( + prop.is_some() + && this + .get_property(&prop.expect("Cannot get object")) + .is_some(), )) } /// Create a new `Object` object. pub fn create(global: &Value) -> Value { - let prototype = ValueData::new_obj(None); + let prototype = Value::new_object(None); make_builtin_fn!(has_own_property, named "hasOwnProperty", of prototype); make_builtin_fn!(to_string, named "toString", of prototype); let object = make_constructor_fn!(make_object, make_object, global, prototype); - object.set_field_slice("length", to_value(1_i32)); + object.set_field_slice("length", Value::from(1)); make_builtin_fn!(set_prototype_of, named "setPrototypeOf", with length 2, of object); make_builtin_fn!(get_prototype_of, named "getPrototypeOf", with length 1, of object); make_builtin_fn!(define_property, named "defineProperty", with length 3, of object); diff --git a/boa/src/builtins/property.rs b/boa/src/builtins/property/mod.rs similarity index 75% rename from boa/src/builtins/property.rs rename to boa/src/builtins/property/mod.rs index 657586a14e..2153642b2f 100644 --- a/boa/src/builtins/property.rs +++ b/boa/src/builtins/property/mod.rs @@ -14,7 +14,7 @@ //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty //! [section]: https://tc39.es/ecma262/#sec-property-attributes -use crate::builtins::value::{from_value, to_value, FromValue, ToValue, Value, ValueData}; +use crate::builtins::value::Value; use gc::{Finalize, Trace}; /// This represents a Javascript Property AKA The Property Descriptor. @@ -170,59 +170,33 @@ impl Default for Property { } } -impl ToValue for Property { - fn to_value(&self) -> Value { - let prop = ValueData::new_obj(None); - prop.set_field_slice("configurable", to_value(self.configurable)); - prop.set_field_slice("enumerable", to_value(self.enumerable)); - prop.set_field_slice("writable", to_value(self.writable)); - prop.set_field_slice("value", to_value(self.value.clone())); - prop.set_field_slice("get", to_value(self.get.clone())); - prop.set_field_slice("set", to_value(self.set.clone())); - prop +impl From<&Property> for Value { + fn from(value: &Property) -> Value { + let property = Value::new_object(None); + property.set_field_slice("configurable", Value::from(value.configurable)); + property.set_field_slice("enumerable", Value::from(value.enumerable)); + property.set_field_slice("writable", Value::from(value.writable)); + property.set_field_slice("value", value.value.clone().unwrap_or_else(Value::null)); + property.set_field_slice("get", value.get.clone().unwrap_or_else(Value::null)); + property.set_field_slice("set", value.set.clone().unwrap_or_else(Value::null)); + property } } -impl FromValue for Property { +impl<'a> From<&'a Value> for Property { /// Attempt to fetch values "configurable", "enumerable", "writable" from the value, /// if they're not there default to false - fn from_value(v: Value) -> Result { - Ok(Self { - configurable: { - match from_value::(v.get_field_slice("configurable")) { - Ok(v) => Some(v), - Err(_) => Some(false), - } - }, - enumerable: { - match from_value::(v.get_field_slice("enumerable")) { - Ok(v) => Some(v), - Err(_) => Some(false), - } - }, - writable: { - match from_value(v.get_field_slice("writable")) { - Ok(v) => Some(v), - Err(_) => Some(false), - } - }, - value: Some(v.get_field_slice("value")), - get: Some(v.get_field_slice("get")), - set: Some(v.get_field_slice("set")), - }) + fn from(value: &Value) -> Self { + Self { + configurable: { Some(bool::from(&value.get_field_slice("configurable"))) }, + enumerable: { Some(bool::from(&value.get_field_slice("enumerable"))) }, + writable: { Some(bool::from(&value.get_field_slice("writable"))) }, + value: Some(value.get_field_slice("value")), + get: Some(value.get_field_slice("get")), + set: Some(value.get_field_slice("set")), + } } } #[cfg(test)] -mod test { - use super::*; - - #[test] - fn is_property_key_test() { - let v = Value::new(ValueData::String(String::from("Boop"))); - assert!(Property::is_property_key(&v)); - - let v = Value::new(ValueData::Boolean(true)); - assert!(!Property::is_property_key(&v)); - } -} +mod tests; diff --git a/boa/src/builtins/property/tests.rs b/boa/src/builtins/property/tests.rs new file mode 100644 index 0000000000..09189e82a3 --- /dev/null +++ b/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)); +} diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 889aa6d6b9..b2cbecc261 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -11,14 +11,13 @@ use std::ops::Deref; -use gc::Gc; use regex::Regex; use crate::{ builtins::{ object::{InternalState, Object, ObjectInternalMethods, ObjectKind, PROTOTYPE}, property::Property, - value::{from_value, to_value, undefined, FromValue, ResultValue, Value, ValueData}, + value::{ResultValue, Value, ValueData}, }, exec::Interpreter, }; @@ -59,18 +58,10 @@ struct RegExp { impl InternalState for RegExp {} -/// Helper function for getting an argument. -fn get_argument(args: &[Value], idx: usize) -> Result { - match args.get(idx) { - Some(arg) => from_value(arg.clone()).map_err(to_value), - None => Err(to_value(format!("expected argument at index {}", idx))), - } -} - /// Create a new `RegExp` pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if args.is_empty() { - return Err(undefined()); + return Err(Value::undefined()); } let mut regex_body = String::new(); let mut regex_flags = String::new(); @@ -85,16 +76,14 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res if slots.get("RegExpMatcher").is_some() { // first argument is another `RegExp` object, so copy its pattern and flags if let Some(body) = slots.get("OriginalSource") { - regex_body = - from_value(body.clone()).expect("Could not convert value to String"); + regex_body = String::from(body); } if let Some(flags) = slots.get("OriginalFlags") { - regex_flags = - from_value(flags.clone()).expect("Could not convert value to String"); + regex_flags = String::from(flags); } } } - _ => return Err(undefined()), + _ => return Err(Value::undefined()), } // if a second argument is given and it's a string, use it as flags match args.get(1) { @@ -165,9 +154,9 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res // This value is used by console.log and other routines to match Object type // to its Javascript Identifier (global constructor method name) this.set_kind(ObjectKind::Ordinary); - this.set_internal_slot("RegExpMatcher", undefined()); - this.set_internal_slot("OriginalSource", to_value(regex_body)); - this.set_internal_slot("OriginalFlags", to_value(regex_flags)); + this.set_internal_slot("RegExpMatcher", Value::undefined()); + this.set_internal_slot("OriginalSource", Value::from(regex_body)); + this.set_internal_slot("OriginalFlags", Value::from(regex_flags)); this.set_internal_state(regexp); Ok(this.clone()) @@ -184,7 +173,7 @@ pub fn make_regexp(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll fn get_dot_all(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.dot_all))) + this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all))) } /// `RegExp.prototype.flags` @@ -199,7 +188,7 @@ fn get_dot_all(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags /// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2 fn get_flags(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.flags.clone()))) + this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone()))) } /// `RegExp.prototype.global` @@ -213,7 +202,7 @@ fn get_flags(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global fn get_global(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.global))) + this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global))) } /// `RegExp.prototype.ignoreCase` @@ -227,7 +216,7 @@ fn get_global(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase fn get_ignore_case(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.ignore_case))) + this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case))) } /// `RegExp.prototype.multiline` @@ -241,7 +230,7 @@ fn get_ignore_case(this: &mut Value, _: &[Value], _: &mut Interpreter) -> Result /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline fn get_multiline(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.multiline))) + this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline))) } /// `RegExp.prototype.source` @@ -270,7 +259,7 @@ fn get_source(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky fn get_sticky(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.sticky))) + this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky))) } /// `RegExp.prototype.unicode` @@ -285,7 +274,7 @@ fn get_sticky(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode fn get_unicode(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - this.with_internal_state_ref(|regex: &RegExp| Ok(to_value(regex.unicode))) + this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode))) } /// `RegExp.prototype.test( string )` @@ -301,9 +290,8 @@ fn get_unicode(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValu /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test pub fn test(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - let arg_str = get_argument::(args, 0)?; - let mut last_index = - from_value::(this.get_field_slice("lastIndex")).map_err(to_value)?; + let arg_str = String::from(args.get(0).expect("could not get argument")); + let mut last_index = usize::from(&this.get_field_slice("lastIndex")); let result = this.with_internal_state_ref(|regex: &RegExp| { let result = if let Some(m) = regex.matcher.find_at(arg_str.as_str(), last_index) { if regex.use_last_index { @@ -316,9 +304,9 @@ pub fn test(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu } false }; - Ok(Gc::new(ValueData::Boolean(result))) + Ok(Value::boolean(result)) }); - this.set_field_slice("lastIndex", to_value(last_index)); + this.set_field_slice("lastIndex", Value::from(last_index)); result } @@ -335,9 +323,8 @@ pub fn test(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec pub fn exec(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - let arg_str = get_argument::(args, 0)?; - let mut last_index = - from_value::(this.get_field_slice("lastIndex")).map_err(to_value)?; + let arg_str = String::from(args.get(0).expect("could not get argument")); + let mut last_index = usize::from(&this.get_field_slice("lastIndex")); let result = this.with_internal_state_ref(|regex: &RegExp| { let mut locations = regex.matcher.capture_locations(); let result = if let Some(m) = @@ -351,26 +338,27 @@ pub fn exec(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValu let mut result = Vec::with_capacity(locations.len()); for i in 0..locations.len() { if let Some((start, end)) = locations.get(i) { - result.push(to_value( + result.push(Value::from( arg_str.get(start..end).expect("Could not get slice"), )); } else { - result.push(undefined()); + result.push(Value::undefined()); } } - let result = to_value(result); - result.set_prop_slice("index", Property::default().value(to_value(m.start()))); - result.set_prop_slice("input", Property::default().value(to_value(arg_str))); + + let result = Value::from(result); + result.set_property_slice("index", Property::default().value(Value::from(m.start()))); + result.set_property_slice("input", Property::default().value(Value::from(arg_str))); result } else { if regex.use_last_index { last_index = 0; } - Gc::new(ValueData::Null) + Value::null() }; Ok(result) }); - this.set_field_slice("lastIndex", to_value(last_index)); + this.set_field_slice("lastIndex", Value::from(last_index)); result } @@ -390,14 +378,14 @@ pub fn r#match(this: &mut Value, arg: String, ctx: &mut Interpreter) -> ResultVa if flags.contains('g') { let mut matches = Vec::new(); for mat in matcher.find_iter(&arg) { - matches.push(to_value(mat.as_str())); + matches.push(Value::from(mat.as_str())); } if matches.is_empty() { - return Ok(Gc::new(ValueData::Null)); + return Ok(Value::null()); } - Ok(to_value(matches)) + Ok(Value::from(matches)) } else { - exec(this, &[to_value(arg)], ctx) + exec(this, &[Value::from(arg)], ctx) } } @@ -412,9 +400,9 @@ pub fn r#match(this: &mut Value, arg: String, ctx: &mut Interpreter) -> ResultVa /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let body = from_value::(this.get_internal_slot("OriginalSource")).map_err(to_value)?; + let body = String::from(&this.get_internal_slot("OriginalSource")); let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone()); - Ok(to_value(format!("/{}/{}", body, flags))) + Ok(Value::from(format!("/{}/{}", body, flags))) } /// `RegExp.prototype[ @@matchAll ]( string )` @@ -437,17 +425,18 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue { let match_vec = caps .iter() .map(|group| match group { - Some(g) => to_value(g.as_str()), - None => undefined(), + Some(g) => Value::from(g.as_str()), + None => Value::undefined(), }) .collect::>(); - let match_val = to_value(match_vec); + let match_val = Value::from(match_vec); - match_val.set_prop_slice("index", Property::default().value(to_value(m.start()))); - match_val.set_prop_slice( + match_val + .set_property_slice("index", Property::default().value(Value::from(m.start()))); + match_val.set_property_slice( "input", - Property::default().value(to_value(arg_str.clone())), + Property::default().value(Value::from(arg_str.clone())), ); matches.push(match_val); @@ -461,8 +450,8 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue { }); let length = matches.len(); - let result = to_value(matches); - result.set_field_slice("length", to_value(length)); + let result = Value::from(matches); + result.set_field_slice("length", Value::from(length)); result.set_kind(ObjectKind::Array); Ok(result) @@ -471,8 +460,8 @@ pub fn match_all(this: &mut Value, arg_str: String) -> ResultValue { /// Create a new `RegExp` object. pub fn create(global: &Value) -> Value { // Create prototype - let prototype = ValueData::new_obj(Some(global)); - prototype.set_field_slice("lastIndex", to_value(0)); + let prototype = Value::new_object(Some(global)); + prototype.set_field_slice("lastIndex", Value::from(0)); make_builtin_fn!(test, named "test", with length 1, of prototype); make_builtin_fn!(exec, named "exec", with length 1, of prototype); diff --git a/boa/src/builtins/regexp/tests.rs b/boa/src/builtins/regexp/tests.rs index 63dac7132f..dd2fbfc0cb 100644 --- a/boa/src/builtins/regexp/tests.rs +++ b/boa/src/builtins/regexp/tests.rs @@ -21,7 +21,7 @@ fn constructors() { #[test] fn check_regexp_constructor_is_function() { - let global = ValueData::new_obj(None); + let global = Value::new_object(None); let regexp_constructor = create(&global); assert_eq!(regexp_constructor.is_function(), true); } diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index d96bdd3f9f..7ece2e74c3 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -17,11 +17,10 @@ use crate::{ object::{internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, PROTOTYPE}, property::Property, regexp::{make_regexp, match_all as regexp_match_all, r#match as regexp_match}, - value::{from_value, to_value, undefined, ResultValue, Value, ValueData}, + value::{ResultValue, Value, ValueData}, }, exec::Interpreter, }; -use gc::Gc; use regex::Regex; use std::{ cmp::{max, min}, @@ -55,21 +54,21 @@ pub fn make_string(this: &mut Value, args: &[Value], _: &mut Interpreter) -> Res pub fn call_string(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let arg = match args.get(0) { Some(v) => v.clone(), - None => undefined(), + None => Value::undefined(), }; if arg.is_undefined() { - return Ok(to_value("")); + return Ok(Value::from(String::new())); } - Ok(to_value(arg.to_string())) + Ok(Value::from(arg.to_string())) } /// Get the string value to a primitive string pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { // Get String from String Object and send it back as a new value let primitive_val = this.get_internal_slot("StringData"); - Ok(to_value(format!("{}", primitive_val))) + Ok(Value::from(format!("{}", primitive_val))) } /// `String.prototype.charAt( index )` @@ -92,12 +91,10 @@ pub fn char_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.value_to_rust_string(this); - let pos: i32 = from_value( + let pos = i32::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); // Calling .len() on a string would give the wrong result, as they are bytes not the number of // unicode code points @@ -107,10 +104,10 @@ pub fn char_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul // We should return an empty string is pos is out of range if pos >= length as i32 || pos < 0 { - return Ok(to_value::(String::new())); + return Ok(Value::from(String::new())); } - Ok(to_value::( + Ok(Value::from( primitive_val .chars() .nth(pos as usize) @@ -140,15 +137,13 @@ pub fn char_code_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. let length = primitive_val.chars().count(); - let pos: i32 = from_value( + let pos = i32::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); if pos >= length as i32 || pos < 0 { - return Ok(to_value(NAN)); + return Ok(Value::from(NAN)); } let utf16_val = primitive_val @@ -157,7 +152,7 @@ pub fn char_code_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> .expect("failed to get utf16 value"); // If there is no element at that index, the result is NaN // TODO: We currently don't have NaN - Ok(to_value(f64::from(utf16_val))) + Ok(Value::from(f64::from(utf16_val))) } /// `String.prototype.concat( str1[, ...strN] )` @@ -180,11 +175,11 @@ pub fn concat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result let mut new_str = ctx.value_to_rust_string(this); for arg in args { - let concat_str: String = from_value(arg.clone()).expect("failed to get argument value"); + let concat_str = String::from(arg); new_str.push_str(&concat_str); } - Ok(to_value(new_str)) + Ok(Value::from(new_str)) } /// `String.prototype.repeat( count )` @@ -203,13 +198,12 @@ pub fn repeat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result // Then we convert it into a Rust String by wrapping it in from_value let primitive_val: String = ctx.value_to_rust_string(this); - let repeat_times: usize = from_value( + let repeat_times = usize::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); - Ok(to_value(primitive_val.repeat(repeat_times))) + .expect("failed to get argument for String method"), + ); + + Ok(Value::from(primitive_val.repeat(repeat_times))) } /// `String.prototype.slice( beginIndex [, endIndex] )` @@ -227,18 +221,12 @@ pub fn slice(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV // Then we convert it into a Rust String by wrapping it in from_value let primitive_val: String = ctx.value_to_rust_string(this); - let start: i32 = from_value( + let start = i32::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); - let end: i32 = from_value( - args.get(1) - .expect("failed to get argument in slice") - .clone(), - ) - .expect("failed to parse argument"); + .expect("failed to get argument for String method"), + ); + + let end = i32::from(args.get(1).expect("failed to get argument in slice")); // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. @@ -266,7 +254,7 @@ pub fn slice(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV .expect("Could not get nth char"), ); } - Ok(to_value(new_str)) + Ok(Value::from(new_str)) } /// `String.prototype.startWith( searchString[, position] )` @@ -285,12 +273,10 @@ pub fn starts_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> R let primitive_val: String = ctx.value_to_rust_string(this); // TODO: Should throw TypeError if pattern is regular expression - let search_string: String = from_value( + let search_string = String::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); let length: i32 = primitive_val.chars().count() as i32; let search_length: i32 = search_string.chars().count() as i32; @@ -299,18 +285,18 @@ pub fn starts_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> R let position: i32 = if args.len() < 2 { 0 } else { - from_value(args.get(1).expect("failed to get arg").clone()).expect("failed to get argument") + i32::from(args.get(1).expect("failed to get arg")) }; let start = min(max(position, 0), length); let end = start.wrapping_add(search_length); if end > length { - Ok(to_value(false)) + Ok(Value::from(false)) } else { // Only use the part of the string from "start" let this_string: String = primitive_val.chars().skip(start as usize).collect(); - Ok(to_value(this_string.starts_with(&search_string))) + Ok(Value::from(this_string.starts_with(&search_string))) } } @@ -330,12 +316,10 @@ pub fn ends_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res let primitive_val: String = ctx.value_to_rust_string(this); // TODO: Should throw TypeError if search_string is regular expression - let search_string: String = from_value( + let search_string = String::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); let length: i32 = primitive_val.chars().count() as i32; let search_length: i32 = search_string.chars().count() as i32; @@ -345,19 +329,18 @@ pub fn ends_with(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res let end_position: i32 = if args.len() < 2 { length } else { - from_value(args.get(1).expect("Could not get argumetn").clone()) - .expect("Could not convert value to i32") + i32::from(args.get(1).expect("Could not get argumetn")) }; let end = min(max(end_position, 0), length); let start = end.wrapping_sub(search_length); if start < 0 { - Ok(to_value(false)) + Ok(Value::from(false)) } else { // Only use the part of the string up to "end" let this_string: String = primitive_val.chars().take(end as usize).collect(); - Ok(to_value(this_string.ends_with(&search_string))) + Ok(Value::from(this_string.ends_with(&search_string))) } } @@ -377,12 +360,10 @@ pub fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu let primitive_val: String = ctx.value_to_rust_string(this); // TODO: Should throw TypeError if search_string is regular expression - let search_string: String = from_value( + let search_string = String::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); let length: i32 = primitive_val.chars().count() as i32; @@ -390,8 +371,7 @@ pub fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu let position: i32 = if args.len() < 2 { 0 } else { - from_value(args.get(1).expect("Could not get argument").clone()) - .expect("Could not convert value to i32") + i32::from(args.get(1).expect("Could not get argument")) }; let start = min(max(position, 0), length); @@ -399,7 +379,7 @@ pub fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu // Take the string from "this" and use only the part of it after "start" let this_string: String = primitive_val.chars().skip(start as usize).collect(); - Ok(to_value(this_string.contains(&search_string))) + Ok(Value::from(this_string.contains(&search_string))) } /// Return either the string itself or the string of the regex equivalent @@ -411,8 +391,7 @@ fn get_regex_string(value: &Value) -> String { if slots.get("RegExpMatcher").is_some() { // first argument is another `RegExp` object, so copy its pattern and flags if let Some(body) = slots.get("OriginalSource") { - return from_value(r#body.clone()) - .expect("unable to get body from regex value"); + return String::from(r#body); } } "undefined".to_string() @@ -440,7 +419,7 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul // TODO: Support Symbol replacer let primitive_val: String = ctx.value_to_rust_string(this); if args.is_empty() { - return Ok(to_value(primitive_val)); + return Ok(Value::from(primitive_val)); } let regex_body = get_regex_string(args.get(0).expect("Value needed")); @@ -497,7 +476,7 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul // This will return the matched substring first, then captured parenthesized groups later let mut results: Vec = caps .iter() - .map(|capture| to_value(capture.unwrap().as_str())) + .map(|capture| Value::from(capture.unwrap().as_str())) .collect(); // Returns the starting byte offset of the match @@ -505,9 +484,9 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul .get(0) .expect("Unable to get Byte offset from string for match") .start(); - results.push(to_value(start)); + results.push(Value::from(start)); // Push the whole string being examined - results.push(to_value(primitive_val.to_string())); + results.push(Value::from(primitive_val.to_string())); let result = ctx.call(&replace_object, this, &results).unwrap(); @@ -519,7 +498,7 @@ pub fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul "undefined".to_string() }; - Ok(to_value(primitive_val.replacen( + Ok(Value::from(primitive_val.replacen( &mat.as_str(), &replace_value, 1, @@ -544,12 +523,10 @@ pub fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu let primitive_val: String = ctx.value_to_rust_string(this); // TODO: Should throw TypeError if search_string is regular expression - let search_string: String = from_value( + let search_string = String::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); let length: i32 = primitive_val.chars().count() as i32; @@ -557,8 +534,7 @@ pub fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu let position: i32 = if args.len() < 2 { 0 } else { - from_value(args.get(1).expect("Could not get argument").clone()) - .expect("Could not convert value to i32") + i32::from(args.get(1).expect("Could not get argument")) }; let start = min(max(position, 0), length); @@ -571,11 +547,11 @@ pub fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu let this_string: String = primitive_val.chars().skip(index as usize).collect(); if this_string.starts_with(&search_string) { // Explicitly return early with the index value - return Ok(to_value(index)); + return Ok(Value::from(index)); } } // Didn't find a match, so return -1 - Ok(to_value(-1)) + Ok(Value::from(-1)) } /// `String.prototype.lastIndexOf( searchValue[, fromIndex] )` @@ -596,12 +572,10 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> let primitive_val: String = ctx.value_to_rust_string(this); // TODO: Should throw TypeError if search_string is regular expression - let search_string: String = from_value( + let search_string = String::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); let length: i32 = primitive_val.chars().count() as i32; @@ -609,8 +583,7 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> let position: i32 = if args.len() < 2 { 0 } else { - from_value(args.get(1).expect("Could not get argument").clone()) - .expect("Could not convert value to i32") + i32::from(args.get(1).expect("Could not get argument")) }; let start = min(max(position, 0), length); @@ -628,7 +601,7 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> } // This will still be -1 if no matches were found, else with be >= 0 - Ok(to_value(highest_index)) + Ok(Value::from(highest_index)) } /// `String.prototype.match( regexp )` @@ -643,7 +616,7 @@ pub fn last_index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions pub fn r#match(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { - let mut re = make_regexp(&mut to_value(Object::default()), &[args[0].clone()], ctx)?; + let mut re = make_regexp(&mut Value::from(Object::default()), &[args[0].clone()], ctx)?; regexp_match(&mut re, ctx.value_to_rust_string(this), ctx) } @@ -660,7 +633,7 @@ fn string_pad( let primitive_length = primitive.len() as i32; if max_length <= primitive_length { - return Ok(to_value(primitive)); + return Ok(Value::from(primitive)); } let filler = match fill_string { @@ -669,7 +642,7 @@ fn string_pad( }; if filler == "" { - return Ok(to_value(primitive)); + return Ok(Value::from(primitive)); } let fill_len = max_length.wrapping_sub(primitive_length); @@ -682,9 +655,9 @@ fn string_pad( let concat_fill_str: String = fill_str.chars().take(fill_len as usize).collect(); if at_start { - Ok(to_value(format!("{}{}", concat_fill_str, &primitive))) + Ok(Value::from(format!("{}{}", concat_fill_str, &primitive))) } else { - Ok(to_value(format!("{}{}", primitive, &concat_fill_str))) + Ok(Value::from(format!("{}{}", primitive, &concat_fill_str))) } } @@ -703,20 +676,16 @@ fn string_pad( pub fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let primitive_val: String = ctx.value_to_rust_string(this); if args.is_empty() { - return Err(to_value("padEnd requires maxLength argument")); + return Err(Value::from("padEnd requires maxLength argument")); } - let max_length = from_value( + let max_length = i32::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); + let fill_string: Option = match args.len() { 1 => None, - _ => Some( - from_value(args.get(1).expect("Could not get argument").clone()) - .expect("Could not convert value to Option"), - ), + _ => Some(String::from(args.get(1).expect("Could not get argument"))), }; string_pad(primitive_val, max_length, fill_string, false) @@ -737,20 +706,16 @@ pub fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resul pub fn pad_start(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let primitive_val: String = ctx.value_to_rust_string(this); if args.is_empty() { - return Err(to_value("padStart requires maxLength argument")); + return Err(Value::from("padStart requires maxLength argument")); } - let max_length = from_value( + let max_length = i32::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), - ) - .expect("failed to parse argument for String method"); + .expect("failed to get argument for String method"), + ); + let fill_string: Option = match args.len() { 1 => None, - _ => Some( - from_value(args.get(1).expect("Could not get argument").clone()) - .expect("Could not convert value to Option"), - ), + _ => Some(String::from(args.get(1).expect("Could not get argument"))), }; string_pad(primitive_val, max_length, fill_string, true) @@ -789,7 +754,7 @@ fn is_trimmable_whitespace(c: char) -> bool { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim pub fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str: String = ctx.value_to_rust_string(this); - Ok(to_value(this_str.trim_matches(is_trimmable_whitespace))) + Ok(Value::from(this_str.trim_matches(is_trimmable_whitespace))) } /// `String.prototype.trimStart()` @@ -806,7 +771,7 @@ pub fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart pub fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str: String = ctx.value_to_rust_string(this); - Ok(to_value( + Ok(Value::from( this_str.trim_start_matches(is_trimmable_whitespace), )) } @@ -825,7 +790,9 @@ pub fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> Resul /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd pub fn trim_end(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str: String = ctx.value_to_rust_string(this); - Ok(to_value(this_str.trim_end_matches(is_trimmable_whitespace))) + Ok(Value::from( + this_str.trim_end_matches(is_trimmable_whitespace), + )) } /// `String.prototype.toLowerCase()` @@ -844,7 +811,7 @@ pub fn to_lowercase(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> Res let this_str: String = ctx.value_to_rust_string(this); // The Rust String is mapped to uppercase using the builtin .to_lowercase(). // There might be corner cases where it does not behave exactly like Javascript expects - Ok(to_value(this_str.to_lowercase())) + Ok(Value::from(this_str.to_lowercase())) } /// `String.prototype.toUpperCase()` @@ -865,7 +832,7 @@ pub fn to_uppercase(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> Res let this_str: String = ctx.value_to_rust_string(this); // The Rust String is mapped to uppercase using the builtin .to_uppercase(). // There might be corner cases where it does not behave exactly like Javascript expects - Ok(to_value(this_str.to_uppercase())) + Ok(Value::from(this_str.to_uppercase())) } /// `String.prototype.substring( indexStart[, indexEnd] )` @@ -886,20 +853,17 @@ pub fn substring(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res let start = if args.is_empty() { 0 } else { - from_value( + i32::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), + .expect("failed to get argument for String method"), ) - .expect("failed to parse argument for String method") }; let length: i32 = primitive_val.chars().count() as i32; // If less than 2 args specified, end is the length of the this object converted to a String let end = if args.len() < 2 { length } else { - from_value(args.get(1).expect("Could not get argument").clone()) - .expect("failed to parse argument for String method") + i32::from(args.get(1).expect("Could not get argument")) }; // Both start and end args replaced by 0 if they were negative // or by the length of the String if they were greater @@ -915,7 +879,7 @@ pub fn substring(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res .skip(from) .take(to.wrapping_sub(from)) .collect(); - Ok(to_value(extracted_string)) + Ok(Value::from(extracted_string)) } /// `String.prototype.substr( start[, length] )` @@ -937,12 +901,10 @@ pub fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result let mut start = if args.is_empty() { 0 } else { - from_value( + i32::from( args.get(0) - .expect("failed to get argument for String method") - .clone(), + .expect("failed to get argument for String method"), ) - .expect("failed to parse argument for String method") }; let length: i32 = primitive_val.chars().count() as i32; // If less than 2 args specified, end is +infinity, the maximum number value. @@ -952,8 +914,7 @@ pub fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result let end = if args.len() < 2 { i32::max_value() } else { - from_value(args.get(1).expect("Could not get argument").clone()) - .expect("failed to parse argument for String method") + i32::from(args.get(1).expect("Could not get argument")) }; // If start is negative it become the number of code units from the end of the string if start < 0 { @@ -965,14 +926,15 @@ pub fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result // If length is negative we return an empty string // otherwise we extract the part of the string from start and is length code units long if result_length <= 0 { - Ok(to_value("".to_string())) + Ok(Value::from("".to_string())) } else { let extracted_string: String = primitive_val .chars() .skip(start as usize) .take(result_length as usize) .collect(); - Ok(to_value(extracted_string)) + + Ok(Value::from(extracted_string)) } } @@ -1007,28 +969,28 @@ pub fn value_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let mut re: Value = match args.get(0) { Some(arg) => { - if arg == &Gc::new(ValueData::Null) { + if arg == &Value::null() { make_regexp( - &mut to_value(Object::default()), + &mut Value::from(Object::default()), &[ - to_value(ctx.value_to_rust_string(arg)), - to_value(String::from("g")), + Value::from(ctx.value_to_rust_string(arg)), + Value::from(String::from("g")), ], ctx, ) - } else if arg == &undefined() { + } else if arg == &Value::undefined() { make_regexp( - &mut to_value(Object::default()), - &[undefined(), to_value(String::from("g"))], + &mut Value::from(Object::default()), + &[Value::undefined(), Value::from(String::from("g"))], ctx, ) } else { - from_value(arg.clone()).map_err(to_value) + Ok(arg.clone()) } } None => make_regexp( - &mut to_value(Object::default()), - &[to_value(String::new()), to_value(String::from("g"))], + &mut Value::from(Object::default()), + &[Value::from(String::new()), Value::from("g")], ctx, ), }?; @@ -1039,10 +1001,10 @@ pub fn match_all(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Res /// Create a new `String` object. pub fn create(global: &Value) -> Value { // Create prototype - let prototype = ValueData::new_obj(Some(global)); - let length = Property::default().value(to_value(0_i32)); + let prototype = Value::new_object(Some(global)); + let length = Property::default().value(Value::from(0)); - prototype.set_prop_slice("length", length); + prototype.set_property_slice("length", length); make_builtin_fn!(char_at, named "charAt", with length 1, of prototype); make_builtin_fn!(char_code_at, named "charCodeAt", with length 1, of prototype); make_builtin_fn!(to_string, named "toString", of prototype); diff --git a/boa/src/builtins/string/tests.rs b/boa/src/builtins/string/tests.rs index 890db9da8f..e5f8d21fbe 100644 --- a/boa/src/builtins/string/tests.rs +++ b/boa/src/builtins/string/tests.rs @@ -5,7 +5,7 @@ use crate::{forward, forward_val}; #[test] fn check_string_constructor_is_function() { - let global = ValueData::new_obj(None); + let global = Value::new_object(None); let string_constructor = create(&global); assert_eq!(string_constructor.is_function(), true); } diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 72f0e2d32a..161bde9f44 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -24,7 +24,7 @@ use crate::{ internal_methods_trait::ObjectInternalMethods, Object, ObjectKind, INSTANCE_PROTOTYPE, PROTOTYPE, }, - value::{to_value, undefined, ResultValue, Value, ValueData}, + value::{ResultValue, Value, ValueData}, }, exec::Interpreter, }; @@ -51,12 +51,12 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu // Set description which should either be undefined or a string let desc_string = match args.get(0) { - Some(value) => to_value(value.to_string()), - None => undefined(), + Some(value) => Value::from(value.to_string()), + None => Value::undefined(), }; sym_instance.set_internal_slot("Description", desc_string); - sym_instance.set_internal_slot("SymbolData", to_value(random::())); + sym_instance.set_internal_slot("SymbolData", Value::from(random::())); // Set __proto__ internal slot let proto = ctx @@ -66,9 +66,9 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu .get_field_slice(PROTOTYPE); sym_instance.set_internal_slot(INSTANCE_PROTOTYPE, proto); - Ok(Gc::new(ValueData::Symbol(Box::new(GcCell::new( + Ok(Value(Gc::new(ValueData::Symbol(Box::new(GcCell::new( sym_instance, - ))))) + )))))) } /// `Symbol.prototype.toString()` @@ -84,13 +84,13 @@ pub fn call_symbol(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let s: Value = this.get_internal_slot("Description"); let full_string = format!(r#"Symbol({})"#, s.to_string()); - Ok(to_value(full_string)) + Ok(Value::from(full_string)) } /// Create a new `Symbol` object. pub fn create(global: &Value) -> Value { // Create prototype object - let prototype = ValueData::new_obj(Some(global)); + let prototype = Value::new_object(Some(global)); make_builtin_fn!(to_string, named "toString", of prototype); make_constructor_fn!(call_symbol, call_symbol, global, prototype) } diff --git a/boa/src/builtins/symbol/tests.rs b/boa/src/builtins/symbol/tests.rs index ae53fd6932..31b78dc16c 100644 --- a/boa/src/builtins/symbol/tests.rs +++ b/boa/src/builtins/symbol/tests.rs @@ -5,7 +5,7 @@ use crate::{forward, forward_val}; #[test] fn check_symbol_constructor_is_function() { - let global: Gc = ValueData::new_obj(None); + let global = Value::new_object(None); let symbol_constructor = create(&global); assert_eq!(symbol_constructor.is_function(), true); } diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index cf1b58881f..72ab5da5c6 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -1,202 +1,187 @@ use super::*; +use std::convert::TryFrom; -/// Conversion to Javascript values from Rust values -pub trait ToValue { - /// Convert this value to a Rust value - fn to_value(&self) -> Value; -} -/// Conversion to Rust values from Javascript values -pub trait FromValue { - /// Convert this value to a Javascript value - fn from_value(value: Value) -> Result - where - Self: Sized; -} - -impl ToValue for Value { - fn to_value(&self) -> Value { - self.clone() +impl From<&Value> for Value { + fn from(value: &Value) -> Self { + value.clone() } } -impl FromValue for Value { - fn from_value(value: Value) -> Result { - Ok(value) +impl From for Value { + fn from(value: String) -> Self { + Self::string(value) } } -impl ToValue for String { - fn to_value(&self) -> Value { - Gc::new(ValueData::String(self.clone())) +impl From<&Value> for String { + fn from(value: &Value) -> Self { + value.to_string() } } -impl FromValue for String { - fn from_value(v: Value) -> Result { - Ok(v.to_string()) +impl From<&str> for Value { + fn from(value: &str) -> Value { + Value::string(value) } } -impl<'s> ToValue for &'s str { - fn to_value(&self) -> Value { - Gc::new(ValueData::String( - String::from_str(*self).expect("Could not convert string to self to String"), - )) +impl From for Value { + fn from(value: char) -> Self { + Value::string(value.to_string()) } } -impl ToValue for char { - fn to_value(&self) -> Value { - Gc::new(ValueData::String(self.to_string())) +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct TryFromCharError; + +impl Display for TryFromCharError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not convert value to a char type") } } -impl FromValue for char { - fn from_value(v: Value) -> Result { - Ok(v.to_string() - .chars() - .next() - .expect("Could not get next char")) + +impl TryFrom<&Value> for char { + type Error = TryFromCharError; + + fn try_from(value: &Value) -> Result { + if let Some(c) = value.to_string().chars().next() { + Ok(c) + } else { + Err(TryFromCharError) + } } } -impl ToValue for f64 { - fn to_value(&self) -> Value { - Gc::new(ValueData::Rational(*self)) +impl From for Value { + fn from(value: f64) -> Self { + Self::rational(value) } } -impl FromValue for f64 { - fn from_value(v: Value) -> Result { - Ok(v.to_number()) + +impl From<&Value> for f64 { + fn from(value: &Value) -> Self { + value.to_number() } } -impl ToValue for i32 { - fn to_value(&self) -> Value { - Gc::new(ValueData::Integer(*self)) +impl From for Value { + fn from(value: i32) -> Value { + Value::integer(value) } } -impl FromValue for i32 { - fn from_value(v: Value) -> Result { - Ok(v.to_integer()) + +impl From<&Value> for i32 { + fn from(value: &Value) -> i32 { + value.to_integer() } } -impl ToValue for usize { - fn to_value(&self) -> Value { - Gc::new(ValueData::Integer(*self as i32)) +impl From for Value { + fn from(value: usize) -> Value { + Value::integer(value as i32) } } -impl FromValue for usize { - fn from_value(v: Value) -> Result { - Ok(v.to_integer() as Self) +impl From<&Value> for usize { + fn from(value: &Value) -> usize { + value.to_integer() as Self } } -impl ToValue for bool { - fn to_value(&self) -> Value { - Gc::new(ValueData::Boolean(*self)) +impl From for Value { + fn from(value: bool) -> Self { + Value::boolean(value) } } -impl FromValue for bool { - fn from_value(v: Value) -> Result { - Ok(v.is_true()) + +impl From<&Value> for bool { + fn from(value: &Value) -> Self { + value.is_true() } } -impl<'s, T: ToValue> ToValue for &'s [T] { - fn to_value(&self) -> Value { - let mut arr = Object::default(); - for (i, item) in self.iter().enumerate() { - arr.properties - .insert(i.to_string(), Property::default().value(item.to_value())); +impl From<&[T]> for Value +where + T: Clone + Into, +{ + fn from(value: &[T]) -> Self { + let mut array = Object::default(); + for (i, item) in value.iter().enumerate() { + array.properties.insert( + i.to_string(), + Property::default().value(item.clone().into()), + ); } - to_value(arr) + Self::from(array) } } -impl ToValue for Vec { - fn to_value(&self) -> Value { - let mut arr = Object::default(); - for (i, item) in self.iter().enumerate() { - arr.properties - .insert(i.to_string(), Property::default().value(item.to_value())); + +impl From> for Value +where + T: Into, +{ + fn from(value: Vec) -> 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 FromValue for Vec { - fn from_value(v: Value) -> Result { - let len = v.get_field_slice("length").to_integer(); - let mut vec = Self::with_capacity(len as usize); - for i in 0..len { - vec.push(from_value(v.get_field_slice(&i.to_string()))?) - } - Ok(vec) +impl From for Value { + fn from(object: Object) -> Self { + Value::object(object) } } -impl ToValue for Object { - fn to_value(&self) -> Value { - Gc::new(ValueData::Object(Box::new(GcCell::new(self.clone())))) +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct TryFromObjectError; + +impl Display for TryFromObjectError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not convert value to an Object type") } } -impl FromValue for Object { - fn from_value(v: Value) -> Result { - match *v { - ValueData::Object(ref obj) => Ok(obj.clone().into_inner()), - _ => Err("Value is not a valid object"), +impl TryFrom<&Value> for Object { + type Error = TryFromObjectError; + + fn try_from(value: &Value) -> Result { + match value.data() { + ValueData::Object(ref object) => Ok(object.clone().into_inner()), + _ => Err(TryFromObjectError), } } } -impl ToValue for JSONValue { - fn to_value(&self) -> Value { - Gc::new(ValueData::from_json(self.clone())) +impl From for Value { + fn from(value: JSONValue) -> Self { + Self(Gc::new(ValueData::from_json(value))) } } -impl FromValue for JSONValue { - fn from_value(v: Value) -> Result { - Ok(v.to_json()) +impl From<&Value> for JSONValue { + fn from(value: &Value) -> Self { + value.to_json() } } -impl ToValue for () { - fn to_value(&self) -> Value { - Gc::new(ValueData::Null) - } -} -impl FromValue for () { - fn from_value(_: Value) -> Result<(), &'static str> { - Ok(()) +impl From<()> for Value { + fn from(_: ()) -> Self { + Value::null() } } -impl ToValue for Option { - fn to_value(&self) -> Value { - match *self { - Some(ref v) => v.to_value(), - None => Gc::new(ValueData::Null), +impl From> for Value +where + T: Into, +{ + fn from(value: Option) -> Self { + match value { + Some(value) => value.into(), + None => Value::null(), } } } -impl FromValue for Option { - fn from_value(value: Value) -> Result { - 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(v: Value) -> Result { - FromValue::from_value(v) -} - -/// A utility function that just calls `ToValue::to_value` -pub fn to_value(v: A) -> Value { - v.to_value() -} diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 4396301cec..583d314649 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -34,11 +34,118 @@ pub use operations::*; pub type ResultValue = Result; /// A Garbage-collected Javascript value as represented in the interpreter. -pub type Value = Gc; +#[derive(Debug, Clone, Trace, Finalize, Default)] +pub struct Value(pub(crate) Gc); -#[inline] -pub fn undefined() -> Value { - Gc::new(ValueData::Undefined) +impl Value { + /// Creates a new `undefined` value. + #[inline] + pub fn undefined() -> Self { + Self(Gc::new(ValueData::Undefined)) + } + + /// Creates a new `null` value. + #[inline] + pub fn null() -> Self { + Self(Gc::new(ValueData::Null)) + } + + /// Creates a new string value. + #[inline] + pub fn string(value: S) -> Self + where + S: Into, + { + Self(Gc::new(ValueData::String(value.into()))) + } + + /// Creates a new number value. + #[inline] + pub fn rational(value: N) -> Self + where + N: Into, + { + Self(Gc::new(ValueData::Rational(value.into()))) + } + + /// Creates a new number value. + #[inline] + pub fn integer(value: I) -> Self + where + I: Into, + { + Self(Gc::new(ValueData::Integer(value.into()))) + } + + /// Creates a new number value. + #[inline] + pub fn number(value: N) -> Self + where + N: Into, + { + Self::rational(value.into()) + } + + /// Creates a new boolean value. + #[inline] + pub fn boolean(value: bool) -> Self { + Self(Gc::new(ValueData::Boolean(value))) + } + + /// Creates a new object value. + #[inline] + pub fn object(object: Object) -> Self { + Self(Gc::new(ValueData::Object(Box::new(GcCell::new(object))))) + } + + /// Gets the underlying `ValueData` structure. + #[inline] + pub fn data(&self) -> &ValueData { + &*self.0 + } + + /// Helper function to convert the `Value` to a number and compute its power. + pub fn as_num_to_power(&self, other: Self) -> Self { + Self::rational(self.to_number().powf(other.to_number())) + } + + /// Returns a new empty object + pub fn new_object(global: Option<&Value>) -> Self { + if let Some(global) = global { + let object_prototype = global.get_field_slice("Object").get_field_slice(PROTOTYPE); + + let object = Object::create(object_prototype); + Self::object(object) + } else { + Self::object(Object::default()) + } + } + + /// Similar to `new_object`, but you can pass a prototype to create from, plus a kind + pub fn new_object_from_prototype(proto: Value, kind: ObjectKind) -> Self { + let mut object = Object::default(); + object.kind = kind; + + object + .internal_slots + .insert(INSTANCE_PROTOTYPE.to_string(), proto); + + Self::object(object) + } +} + +impl Deref for Value { + type Target = ValueData; + + fn deref(&self) -> &Self::Target { + self.data() + } +} + +impl Display for Value { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } } /// A Javascript value @@ -63,31 +170,6 @@ pub enum ValueData { } impl ValueData { - /// Returns a new empty object - pub fn new_obj(global: Option<&Value>) -> Value { - if let Some(glob) = global { - let obj_proto = glob.get_field_slice("Object").get_field_slice(PROTOTYPE); - - let obj = Object::create(obj_proto); - Gc::new(Self::Object(Box::new(GcCell::new(obj)))) - } else { - let obj = Object::default(); - Gc::new(Self::Object(Box::new(GcCell::new(obj)))) - } - } - - /// Similar to `new_obj`, but you can pass a prototype to create from, - /// plus a kind - pub fn new_obj_from_prototype(proto: Value, kind: ObjectKind) -> Value { - let mut obj = Object::default(); - obj.kind = kind; - - obj.internal_slots - .insert(INSTANCE_PROTOTYPE.to_string(), proto); - - Gc::new(Self::Object(Box::new(GcCell::new(obj)))) - } - /// This will tell us if we can exten an object or not, not properly implemented yet /// /// For now always returns true. @@ -249,10 +331,10 @@ impl ValueData { } } - /// remove_prop removes a property from a Value object. + /// Removes a property from a Value object. /// /// It will return a boolean based on if the value was removed, if there was no value to remove false is returned - pub fn remove_prop(&self, field: &str) { + pub fn remove_property(&self, field: &str) { match *self { Self::Object(ref obj) => obj.borrow_mut().deref_mut().properties.remove(field), _ => None, @@ -262,12 +344,12 @@ impl ValueData { /// Resolve the property in the object. /// /// A copy of the Property is returned. - pub fn get_prop(&self, field: &str) -> Option { + pub fn get_property(&self, field: &str) -> Option { // Spidermonkey has its own GetLengthProperty: https://searchfox.org/mozilla-central/source/js/src/vm/Interpreter-inl.h#154 // This is only for primitive strings, String() objects have their lengths calculated in string.rs if self.is_string() && field == "length" { if let Self::String(ref s) = *self { - return Some(Property::default().value(to_value(s.len() as i32))); + return Some(Property::default().value(Value::from(s.len()))); } } @@ -292,7 +374,7 @@ impl ValueData { match obj.properties.get(field) { Some(val) => Some(val.clone()), None => match obj.internal_slots.get(&INSTANCE_PROTOTYPE.to_string()) { - Some(value) => value.get_prop(field), + Some(value) => value.get_property(field), None => None, }, } @@ -301,7 +383,7 @@ impl ValueData { /// update_prop will overwrite individual [Property] fields, unlike /// Set_prop, which will overwrite prop with a new Property /// Mostly used internally for now - pub fn update_prop( + pub fn update_property( &self, field: &str, value: Option, @@ -338,12 +420,12 @@ impl ValueData { let hash = obj.clone(); hash.into_inner() } - _ => return Gc::new(Self::Undefined), + _ => return Value::undefined(), }; match obj.internal_slots.get(field) { Some(val) => val.clone(), - None => Gc::new(Self::Undefined), + None => Value::undefined(), } } @@ -354,7 +436,7 @@ impl ValueData { match *field { // Our field will either be a String or a Symbol Self::String(ref s) => { - match self.get_prop(s) { + match self.get_property(s) { Some(prop) => { // If the Property has [[Get]] set to a function, we should run that and return the Value let prop_getter = match prop.get { @@ -373,11 +455,11 @@ impl ValueData { val.clone() } } - None => Gc::new(Self::Undefined), + None => Value::undefined(), } } Self::Symbol(_) => unimplemented!(), - _ => Gc::new(Self::Undefined), + _ => Value::undefined(), } } @@ -449,14 +531,14 @@ impl ValueData { /// Check to see if the Value has the field, mainly used by environment records pub fn has_field(&self, field: &str) -> bool { - self.get_prop(field).is_some() + self.get_property(field).is_some() } /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist pub fn get_field_slice(&self, field: &str) -> Value { // get_field used to accept strings, but now Symbols accept it needs to accept a value // So this function will now need to Box strings back into values (at least for now) - let f = Gc::new(Self::String(field.to_string())); + let f = Value::string(field.to_string()); self.get_field(f) } @@ -467,10 +549,9 @@ impl ValueData { if obj.borrow().kind == ObjectKind::Array { if let Ok(num) = field.to_string().parse::() { if num > 0 { - let len: i32 = from_value(self.get_field_slice("length")) - .expect("Could not convert argument to i32"); + let len = i32::from(&self.get_field_slice("length")); if len < (num + 1) as i32 { - self.set_field_slice("length", to_value(num + 1)); + self.set_field_slice("length", Value::from(num + 1)); } } } @@ -481,7 +562,7 @@ impl ValueData { obj.borrow_mut().set(field, val.clone()); } else { obj.borrow_mut() - .set(to_value(field.to_string()), val.clone()); + .set(Value::from(field.to_string()), val.clone()); } } @@ -492,7 +573,7 @@ impl ValueData { pub fn set_field_slice(&self, field: &str, val: Value) -> Value { // set_field used to accept strings, but now Symbols accept it needs to accept a value // So this function will now need to Box strings back into values (at least for now) - let f = Gc::new(Self::String(field.to_string())); + let f = Value::string(field.to_string()); self.set_field(f, val) } @@ -514,7 +595,7 @@ impl ValueData { } /// Set the property in the value - pub fn set_prop(&self, field: String, prop: Property) -> Property { + pub fn set_property(&self, field: String, prop: Property) -> Property { if let Self::Object(ref obj) = *self { obj.borrow_mut().properties.insert(field, prop.clone()); } @@ -522,8 +603,8 @@ impl ValueData { } /// Set the property in the value - pub fn set_prop_slice(&self, field: &str, prop: Property) -> Property { - self.set_prop(field.to_string(), prop) + pub fn set_property_slice(&self, field: &str, prop: Property) -> Property { + self.set_property(field.to_string(), prop) } /// Set internal state of an Object. Discards the previous state if it was set. @@ -544,9 +625,9 @@ impl ValueData { // Set [[Call]] internal slot new_func.set_call(native_func); // Wrap Object in GC'd Value - let new_func_val = to_value(new_func); + let new_func_val = Value::from(new_func); // Set length to parameters - new_func_val.set_field_slice("length", to_value(length)); + new_func_val.set_field_slice("length", Value::from(length)); new_func_val } @@ -563,12 +644,12 @@ impl ValueData { for (idx, json) in vs.iter().enumerate() { new_obj.properties.insert( idx.to_string(), - Property::default().value(to_value(json.clone())), + Property::default().value(Value::from(json.clone())), ); } new_obj.properties.insert( "length".to_string(), - Property::default().value(to_value(vs.len() as i32)), + Property::default().value(Value::from(vs.len())), ); Self::Object(Box::new(GcCell::new(new_obj))) } @@ -577,7 +658,7 @@ impl ValueData { for (key, json) in obj.iter() { new_obj.properties.insert( key.clone(), - Property::default().value(to_value(json.clone())), + Property::default().value(Value::from(json.clone())), ); } @@ -629,10 +710,6 @@ impl ValueData { } } } - - pub fn as_num_to_power(&self, other: Self) -> Self { - Self::Rational(self.to_number().powf(other.to_number())) - } } impl Default for ValueData { @@ -709,30 +786,27 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String { // Can use the private "type" field of an Object to match on // which type of Object it represents for special printing match v.borrow().kind { - ObjectKind::String => from_value( + ObjectKind::String => String::from( v.borrow() .internal_slots .get("StringData") - .expect("Cannot get primitive value from String") - .clone(), - ) - .expect("Cannot clone primitive value from String"), + .expect("Cannot get primitive value from String"), + ), ObjectKind::Boolean => { let bool_data = v.borrow().get_internal_slot("BooleanData").to_string(); format!("Boolean {{ {} }}", bool_data) } ObjectKind::Array => { - let len: i32 = from_value( - v.borrow() + let len = i32::from( + &v.borrow() .properties .get("length") .unwrap() .value .clone() .expect("Could not borrow value"), - ) - .expect("Could not convert JS value to i32"); + ); if len == 0 { return String::from("[]"); diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index e465d05f2c..66c550639a 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -1,92 +1,98 @@ use super::*; -impl PartialEq for ValueData { +impl PartialEq for Value { fn eq(&self, other: &Self) -> bool { - match (self.clone(), other.clone()) { + match (self.data(), other.data()) { // TODO: fix this // _ if self.ptr.to_inner() == &other.ptr.to_inner() => true, _ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, - (Self::String(_), _) | (_, Self::String(_)) => self.to_string() == other.to_string(), - (Self::Boolean(a), Self::Boolean(b)) if a == b => true, - (Self::Rational(a), Self::Rational(b)) if a == b && !a.is_nan() && !b.is_nan() => true, - (Self::Rational(a), _) if a == other.to_number() => true, - (_, Self::Rational(a)) if a == self.to_number() => true, - (Self::Integer(a), Self::Integer(b)) if a == b => true, + (ValueData::String(_), _) | (_, ValueData::String(_)) => { + self.to_string() == other.to_string() + } + (ValueData::Boolean(a), ValueData::Boolean(b)) if a == b => true, + (ValueData::Rational(a), ValueData::Rational(b)) + if a == b && !a.is_nan() && !b.is_nan() => + { + true + } + (ValueData::Rational(a), _) if *a == other.to_number() => true, + (_, ValueData::Rational(a)) if *a == self.to_number() => true, + (ValueData::Integer(a), ValueData::Integer(b)) if a == b => true, _ => false, } } } -impl Add for ValueData { +impl Add for Value { type Output = Self; fn add(self, other: Self) -> Self { - match (self, other) { - (Self::String(ref s), ref o) => { - Self::String(format!("{}{}", s.clone(), &o.to_string())) + match (self.data(), other.data()) { + (ValueData::String(ref s), ref o) => { + Self::string(format!("{}{}", s.clone(), &o.to_string())) } - (ref s, Self::String(ref o)) => Self::String(format!("{}{}", s.to_string(), o)), - (ref s, ref o) => Self::Rational(s.to_number() + o.to_number()), + (ref s, ValueData::String(ref o)) => Self::string(format!("{}{}", s.to_string(), o)), + (ref s, ref o) => Self::rational(s.to_number() + o.to_number()), } } } -impl Sub for ValueData { +impl Sub for Value { type Output = Self; fn sub(self, other: Self) -> Self { - Self::Rational(self.to_number() - other.to_number()) + Self::rational(self.to_number() - other.to_number()) } } -impl Mul for ValueData { +impl Mul for Value { type Output = Self; fn mul(self, other: Self) -> Self { - Self::Rational(self.to_number() * other.to_number()) + Self::rational(self.to_number() * other.to_number()) } } -impl Div for ValueData { +impl Div for Value { type Output = Self; fn div(self, other: Self) -> Self { - Self::Rational(self.to_number() / other.to_number()) + Self::rational(self.to_number() / other.to_number()) } } -impl Rem for ValueData { +impl Rem for Value { type Output = Self; fn rem(self, other: Self) -> Self { - Self::Rational(self.to_number() % other.to_number()) + Self::rational(self.to_number() % other.to_number()) } } -impl BitAnd for ValueData { +impl BitAnd for Value { type Output = Self; fn bitand(self, other: Self) -> Self { - Self::Integer(self.to_integer() & other.to_integer()) + Self::integer(self.to_integer() & other.to_integer()) } } -impl BitOr for ValueData { +impl BitOr for Value { type Output = Self; fn bitor(self, other: Self) -> Self { - Self::Integer(self.to_integer() | other.to_integer()) + Self::integer(self.to_integer() | other.to_integer()) } } -impl BitXor for ValueData { +impl BitXor for Value { type Output = Self; fn bitxor(self, other: Self) -> Self { - Self::Integer(self.to_integer() ^ other.to_integer()) + Self::integer(self.to_integer() ^ other.to_integer()) } } -impl Shl for ValueData { +impl Shl for Value { type Output = Self; fn shl(self, other: Self) -> Self { - Self::Integer(self.to_integer() << other.to_integer()) + Self::integer(self.to_integer() << other.to_integer()) } } -impl Shr for ValueData { +impl Shr for Value { type Output = Self; fn shr(self, other: Self) -> Self { - Self::Integer(self.to_integer() >> other.to_integer()) + Self::integer(self.to_integer() >> other.to_integer()) } } -impl Not for ValueData { +impl Not for Value { type Output = Self; fn not(self) -> Self { - Self::Boolean(!self.is_true()) + Self::boolean(!self.is_true()) } } @@ -99,8 +105,8 @@ impl Not for ValueData { pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { if strict { // Do both Values point to the same underlying valueData? - let x_ptr = Gc::into_raw(x.clone()); - let y_ptr = Gc::into_raw(y.clone()); + let x_ptr = Gc::into_raw(x.0.clone()); + let y_ptr = Gc::into_raw(y.0.clone()); return x_ptr == y_ptr; } @@ -109,8 +115,8 @@ pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { } if x.get_type() == "number" { - let native_x: f64 = from_value(x.clone()).expect("failed to get value"); - let native_y: f64 = from_value(y.clone()).expect("failed to get value"); + let native_x = f64::from(x); + let native_y = f64::from(y); return native_x.abs() - native_y.abs() == 0.0; } @@ -128,10 +134,7 @@ pub fn same_value_non_number(x: &Value, y: &Value) -> bool { } false } - "boolean" => { - from_value::(x.clone()).expect("failed to get value") - == from_value::(y.clone()).expect("failed to get value") - } + "boolean" => bool::from(x) == bool::from(y), "object" => *x == *y, _ => false, } diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 0327dece7f..af94f1b57b 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -2,14 +2,14 @@ use super::*; #[test] fn check_is_object() { - let val = ValueData::new_obj(None); + let val = Value::new_object(None); assert_eq!(val.is_object(), true); } #[test] fn check_string_to_value() { let s = String::from("Hello"); - let v = s.to_value(); + let v = Value::from(s); assert_eq!(v.is_string(), true); assert_eq!(v.is_null(), false); } @@ -23,26 +23,26 @@ fn check_undefined() { #[test] fn check_get_set_field() { - let obj = ValueData::new_obj(None); + let obj = Value::new_object(None); // Create string and convert it to a Value - let s = String::from("bar").to_value(); + let s = Value::from("bar"); obj.set_field_slice("foo", s); assert_eq!(obj.get_field_slice("foo").to_string(), "bar"); } #[test] fn check_integer_is_true() { - assert_eq!(1.to_value().is_true(), true); - assert_eq!(0.to_value().is_true(), false); - assert_eq!((-1).to_value().is_true(), true); + assert_eq!(Value::from(1).is_true(), true); + assert_eq!(Value::from(0).is_true(), false); + assert_eq!(Value::from(-1).is_true(), true); } #[test] fn check_number_is_true() { - assert_eq!(1.0.to_value().is_true(), true); - assert_eq!(0.1.to_value().is_true(), true); - assert_eq!(0.0.to_value().is_true(), false); - assert_eq!((-0.0).to_value().is_true(), false); - assert_eq!((-1.0).to_value().is_true(), true); - assert_eq!(NAN.to_value().is_true(), false); + assert_eq!(Value::from(1.0).is_true(), true); + assert_eq!(Value::from(0.1).is_true(), true); + assert_eq!(Value::from(0.0).is_true(), false); + assert_eq!(Value::from(-0.0).is_true(), false); + assert_eq!(Value::from(-1.0).is_true(), true); + assert_eq!(Value::from(NAN).is_true(), false); } diff --git a/boa/src/environment/declarative_environment_record.rs b/boa/src/environment/declarative_environment_record.rs index 43eb25cbc9..5b36143d8f 100644 --- a/boa/src/environment/declarative_environment_record.rs +++ b/boa/src/environment/declarative_environment_record.rs @@ -6,13 +6,13 @@ //! More info: [ECMA-262 sec-declarative-environment-records](https://tc39.es/ecma262/#sec-declarative-environment-records) use crate::{ - builtins::value::{Value, ValueData}, + builtins::value::Value, environment::{ environment_record_trait::EnvironmentRecordTrait, lexical_environment::{Environment, EnvironmentType}, }, }; -use gc::{Finalize, Gc, Trace}; +use gc::{Finalize, Trace}; use rustc_hash::FxHashMap; /// Declarative Bindings have a few properties for book keeping purposes, such as mutability (const vs let). @@ -154,7 +154,7 @@ impl EnvironmentRecordTrait for DeclarativeEnvironmentRecord { } fn with_base_object(&self) -> Value { - Gc::new(ValueData::Undefined) + Value::undefined() } fn get_outer_environment(&self) -> Option { diff --git a/boa/src/environment/function_environment_record.rs b/boa/src/environment/function_environment_record.rs index 0f173981f0..e7027372f6 100644 --- a/boa/src/environment/function_environment_record.rs +++ b/boa/src/environment/function_environment_record.rs @@ -9,14 +9,14 @@ //! More info: use crate::{ - builtins::value::{Value, ValueData}, + builtins::value::Value, environment::{ declarative_environment_record::DeclarativeEnvironmentRecordBinding, environment_record_trait::EnvironmentRecordTrait, lexical_environment::{Environment, EnvironmentType}, }, }; -use gc::{Finalize, Gc, Trace}; +use gc::{Finalize, Trace}; use rustc_hash::FxHashMap; /// Different binding status for `this`. @@ -220,7 +220,7 @@ impl EnvironmentRecordTrait for FunctionEnvironmentRecord { } fn with_base_object(&self) -> Value { - Gc::new(ValueData::Undefined) + Value::undefined() } fn get_outer_environment(&self) -> Option { diff --git a/boa/src/environment/global_environment_record.rs b/boa/src/environment/global_environment_record.rs index 3bf540836b..079c69a929 100644 --- a/boa/src/environment/global_environment_record.rs +++ b/boa/src/environment/global_environment_record.rs @@ -8,7 +8,7 @@ //! More info: use crate::{ - builtins::value::{Value, ValueData}, + builtins::value::Value, environment::{ declarative_environment_record::DeclarativeEnvironmentRecord, environment_record_trait::EnvironmentRecordTrait, @@ -16,7 +16,7 @@ use crate::{ object_environment_record::ObjectEnvironmentRecord, }, }; -use gc::{Finalize, Gc, Trace}; +use gc::{Finalize, Trace}; use rustc_hash::FxHashSet; #[derive(Debug, Trace, Finalize, Clone)] @@ -42,7 +42,7 @@ impl GlobalEnvironmentRecord { pub fn has_restricted_global_property(&self, name: &str) -> bool { let global_object = &self.object_record.bindings; - let existing_prop = global_object.get_prop(name); + let existing_prop = global_object.get_property(name); match existing_prop { Some(prop) => { if prop.value.is_none() || prop.configurable.unwrap_or(false) { @@ -61,7 +61,7 @@ impl GlobalEnvironmentRecord { let extensible = global_object.is_extensible(); if !has_property && extensible { obj_rec.create_mutable_binding(name.clone(), deletion); - obj_rec.initialize_binding(&name, Gc::new(ValueData::Undefined)); + obj_rec.initialize_binding(&name, Value::undefined()); } let var_declared_names = &mut self.var_names; @@ -72,10 +72,10 @@ impl GlobalEnvironmentRecord { pub fn create_global_function_binding(&mut self, name: &str, value: Value, deletion: bool) { let global_object = &mut self.object_record.bindings; - let existing_prop = global_object.get_prop(&name); + let existing_prop = global_object.get_property(&name); if let Some(prop) = existing_prop { if prop.value.is_none() || prop.configurable.unwrap_or(false) { - global_object.update_prop( + global_object.update_property( name, Some(value), Some(true), @@ -84,7 +84,13 @@ impl GlobalEnvironmentRecord { ); } } else { - global_object.update_prop(name, Some(value), Some(true), Some(true), Some(deletion)); + global_object.update_property( + name, + Some(value), + Some(true), + Some(true), + Some(deletion), + ); } } } @@ -170,7 +176,7 @@ impl EnvironmentRecordTrait for GlobalEnvironmentRecord { } fn with_base_object(&self) -> Value { - Gc::new(ValueData::Undefined) + Value::undefined() } fn get_outer_environment(&self) -> Option { diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index de44a37472..487820f4aa 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -6,7 +6,7 @@ //! This is the entrypoint to lexical environments. use crate::{ - builtins::value::{Value, ValueData}, + builtins::value::Value, environment::{ declarative_environment_record::DeclarativeEnvironmentRecord, environment_record_trait::EnvironmentRecordTrait, @@ -211,7 +211,7 @@ impl LexicalEnvironment { self.environments() .find(|env| env.borrow().has_binding(name)) .map(|env| env.borrow().get_binding_value(name, false)) - .unwrap_or_else(|| Gc::new(ValueData::Undefined)) + .unwrap_or_else(Value::undefined) } } @@ -234,10 +234,10 @@ pub fn new_function_environment( env_rec: FxHashMap::default(), function: f, this_binding_status: BindingStatus::Uninitialized, // hardcoding to unitialized for now until short functions are properly supported - home_object: Gc::new(ValueData::Undefined), + home_object: Value::undefined(), new_target, outer_env: outer, // this will come from Environment set as a private property of F - https://tc39.es/ecma262/#sec-ecmascript-function-objects - this_value: Gc::new(ValueData::Undefined), // TODO: this_value should start as an Option as its not always there to begin with + this_value: Value::undefined(), // TODO: this_value should start as an Option as its not always there to begin with }))) } diff --git a/boa/src/environment/object_environment_record.rs b/boa/src/environment/object_environment_record.rs index a467e7731c..43b9f8349e 100644 --- a/boa/src/environment/object_environment_record.rs +++ b/boa/src/environment/object_environment_record.rs @@ -7,16 +7,13 @@ //! More info: [Object Records](https://tc39.es/ecma262/#sec-object-environment-records) use crate::{ - builtins::{ - property::Property, - value::{Value, ValueData}, - }, + builtins::{property::Property, value::Value}, environment::{ environment_record_trait::EnvironmentRecordTrait, lexical_environment::{Environment, EnvironmentType}, }, }; -use gc::{Finalize, Gc, Trace}; +use gc::{Finalize, Trace}; #[derive(Debug, Trace, Finalize, Clone)] pub struct ObjectEnvironmentRecord { @@ -42,12 +39,12 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { // only for it to be replace with the real value later. We could just add the name to a Vector instead let bindings = &mut self.bindings; let prop = Property::default() - .value(Gc::new(ValueData::Undefined)) + .value(Value::undefined()) .writable(true) .enumerable(true) .configurable(deletion); - bindings.set_prop(name, prop); + bindings.set_property(name, prop); } fn create_immutable_binding(&mut self, _name: String, _strict: bool) -> bool { @@ -66,7 +63,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { debug_assert!(value.is_object() || value.is_function()); let bindings = &mut self.bindings; - bindings.update_prop(name, Some(value), None, None, Some(strict)); + bindings.update_property(name, Some(value), None, None, Some(strict)); } fn get_binding_value(&self, name: &str, strict: bool) -> Value { @@ -77,12 +74,12 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { // TODO: throw error here // Error handling not implemented yet } - Gc::new(ValueData::Undefined) + Value::undefined() } } fn delete_binding(&mut self, name: &str) -> bool { - self.bindings.remove_prop(name); + self.bindings.remove_property(name); true } @@ -101,7 +98,7 @@ impl EnvironmentRecordTrait for ObjectEnvironmentRecord { return self.bindings.clone(); } - Gc::new(ValueData::Undefined) + Value::undefined() } fn get_outer_environment(&self) -> Option { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 81a765c78b..dbea9f969e 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -12,7 +12,7 @@ use crate::{ PROTOTYPE, }, property::Property, - value::{from_value, to_value, ResultValue, Value, ValueData}, + value::{ResultValue, Value, ValueData}, }, environment::lexical_environment::{new_declarative_environment, VariableScope}, realm::Realm, @@ -22,7 +22,6 @@ use crate::{ op::{AssignOp, BinOp, BitOp, CompOp, LogOp, NumOp, UnaryOp}, }, }; -use gc::Gc; use std::{ borrow::{Borrow, BorrowMut}, ops::Deref, @@ -44,8 +43,8 @@ pub struct Interpreter { pub realm: Realm, } -fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value { - Gc::new(match *op { +fn exec_assign_op(op: &AssignOp, v_a: Value, v_b: Value) -> Value { + match *op { AssignOp::Add => v_a + v_b, AssignOp::Sub => v_a - v_b, AssignOp::Mul => v_a * v_b, @@ -57,7 +56,7 @@ fn exec_assign_op(op: &AssignOp, v_a: ValueData, v_b: ValueData) -> Value { AssignOp::Xor => v_a ^ v_b, AssignOp::Shl => v_a << v_b, AssignOp::Shr => v_a << v_b, - }) + } } impl Executor for Interpreter { @@ -71,15 +70,15 @@ impl Executor for Interpreter { #[allow(clippy::match_same_arms)] fn run(&mut self, node: &Node) -> ResultValue { match *node { - Node::Const(Const::Null) => Ok(to_value(None::<()>)), - Node::Const(Const::Undefined) => Ok(Gc::new(ValueData::Undefined)), - Node::Const(Const::Num(num)) => Ok(to_value(num)), - Node::Const(Const::Int(num)) => Ok(to_value(num)), + Node::Const(Const::Null) => Ok(Value::null()), + Node::Const(Const::Undefined) => Ok(Value::undefined()), + Node::Const(Const::Num(num)) => Ok(Value::rational(num)), + Node::Const(Const::Int(num)) => Ok(Value::integer(num)), // we can't move String from Const into value, because const is a garbage collected value // Which means Drop() get's called on Const, but str will be gone at that point. // Do Const values need to be garbage collected? We no longer need them once we've generated Values - Node::Const(Const::String(ref str)) => Ok(to_value(str.to_owned())), - Node::Const(Const::Bool(val)) => Ok(to_value(val)), + Node::Const(Const::String(ref value)) => Ok(Value::string(value.to_string())), + Node::Const(Const::Bool(value)) => Ok(Value::boolean(value)), Node::Block(ref es) => { { let env = &mut self.realm.environment; @@ -88,7 +87,7 @@ impl Executor for Interpreter { ))); } - let mut obj = to_value(None::<()>); + let mut obj = Value::null(); for e in es.iter() { let val = self.run(e)?; // early return @@ -160,7 +159,7 @@ impl Executor for Interpreter { fnct_result } Node::WhileLoop(ref cond, ref expr) => { - let mut result = Gc::new(ValueData::Undefined); + let mut result = Value::undefined(); while self.run(cond)?.borrow().is_true() { result = self.run(expr)?; } @@ -189,12 +188,12 @@ impl Executor for Interpreter { } } - Ok(Gc::new(ValueData::Undefined)) + Ok(Value::undefined()) } Node::If(ref cond, ref expr, None) => Ok(if self.run(cond)?.borrow().is_true() { self.run(expr)? } else { - Gc::new(ValueData::Undefined) + Value::undefined() }), Node::If(ref cond, ref expr, Some(ref else_e)) => { Ok(if self.run(cond)?.borrow().is_true() { @@ -205,7 +204,7 @@ impl Executor for Interpreter { } Node::Switch(ref val_e, ref vals, ref default) => { let val = self.run(val_e)?; - let mut result = Gc::new(ValueData::Null); + let mut result = Value::null(); let mut matched = false; for tup in vals.iter() { let cond = &tup.0; @@ -236,7 +235,7 @@ impl Executor for Interpreter { .environment .get_global_object() .expect("Could not get the global object"); - let obj = ValueData::new_obj(Some(global_val)); + let obj = Value::new_object(Some(global_val)); // TODO: Implement the rest of the property types. for property in properties.iter() { @@ -260,7 +259,7 @@ impl Executor for Interpreter { } Node::ArrayDecl(ref arr) => { let array = array::new_array(self)?; - let mut elements: Vec = vec![]; + let mut elements = Vec::new(); for elem in arr.iter() { if let Node::Spread(ref x) = elem.deref() { let val = self.run(x)?; @@ -293,11 +292,11 @@ impl Executor for Interpreter { let mut new_func = Object::function(); new_func.set_call(func); - let val = to_value(new_func); - val.set_field_slice("length", to_value(args.len())); + let val = Value::from(new_func); + val.set_field_slice("length", Value::from(args.len())); // Set the name and assign it in the current environment - val.set_field_slice("name", to_value(name.clone())); + val.set_field_slice("name", Value::from(name.clone())); self.realm.environment.create_mutable_binding( name.clone(), false, @@ -328,11 +327,11 @@ impl Executor for Interpreter { let mut new_func = Object::function(); new_func.set_call(func); - let val = to_value(new_func); - val.set_field_slice("length", to_value(args.len())); + let val = Value::from(new_func); + val.set_field_slice("length", Value::from(args.len())); if let Some(name) = name { - val.set_field_slice("name", to_value(name.clone())); + val.set_field_slice("name", Value::from(name.clone())); } Ok(val) @@ -356,50 +355,49 @@ impl Executor for Interpreter { let mut new_func = Object::function(); new_func.set_call(func); - let val = to_value(new_func); - val.set_field_slice("length", to_value(args.len())); + let val = Value::from(new_func); + val.set_field_slice("length", Value::from(args.len())); Ok(val) } Node::BinOp(BinOp::Num(ref op), ref a, ref b) => { - let v_r_a = self.run(a)?; - let v_r_b = self.run(b)?; - let v_a = (*v_r_a).clone(); - let v_b = (*v_r_b).clone(); - Ok(Gc::new(match *op { + let v_a = self.run(a)?; + let v_b = self.run(b)?; + Ok(match *op { NumOp::Add => v_a + v_b, NumOp::Sub => v_a - v_b, NumOp::Mul => v_a * v_b, NumOp::Exp => v_a.as_num_to_power(v_b), NumOp::Div => v_a / v_b, NumOp::Mod => v_a % v_b, - })) + }) } Node::UnaryOp(ref op, ref a) => { - let v_r_a = self.run(a)?; - let v_a = (*v_r_a).clone(); - Ok(match op { - UnaryOp::Minus => to_value(-v_a.to_number()), - UnaryOp::Plus => to_value(v_a.to_number()), + let v_a = self.run(a)?; + Ok(match *op { + UnaryOp::Minus => Value::from(-v_a.to_number()), + UnaryOp::Plus => Value::from(v_a.to_number()), UnaryOp::IncrementPost => { - self.set_value(a.deref(), to_value(v_a.to_number() + 1.0))?; - v_r_a + let ret = v_a.clone(); + self.set_value(a, Value::from(v_a.to_number() + 1.0))?; + ret } UnaryOp::IncrementPre => { - self.set_value(a.deref(), to_value(v_a.to_number() + 1.0))? + self.set_value(a, Value::from(v_a.to_number() + 1.0))? } UnaryOp::DecrementPost => { - self.set_value(a.deref(), to_value(v_a.to_number() - 1.0))?; - v_r_a + let ret = v_a.clone(); + self.set_value(a, Value::from(v_a.to_number() - 1.0))?; + ret } UnaryOp::DecrementPre => { - self.set_value(a.deref(), to_value(v_a.to_number() - 1.0))? + self.set_value(a, Value::from(v_a.to_number() - 1.0))? } - UnaryOp::Not => Gc::new(!v_a), + UnaryOp::Not => !v_a, UnaryOp::Tilde => { let num_v_a = v_a.to_number(); // NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184 - to_value(if num_v_a.is_nan() { + Value::from(if num_v_a.is_nan() { -1 } else { !(num_v_a as i32) @@ -409,11 +407,9 @@ impl Executor for Interpreter { }) } Node::BinOp(BinOp::Bit(ref op), ref a, ref b) => { - let v_r_a = self.run(a)?; - let v_r_b = self.run(b)?; - let v_a = (*v_r_a).clone(); - let v_b = (*v_r_b).clone(); - Ok(Gc::new(match *op { + let v_a = self.run(a)?; + let v_b = self.run(b)?; + Ok(match *op { BitOp::And => v_a & v_b, BitOp::Or => v_a | v_b, BitOp::Xor => v_a ^ v_b, @@ -421,14 +417,14 @@ impl Executor for Interpreter { BitOp::Shr => v_a >> v_b, // TODO Fix BitOp::UShr => v_a >> v_b, - })) + }) } Node::BinOp(BinOp::Comp(ref op), ref a, ref b) => { let mut v_r_a = self.run(a)?; let mut v_r_b = self.run(b)?; let mut v_a = v_r_a.borrow_mut(); let mut v_b = v_r_b.borrow_mut(); - Ok(to_value(match *op { + Ok(Value::from(match *op { CompOp::Equal if v_a.is_object() => v_r_a == v_r_b, CompOp::Equal => v_a == v_b, CompOp::NotEqual if v_a.is_object() => v_r_a != v_r_b, @@ -452,17 +448,16 @@ impl Executor for Interpreter { } Node::BinOp(BinOp::Log(ref op), ref a, ref b) => { // turn a `Value` into a `bool` - let to_bool = - |val| from_value::(val).expect("Could not convert JS value to bool"); + let to_bool = |value| bool::from(&value); Ok(match *op { - LogOp::And => to_value(to_bool(self.run(a)?) && to_bool(self.run(b)?)), - LogOp::Or => to_value(to_bool(self.run(a)?) || to_bool(self.run(b)?)), + LogOp::And => Value::from(to_bool(self.run(a)?) && to_bool(self.run(b)?)), + LogOp::Or => Value::from(to_bool(self.run(a)?) || to_bool(self.run(b)?)), }) } Node::BinOp(BinOp::Assign(ref op), ref a, ref b) => match a.deref() { Node::Local(ref name) => { - let v_a = (*self.realm.environment.get_binding_value(&name)).clone(); - let v_b = (*self.run(b)?).clone(); + let v_a = self.realm.environment.get_binding_value(&name); + let v_b = self.run(b)?; let value = exec_assign_op(op, v_a, v_b); self.realm .environment @@ -471,15 +466,15 @@ impl Executor for Interpreter { } Node::GetConstField(ref obj, ref field) => { let v_r_a = self.run(obj)?; - let v_a = (*v_r_a.borrow().get_field_slice(field)).clone(); - let v_b = (*self.run(b)?).clone(); + let v_a = v_r_a.get_field_slice(field); + let v_b = self.run(b)?; let value = exec_assign_op(op, v_a, v_b); v_r_a .borrow() .set_field_slice(&field.clone(), value.clone()); Ok(value) } - _ => Ok(Gc::new(ValueData::Undefined)), + _ => Ok(Value::undefined()), }, Node::New(ref call) => { let (callee, args) = match call.as_ref() { @@ -492,7 +487,7 @@ impl Executor for Interpreter { for arg in args.iter() { v_args.push(self.run(arg)?); } - let mut this = ValueData::new_obj(None); + let mut this = Value::new_object(None); // Create a blank object, then set its __proto__ property to the [Constructor].prototype this.borrow().set_internal_slot( INSTANCE_PROTOTYPE, @@ -505,13 +500,13 @@ impl Executor for Interpreter { .as_ref() .unwrap() .construct(&mut func_object.clone(), &v_args, self, &mut this), - _ => Ok(Gc::new(ValueData::Undefined)), + _ => Ok(Value::undefined()), } } Node::Return(ref ret) => { let result = match *ret { Some(ref v) => self.run(v), - None => Ok(Gc::new(ValueData::Undefined)), + None => Ok(Value::undefined()), }; // Set flag for return self.is_return = true; @@ -556,7 +551,7 @@ impl Executor for Interpreter { let (name, value) = var.clone(); let val = match value { Some(v) => self.run(&v)?, - None => Gc::new(ValueData::Undefined), + None => Value::undefined(), }; self.realm.environment.create_mutable_binding( name.clone(), @@ -565,14 +560,14 @@ impl Executor for Interpreter { ); self.realm.environment.initialize_binding(&name, val); } - Ok(Gc::new(ValueData::Undefined)) + Ok(Value::undefined()) } Node::LetDecl(ref vars) => { for var in vars.iter() { let (name, value) = var.clone(); let val = match value { Some(v) => self.run(&v)?, - None => Gc::new(ValueData::Undefined), + None => Value::undefined(), }; self.realm.environment.create_mutable_binding( name.clone(), @@ -581,7 +576,7 @@ impl Executor for Interpreter { ); self.realm.environment.initialize_binding(&name, val); } - Ok(Gc::new(ValueData::Undefined)) + Ok(Value::undefined()) } Node::ConstDecl(ref vars) => { for (name, value) in vars.iter() { @@ -593,11 +588,11 @@ impl Executor for Interpreter { let val = self.run(&value)?; self.realm.environment.initialize_binding(&name, val); } - Ok(Gc::new(ValueData::Undefined)) + Ok(Value::undefined()) } Node::TypeOf(ref val_e) => { let val = self.run(val_e)?; - Ok(to_value(match *val { + Ok(Value::from(match *val { ValueData::Undefined => "undefined", ValueData::Symbol(_) => "symbol", ValueData::Null => "object", @@ -621,7 +616,7 @@ impl Executor for Interpreter { ))); } - let mut obj = to_value(None::<()>); + let mut obj = Value::null(); for (i, item) in list.iter().enumerate() { let val = self.run(item)?; // early return @@ -641,7 +636,7 @@ impl Executor for Interpreter { } Node::Spread(ref node) => { // TODO: for now we can do nothing but return the value as-is - Ok(Gc::new((*self.run(node)?).clone())) + self.run(node) } ref i => unimplemented!("{}", i), } @@ -668,7 +663,7 @@ impl Interpreter { Some(ref func) => func.call(&mut f.clone(), arguments_list, self, this), None => panic!("Expected function"), }, - _ => Err(Gc::new(ValueData::Undefined)), + _ => Err(Value::undefined()), } } @@ -699,7 +694,7 @@ impl Interpreter { } } - Gc::new(ValueData::Undefined) + Value::undefined() } /// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. @@ -734,17 +729,17 @@ impl Interpreter { #[allow(clippy::wrong_self_convention)] pub fn to_string(&mut self, value: &Value) -> Value { match *value.deref().borrow() { - ValueData::Undefined => to_value("undefined"), - ValueData::Null => to_value("null"), - ValueData::Boolean(ref boolean) => to_value(boolean.to_string()), - ValueData::Rational(ref num) => to_value(num.to_string()), - ValueData::Integer(ref num) => to_value(num.to_string()), - ValueData::String(ref string) => to_value(string.clone()), + ValueData::Undefined => Value::from("undefined"), + ValueData::Null => Value::from("null"), + ValueData::Boolean(ref boolean) => Value::from(boolean.to_string()), + ValueData::Rational(ref num) => Value::from(num.to_string()), + ValueData::Integer(ref num) => Value::from(num.to_string()), + ValueData::String(ref string) => Value::from(string.clone()), ValueData::Object(_) => { let prim_value = self.to_primitive(&mut (value.clone()), Some("string")); self.to_string(&prim_value) } - _ => to_value("function(){...}"), + _ => Value::from("function(){...}"), } } @@ -779,7 +774,7 @@ impl Interpreter { pub fn to_object(&mut self, value: &Value) -> ResultValue { match *value.deref().borrow() { ValueData::Undefined | ValueData::Integer(_) | ValueData::Null => { - Err(Gc::new(ValueData::Undefined)) + Err(Value::undefined()) } ValueData::Boolean(_) => { let proto = self @@ -788,7 +783,7 @@ impl Interpreter { .get_binding_value("Boolean") .get_field_slice(PROTOTYPE); - let bool_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::Boolean); + let bool_obj = Value::new_object_from_prototype(proto, ObjectKind::Boolean); bool_obj.set_internal_slot("BooleanData", value.clone()); Ok(bool_obj) } @@ -798,7 +793,7 @@ impl Interpreter { .environment .get_binding_value("Number") .get_field_slice(PROTOTYPE); - let number_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::Number); + let number_obj = Value::new_object_from_prototype(proto, ObjectKind::Number); number_obj.set_internal_slot("NumberData", value.clone()); Ok(number_obj) } @@ -808,7 +803,7 @@ impl Interpreter { .environment .get_binding_value("String") .get_field_slice(PROTOTYPE); - let string_obj = ValueData::new_obj_from_prototype(proto, ObjectKind::String); + let string_obj = Value::new_object_from_prototype(proto, ObjectKind::String); string_obj.set_internal_slot("StringData", value.clone()); Ok(string_obj) } @@ -861,15 +856,15 @@ impl Interpreter { /// `extract_array_properties` converts an array object into a rust vector of Values. /// This is useful for the spread operator, for any other object an `Err` is returned - fn extract_array_properties(&mut self, value: &Value) -> Result>, ()> { + fn extract_array_properties(&mut self, value: &Value) -> Result, ()> { if let ValueData::Object(ref x) = *value.deref().borrow() { // Check if object is array if x.deref().borrow().kind == ObjectKind::Array { let length: i32 = self.value_to_rust_number(&value.get_field_slice("length")) as i32; - let values: Vec> = (0..length) + let values: Vec = (0..length) .map(|idx| value.get_field_slice(&idx.to_string())) - .collect::>(); + .collect(); return Ok(values); } diff --git a/boa/src/realm.rs b/boa/src/realm.rs index bfd8fbcae1..ba044f4645 100644 --- a/boa/src/realm.rs +++ b/boa/src/realm.rs @@ -8,7 +8,7 @@ use crate::{ builtins::{ self, function::NativeFunctionData, - value::{ToValue, Value, ValueData}, + value::{Value, ValueData}, }, environment::{ declarative_environment_record::DeclarativeEnvironmentRecord, @@ -34,7 +34,7 @@ impl Realm { pub fn create() -> Self { // Create brand new global object // Global has no prototype to pass None to new_obj - let global = ValueData::new_obj(None); + let global = Value::new_object(None); // We need to clone the global here because its referenced from separate places (only pointer is cloned) let global_env = new_global_environment(global.clone(), global.clone()); @@ -65,7 +65,7 @@ impl Realm { crate::builtins::function::FunctionBody::BuiltIn(func), ); self.global_obj - .set_field(func_name.to_value(), ValueData::from_func(func)); + .set_field(Value::from(func_name), ValueData::from_func(func)); self }