diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 00cef05873..37c7d4740b 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -17,7 +17,7 @@ use crate::{ builtins::{ object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, property::Property, - value::{same_value_zero, ResultValue, Value, ValueData}, + value::{same_value_zero, ResultValue, Value}, }, exec::Interpreter, BoaProfiler, @@ -109,11 +109,7 @@ impl Array { } /// Create a new array - pub(crate) fn make_array( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn make_array(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // Make a new Object which will internally represent the Array (mapping // between indices and values): this creates an Object with no prototype @@ -169,7 +165,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.isarray /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/isArray pub(crate) fn is_array( - _this: &mut Value, + _this: &Value, args: &[Value], _interpreter: &mut Interpreter, ) -> ResultValue { @@ -191,7 +187,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.concat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/concat - pub(crate) fn concat(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn concat(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if args.is_empty() { // If concat is called with no arguments, it returns the original array return Ok(this.clone()); @@ -228,7 +224,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.push /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/push - pub(crate) fn push(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn push(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let new_array = Self::add_to_array_object(this, args)?; Ok(new_array.get_field("length")) } @@ -243,7 +239,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.pop /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/pop - pub(crate) fn pop(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn pop(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let curr_length = i32::from(&this.get_field("length")); if curr_length < 1 { return Ok(Value::undefined()); @@ -266,7 +262,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.foreach /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach pub(crate) fn for_each( - this: &mut Value, + this: &Value, args: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { @@ -275,7 +271,7 @@ impl Array { } let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); - let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); + let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); let length = i32::from(&this.get_field("length")); @@ -283,7 +279,7 @@ impl Array { let element = this.get_field(i.to_string()); let arguments = [element, Value::from(i), this.clone()]; - interpreter.call(callback_arg, &mut this_arg, &arguments)?; + interpreter.call(callback_arg, &this_arg, &arguments)?; } Ok(Value::undefined()) @@ -301,17 +297,18 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.join /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/join - pub(crate) fn join(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn join(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let separator = if args.is_empty() { String::from(",") } else { ctx.to_string(args.get(0).expect("Could not get argument"))? + .to_string() }; - let mut elem_strs: Vec = Vec::new(); + let mut elem_strs = Vec::new(); let length = i32::from(&this.get_field("length")); for n in 0..length { - let elem_str: String = ctx.to_string(&this.get_field(n.to_string()))?; + let elem_str = ctx.to_string(&this.get_field(n.to_string()))?.to_string(); elem_strs.push(elem_str); } @@ -331,11 +328,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string( - this: &mut Value, - _args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn to_string(this: &Value, _args: &[Value], ctx: &mut Interpreter) -> ResultValue { let method_name = "join"; let mut arguments = vec![Value::from(",")]; // 2. @@ -354,7 +347,7 @@ impl Array { // 4. let join = ctx.call(&method, this, &arguments)?; - let string = if let ValueData::String(ref s) = join.data() { + let string = if let Value::String(ref s) = join { Value::from(s.as_str()) } else { Value::from("") @@ -375,7 +368,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.reverse /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse #[allow(clippy::else_if_without_else)] - pub(crate) fn reverse(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn reverse(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let len = i32::from(&this.get_field("length")); let middle: i32 = len.wrapping_div(2); @@ -413,7 +406,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.shift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/shift - pub(crate) fn shift(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn shift(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let len = i32::from(&this.get_field("length")); if len == 0 { @@ -455,7 +448,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.unshift /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/unshift - pub(crate) fn unshift(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn unshift(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let len = i32::from(&this.get_field("length")); let arg_c: i32 = args.len() as i32; @@ -500,7 +493,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.every /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every pub(crate) fn every( - this: &mut Value, + this: &Value, args: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { @@ -510,7 +503,7 @@ impl Array { )); } let callback = &args[0]; - let mut this_arg = if args.len() > 1 { + let this_arg = if args.len() > 1 { args[1].clone() } else { Value::undefined() @@ -521,9 +514,7 @@ impl Array { while i < len { let element = this.get_field(i.to_string()); let arguments = [element, Value::from(i), this.clone()]; - let result = interpreter - .call(callback, &mut this_arg, &arguments)? - .is_true(); + let result = interpreter.call(callback, &this_arg, &arguments)?.is_true(); if !result { return Ok(Value::from(false)); } @@ -544,11 +535,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.map /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map - pub(crate) fn map( - this: &mut Value, - args: &[Value], - interpreter: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn map(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { return Err(Value::from( "missing argument 0 when calling function Array.prototype.map", @@ -556,7 +543,7 @@ impl Array { } let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); - let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); + let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); let length = i32::from(&this.get_field("length")); @@ -568,7 +555,7 @@ impl Array { let args = [element, Value::from(idx), new.clone()]; interpreter - .call(&callback, &mut this_val, &args) + .call(&callback, &this_val, &args) .unwrap_or_else(|_| Value::undefined()) }) .collect(); @@ -595,7 +582,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.indexof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf - pub(crate) fn index_of(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { // If no arguments, return -1. Not described in spec, but is what chrome does. if args.is_empty() { return Ok(Value::from(-1)); @@ -648,11 +635,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.lastindexof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/lastIndexOf - pub(crate) fn last_index_of( - this: &mut Value, - args: &[Value], - _: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn last_index_of(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { // If no arguments, return -1. Not described in spec, but is what chrome does. if args.is_empty() { return Ok(Value::from(-1)); @@ -699,27 +682,19 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.find /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find - pub(crate) fn find( - this: &mut Value, - args: &[Value], - interpreter: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn find(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { return Err(Value::from( "missing callback when calling function Array.prototype.find", )); } let callback = &args[0]; - let mut this_arg = if args.len() > 1 { - args[1].clone() - } else { - Value::undefined() - }; + let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); let len = i32::from(&this.get_field("length")); for i in 0..len { let element = this.get_field(i.to_string()); let arguments = [element.clone(), Value::from(i), this.clone()]; - let result = interpreter.call(callback, &mut this_arg, &arguments)?; + let result = interpreter.call(callback, &this_arg, &arguments)?; if result.is_true() { return Ok(element); } @@ -740,7 +715,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.findindex /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex pub(crate) fn find_index( - this: &mut Value, + this: &Value, args: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { @@ -752,7 +727,7 @@ impl Array { let predicate_arg = args.get(0).expect("Could not get `predicate` argument."); - let mut this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); + let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); let length = i32::from(&this.get_field("length")); @@ -760,7 +735,7 @@ impl Array { let element = this.get_field(i.to_string()); let arguments = [element, Value::from(i), this.clone()]; - let result = interpreter.call(predicate_arg, &mut this_arg, &arguments)?; + let result = interpreter.call(predicate_arg, &this_arg, &arguments)?; if result.is_true() { return Ok(Value::rational(f64::from(i))); @@ -781,7 +756,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.fill /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/fill - pub(crate) fn fill(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn fill(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let len: i32 = i32::from(&this.get_field("length")); let default_value = Value::undefined(); let value = args.get(0).unwrap_or(&default_value); @@ -820,11 +795,7 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.includes /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/includes - pub(crate) fn includes_value( - this: &mut Value, - args: &[Value], - _: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn includes_value(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let search_element = args.get(0).cloned().unwrap_or_else(Value::undefined); let length = i32::from(&this.get_field("length")); @@ -855,7 +826,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.slice /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/slice pub(crate) fn slice( - this: &mut Value, + this: &Value, args: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { @@ -904,7 +875,7 @@ impl Array { /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.filter /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter pub(crate) fn filter( - this: &mut Value, + this: &Value, args: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { @@ -915,7 +886,7 @@ impl Array { } let callback = args.get(0).cloned().unwrap_or_else(Value::undefined); - let mut this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); + let this_val = args.get(1).cloned().unwrap_or_else(Value::undefined); let length = i32::from(&this.get_field("length")); @@ -928,7 +899,7 @@ impl Array { let args = [element.clone(), Value::from(idx), new.clone()]; let callback_result = interpreter - .call(&callback, &mut this_val, &args) + .call(&callback, &this_val, &args) .unwrap_or_else(|_| Value::undefined()); if callback_result.is_true() { @@ -957,18 +928,14 @@ impl Array { /// /// [spec]: https://tc39.es/ecma262/#sec-array.prototype.some /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some - pub(crate) fn some( - this: &mut Value, - args: &[Value], - interpreter: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn some(this: &Value, args: &[Value], interpreter: &mut Interpreter) -> ResultValue { if args.is_empty() { return Err(Value::from( "missing callback when calling function Array.prototype.some", )); } let callback = &args[0]; - let mut this_arg = if args.len() > 1 { + let this_arg = if args.len() > 1 { args[1].clone() } else { Value::undefined() @@ -979,9 +946,7 @@ impl Array { while i < len { let element = this.get_field(i.to_string()); let arguments = [element, Value::from(i), this.clone()]; - let result = interpreter - .call(callback, &mut this_arg, &arguments)? - .is_true(); + let result = interpreter.call(callback, &this_arg, &arguments)?.is_true(); if result { return Ok(Value::from(true)); } diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 577f71e118..e21643b003 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -16,7 +16,7 @@ use crate::{ builtins::{ function::{make_builtin_fn, make_constructor_fn}, object::ObjectData, - value::{ResultValue, Value, ValueData}, + value::{RcBigInt, ResultValue, Value}, }, exec::Interpreter, BoaProfiler, @@ -61,15 +61,15 @@ impl BigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-thisbigintvalue #[inline] - fn this_bigint_value(value: &Value, ctx: &mut Interpreter) -> Result { - match value.data() { + fn this_bigint_value(value: &Value, ctx: &mut Interpreter) -> Result { + match value { // 1. If Type(value) is BigInt, return value. - ValueData::BigInt(ref bigint) => return Ok(bigint.clone()), + Value::BigInt(ref bigint) => return Ok(bigint.clone()), // 2. If Type(value) is Object and value has a [[BigIntData]] internal slot, then // a. Assert: Type(value.[[BigIntData]]) is BigInt. // b. Return value.[[BigIntData]]. - ValueData::Object(ref object) => { + Value::Object(ref object) => { if let ObjectData::BigInt(ref bigint) = object.borrow().data { return Ok(bigint.clone()); } @@ -91,10 +91,10 @@ impl BigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-bigint-objects /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt - pub(crate) fn make_bigint(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn make_bigint(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let data = match args.get(0) { Some(ref value) => ctx.to_bigint(value)?, - None => Self::from(0), + None => RcBigInt::from(Self::from(0)), }; Ok(Value::from(data)) } @@ -110,11 +110,7 @@ impl BigInt { /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let radix = if !args.is_empty() { args[0].to_integer() } else { @@ -139,11 +135,7 @@ impl BigInt { /// /// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf - pub(crate) fn value_of( - this: &mut Value, - _args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(Value::from(Self::this_bigint_value(this, ctx)?)) } @@ -154,11 +146,7 @@ impl BigInt { /// [spec]: https://tc39.es/ecma262/#sec-bigint.asintn /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asIntN #[allow(clippy::wrong_self_convention)] - pub(crate) fn as_int_n( - _this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn as_int_n(_this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let (modulo, bits) = Self::calculate_as_uint_n(args, ctx)?; if bits > 0 && modulo >= BigInt::from(2).pow(&BigInt::from(bits as i64 - 1)) { @@ -177,11 +165,7 @@ impl BigInt { /// [spec]: https://tc39.es/ecma262/#sec-bigint.asuintn /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/asUintN #[allow(clippy::wrong_self_convention)] - pub(crate) fn as_uint_n( - _this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn as_uint_n(_this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let (modulo, _) = Self::calculate_as_uint_n(args, ctx)?; Ok(Value::from(modulo)) @@ -206,7 +190,10 @@ impl BigInt { let bigint = ctx.to_bigint(bigint_arg)?; Ok(( - bigint.mod_floor(&BigInt::from(2).pow(&BigInt::from(bits as i64))), + bigint + .as_inner() + .clone() + .mod_floor(&BigInt::from(2).pow(&BigInt::from(bits as i64))), bits, )) } diff --git a/boa/src/builtins/boolean/mod.rs b/boa/src/builtins/boolean/mod.rs index 652147e90c..a0b9df09ff 100644 --- a/boa/src/builtins/boolean/mod.rs +++ b/boa/src/builtins/boolean/mod.rs @@ -16,7 +16,7 @@ use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ builtins::{ object::ObjectData, - value::{ResultValue, Value, ValueData}, + value::{ResultValue, Value}, }, exec::Interpreter, BoaProfiler, @@ -40,9 +40,9 @@ impl Boolean { /// /// [spec]: https://tc39.es/ecma262/#sec-thisbooleanvalue fn this_boolean_value(value: &Value, ctx: &mut Interpreter) -> Result { - match value.data() { - ValueData::Boolean(boolean) => return Ok(*boolean), - ValueData::Object(ref object) => { + match value { + Value::Boolean(boolean) => return Ok(*boolean), + Value::Object(ref object) => { let object = object.borrow(); if let Some(boolean) = object.as_boolean() { return Ok(boolean); @@ -58,7 +58,7 @@ impl Boolean { /// /// `[[Call]]` Creates a new boolean primitive pub(crate) fn construct_boolean( - this: &mut Value, + this: &Value, args: &[Value], _: &mut Interpreter, ) -> ResultValue { @@ -78,7 +78,7 @@ impl Boolean { /// [spec]: https://tc39.es/ecma262/#sec-boolean-object /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let boolean = Self::this_boolean_value(this, ctx)?; Ok(Value::from(boolean.to_string())) } @@ -92,7 +92,7 @@ impl Boolean { /// [spec]: https://tc39.es/ecma262/#sec-boolean.prototype.valueof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Boolean/valueOf #[inline] - pub(crate) fn value_of(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn value_of(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(Value::from(Self::this_boolean_value(this, ctx)?)) } diff --git a/boa/src/builtins/boolean/tests.rs b/boa/src/builtins/boolean/tests.rs index 200e3eaa37..b036948726 100644 --- a/boa/src/builtins/boolean/tests.rs +++ b/boa/src/builtins/boolean/tests.rs @@ -71,7 +71,6 @@ fn instances_have_correct_proto_set() { assert!(same_value( &bool_instance.get_internal_slot("__proto__"), - &bool_prototype, - true + &bool_prototype )); } diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 39bcb43b41..056e5b7281 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -20,7 +20,7 @@ use crate::{ builtins::{ function::make_builtin_fn, object::InternalState, - value::{display_obj, ResultValue, Value}, + value::{display_obj, RcString, ResultValue, Value}, }, exec::Interpreter, BoaProfiler, @@ -31,8 +31,8 @@ use std::time::SystemTime; /// This is the internal console object state. #[derive(Debug, Default)] pub struct ConsoleState { - count_map: FxHashMap, - timer_map: FxHashMap, + count_map: FxHashMap, + timer_map: FxHashMap, groups: Vec, } @@ -74,7 +74,7 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result let target = ctx.to_string(&data.get(0).cloned().unwrap_or_default())?; match data.len() { 0 => Ok(String::new()), - 1 => Ok(target), + 1 => Ok(target.to_string()), _ => { let mut formatted = String::new(); let mut arg_index = 1; @@ -141,7 +141,7 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result /// /// [spec]: https://console.spec.whatwg.org/#assert /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/assert -pub fn assert(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn assert(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let assertion = get_arg_at_index::(args, 0).unwrap_or_default(); if !assertion { @@ -175,7 +175,7 @@ pub fn assert(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Result /// /// [spec]: https://console.spec.whatwg.org/#clear /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/clear -pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn clear(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_mut(|state: &mut ConsoleState| { state.groups.clear(); }); @@ -193,7 +193,7 @@ pub fn clear(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue /// /// [spec]: https://console.spec.whatwg.org/#debug /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/debug -pub fn debug(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn debug(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { logger(LogMessage::Log(formatter(args, ctx)?), state); Ok(()) @@ -211,7 +211,7 @@ pub fn debug(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV /// /// [spec]: https://console.spec.whatwg.org/#error /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/error -pub fn error(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { logger(LogMessage::Error(formatter(args, ctx)?), state); Ok(()) @@ -229,7 +229,7 @@ pub fn error(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV /// /// [spec]: https://console.spec.whatwg.org/#info /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/info -pub fn info(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn info(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { logger(LogMessage::Info(formatter(args, ctx)?), state); Ok(()) @@ -247,7 +247,7 @@ pub fn info(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa /// /// [spec]: https://console.spec.whatwg.org/#log /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/log -pub fn log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn log(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { logger(LogMessage::Log(formatter(args, ctx)?), state); Ok(()) @@ -265,7 +265,7 @@ pub fn log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVal /// /// [spec]: https://console.spec.whatwg.org/#trace /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/trace -pub fn trace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn trace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if !args.is_empty() { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { logger(LogMessage::Log(formatter(args, ctx)?), state); @@ -294,7 +294,7 @@ pub fn trace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV /// /// [spec]: https://console.spec.whatwg.org/#warn /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/warn -pub fn warn(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn warn(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { this.with_internal_state_ref::<_, Result<(), Value>, _>(|state| { logger(LogMessage::Warn(formatter(args, ctx)?), state); Ok(()) @@ -312,10 +312,10 @@ pub fn warn(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa /// /// [spec]: https://console.spec.whatwg.org/#count /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count -pub fn count(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn count(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { Some(value) => ctx.to_string(value)?, - None => "default".to_owned(), + None => "default".into(), }; this.with_internal_state_mut(|state: &mut ConsoleState| { @@ -339,10 +339,10 @@ pub fn count(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV /// /// [spec]: https://console.spec.whatwg.org/#countreset /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset -pub fn count_reset(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn count_reset(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { Some(value) => ctx.to_string(value)?, - None => "default".to_owned(), + None => "default".into(), }; this.with_internal_state_mut(|state: &mut ConsoleState| { @@ -372,10 +372,10 @@ fn system_time_in_ms() -> u128 { /// /// [spec]: https://console.spec.whatwg.org/#time /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time -pub fn time(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn time(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { Some(value) => ctx.to_string(value)?, - None => "default".to_owned(), + None => "default".into(), }; this.with_internal_state_mut(|state: &mut ConsoleState| { @@ -403,10 +403,10 @@ pub fn time(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultVa /// /// [spec]: https://console.spec.whatwg.org/#timelog /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog -pub fn time_log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn time_log(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { Some(value) => ctx.to_string(value)?, - None => "default".to_owned(), + None => "default".into(), }; this.with_internal_state_mut(|state: &mut ConsoleState| { @@ -438,14 +438,14 @@ pub fn time_log(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu /// /// [spec]: https://console.spec.whatwg.org/#timeend /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd -pub fn time_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn time_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { Some(value) => ctx.to_string(value)?, - None => "default".to_owned(), + None => "default".into(), }; this.with_internal_state_mut(|state: &mut ConsoleState| { - if let Some(t) = state.timer_map.remove(&label) { + if let Some(t) = state.timer_map.remove(label.as_str()) { let time = system_time_in_ms(); logger( LogMessage::Info(format!("{}: {} ms - timer removed", label, time - t)), @@ -472,7 +472,7 @@ pub fn time_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu /// /// [spec]: https://console.spec.whatwg.org/#group /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/group -pub fn group(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn group(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let group_label = formatter(args, ctx)?; this.with_internal_state_mut(|state: &mut ConsoleState| { @@ -493,7 +493,7 @@ pub fn group(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultV /// /// [spec]: https://console.spec.whatwg.org/#groupend /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/groupEnd -pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn group_end(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_mut(|state: &mut ConsoleState| { state.groups.pop(); }); @@ -511,7 +511,7 @@ pub fn group_end(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa /// /// [spec]: https://console.spec.whatwg.org/#dir /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/dir -pub fn dir(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn dir(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_mut(|state: &mut ConsoleState| { let undefined = Value::undefined(); logger( diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index 59da37a51a..dd4d9d63a4 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -43,7 +43,7 @@ impl Error { pub(crate) const LENGTH: usize = 1; /// Create a new error object. - pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if !args.is_empty() { this.set_field( "message", @@ -71,7 +71,7 @@ impl Error { /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let name = this.get_field("name"); let message = this.get_field("message"); Ok(Value::from(format!("{}: {}", name, message))) diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index c51df0b350..e39bdd16d8 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -32,7 +32,7 @@ impl RangeError { pub(crate) const LENGTH: usize = 1; /// Create a new error object. - pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if !args.is_empty() { this.set_field( "message", @@ -60,7 +60,7 @@ impl RangeError { /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let name = this.get_field("name"); let message = this.get_field("message"); Ok(Value::from(format!("{}: {}", name, message))) diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index fa59dc2d92..67befc5384 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -31,7 +31,7 @@ impl ReferenceError { pub(crate) const LENGTH: usize = 1; /// Create a new error object. - pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if !args.is_empty() { this.set_field( "message", @@ -59,7 +59,7 @@ impl ReferenceError { /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let name = this.get_field("name"); let message = this.get_field("message"); Ok(Value::from(format!("{}: {}", name, message))) diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index 3a4f2de977..54251416ba 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -38,7 +38,7 @@ impl TypeError { pub(crate) const LENGTH: usize = 1; /// Create a new error object. - pub(crate) fn make_error(this: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn make_error(this: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { if !args.is_empty() { this.set_field( "message", @@ -67,7 +67,7 @@ impl TypeError { /// [spec]: https://tc39.es/ecma262/#sec-error.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let name = this.get_field("name"); let message = this.get_field("message"); Ok(Value::from(format!("{}: {}", name, message))) diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index 0e1653a81b..1e19f78afe 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -16,7 +16,7 @@ use crate::{ array::Array, object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, property::Property, - value::{ResultValue, Value}, + value::{RcString, ResultValue, Value}, }, environment::function_environment_record::BindingStatus, environment::lexical_environment::{new_function_environment, Environment}, @@ -28,7 +28,7 @@ use gc::{unsafe_empty_trace, Finalize, Trace}; use std::fmt::{self, Debug}; /// _fn(this, arguments, ctx) -> ResultValue_ - The signature of a built-in function -pub type NativeFunctionData = fn(&mut Value, &[Value], &mut Interpreter) -> ResultValue; +pub type NativeFunctionData = fn(&Value, &[Value], &mut Interpreter) -> ResultValue; /// Sets the ConstructorKind #[derive(Debug, Copy, Clone)] @@ -178,7 +178,7 @@ impl Function { pub fn call( &self, function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object) - this: &mut Value, + this: &Value, args_list: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { @@ -236,7 +236,7 @@ impl Function { pub fn construct( &self, function: Value, // represents a pointer to this function object wrapped in a GC (not a `this` JS object) - this: &mut Value, + this: &Value, args_list: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { @@ -376,7 +376,8 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { .writable(true) .configurable(true); - obj.properties_mut().insert(index.to_string(), prop); + obj.properties_mut() + .insert(RcString::from(index.to_string()), prop); index += 1; } @@ -386,7 +387,7 @@ pub fn create_unmapped_arguments_object(arguments_list: &[Value]) -> Value { /// Create new function `[[Construct]]` /// // This gets called when a new Function() is created. -pub fn make_function(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn make_function(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.set_data(ObjectData::Function(Function::builtin( Vec::new(), |_, _, _| Ok(Value::undefined()), diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 0b18599dad..d266c3019f 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -44,7 +44,7 @@ impl Json { /// /// [spec]: https://tc39.es/ecma262/#sec-json.parse /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse - pub(crate) fn parse(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn parse(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { match serde_json::from_str::( &ctx.to_string(args.get(0).expect("cannot get argument for JSON.parse"))?, ) { @@ -106,7 +106,7 @@ impl Json { /// /// [spec]: https://tc39.es/ecma262/#sec-json.stringify /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/stringify - pub(crate) fn stringify(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn stringify(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let object = match args.get(0) { Some(obj) if obj.is_symbol() || obj.is_function() || obj.is_undefined() => { return Ok(Value::undefined()) @@ -132,13 +132,13 @@ impl Json { .iter() .filter_map(|(k, v)| v.value.as_ref().map(|value| (k, value))) { - let mut this_arg = object.clone(); + let this_arg = object.clone(); object_to_return.set_property( key.to_owned(), Property::default().value(ctx.call( replacer, - &mut this_arg, - &[Value::string(key), val.clone()], + &this_arg, + &[Value::from(key.clone()), val.clone()], )?), ); } diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index e7f8cb84e8..130f7a41a2 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -302,13 +302,10 @@ fn json_parse_sets_prototypes() { .get_field("Array") .get_field(PROTOTYPE); assert_eq!( - same_value(&object_prototype, &global_object_prototype, true), - true - ); - assert_eq!( - same_value(&array_prototype, &global_array_prototype, true), + same_value(&object_prototype, &global_object_prototype), true ); + assert_eq!(same_value(&array_prototype, &global_array_prototype), true); } #[test] diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index eef2600712..4e6b3f4dbb 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -24,6 +24,7 @@ use std::f64; #[cfg(test)] mod tests; +/// Javascript `Math` object. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub(crate) struct Math; @@ -39,7 +40,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.abs /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/abs - pub(crate) fn abs(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn abs(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).abs()).into()) } @@ -51,7 +52,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.acos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acos - pub(crate) fn acos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn acos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).acos()).into()) } @@ -63,7 +64,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.acosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/acosh - pub(crate) fn acosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn acosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args .get(0) .map_or(f64::NAN, |x| f64::from(x).acosh()) @@ -78,7 +79,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.asin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asin - pub(crate) fn asin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn asin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).asin()).into()) } @@ -90,7 +91,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.asinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/asinh - pub(crate) fn asinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn asinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args .get(0) .map_or(f64::NAN, |x| f64::from(x).asinh()) @@ -105,7 +106,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.atan /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan - pub(crate) fn atan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn atan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).atan()).into()) } @@ -117,7 +118,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.atanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atanh - pub(crate) fn atanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn atanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args .get(0) .map_or(f64::NAN, |x| f64::from(x).atanh()) @@ -132,7 +133,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.atan2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 - pub(crate) fn atan2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn atan2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(if args.is_empty() { f64::NAN } else { @@ -149,7 +150,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.cbrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cbrt - pub(crate) fn cbrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn cbrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cbrt()).into()) } @@ -161,7 +162,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.ceil /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/ceil - pub(crate) fn ceil(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn ceil(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).ceil()).into()) } @@ -173,7 +174,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.cos /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cos - pub(crate) fn cos(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn cos(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cos()).into()) } @@ -185,7 +186,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.cosh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/cosh - pub(crate) fn cosh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn cosh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).cosh()).into()) } @@ -197,7 +198,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.exp /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/exp - pub(crate) fn exp(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn exp(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).exp()).into()) } @@ -209,7 +210,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.floor /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/floor - pub(crate) fn floor(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn floor(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args .get(0) .map_or(f64::NAN, |x| f64::from(x).floor()) @@ -224,7 +225,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.log /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log - pub(crate) fn log(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn log(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(if args.is_empty() { f64::NAN } else { @@ -246,7 +247,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.log10 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log10 - pub(crate) fn log10(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn log10(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(if args.is_empty() { f64::NAN } else { @@ -268,7 +269,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.log2 /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/log2 - pub(crate) fn log2(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn log2(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(if args.is_empty() { f64::NAN } else { @@ -290,7 +291,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.max /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/max - pub(crate) fn max(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn max(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let mut max = f64::NEG_INFINITY; for arg in args { let num = f64::from(arg); @@ -307,7 +308,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.min /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/min - pub(crate) fn min(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn min(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let mut max = f64::INFINITY; for arg in args { let num = f64::from(arg); @@ -324,7 +325,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.pow /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow - pub(crate) fn pow(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn pow(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(if args.len() >= 2 { let num = f64::from(args.get(0).expect("Could not get argument")); let power = f64::from(args.get(1).expect("Could not get argument")); @@ -342,7 +343,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.random /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/random - pub(crate) fn random(_: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn random(_: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(rand::random::())) } @@ -354,7 +355,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.round /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/round - pub(crate) fn round(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn round(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args .get(0) .map_or(f64::NAN, |x| f64::from(x).round()) @@ -369,7 +370,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.sign /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sign - pub(crate) fn sign(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn sign(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(if args.is_empty() { f64::NAN } else { @@ -391,7 +392,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.sin /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sin - pub(crate) fn sin(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn sin(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sin()).into()) } @@ -403,7 +404,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.sinh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sinh - pub(crate) fn sinh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn sinh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sinh()).into()) } @@ -415,11 +416,19 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.sqrt /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/sqrt - pub(crate) fn sqrt(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn sqrt(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).sqrt()).into()) } - /// Get the tangent of a number - pub(crate) fn tan(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + + /// Get the tangent of a number. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-math.tan + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tan + pub(crate) fn tan(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).tan()).into()) } @@ -431,7 +440,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.tanh /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/tanh - pub(crate) fn tanh(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn tanh(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args.get(0).map_or(f64::NAN, |x| f64::from(x).tanh()).into()) } @@ -443,7 +452,7 @@ impl Math { /// /// [spec]: https://tc39.es/ecma262/#sec-math.trunc /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/trunc - pub(crate) fn trunc(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn trunc(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { Ok(args .get(0) .map_or(f64::NAN, |x| f64::from(x).trunc()) diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index d46ba79f15..87514815cb 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -35,7 +35,7 @@ pub(crate) use self::{ string::String, symbol::Symbol, undefined::Undefined, - value::{ResultValue, Value, ValueData}, + value::{ResultValue, Value}, }; /// Initializes builtin objects and functions @@ -67,8 +67,8 @@ pub fn init(global: &Value) { Undefined::init, ]; - match global.data() { - ValueData::Object(ref global_object) => { + match global { + Value::Object(ref global_object) => { for init in &globals { let (name, value) = init(global); global_object.borrow_mut().insert_field(name, value); diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index c06b22b7c6..8252de5ca9 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -21,7 +21,7 @@ use super::{ object::ObjectData, }; use crate::{ - builtins::value::{ResultValue, Value, ValueData}, + builtins::value::{ResultValue, Value}, exec::Interpreter, BoaProfiler, }; @@ -57,10 +57,10 @@ impl Number { /// /// [spec]: https://tc39.es/ecma262/#sec-thisnumbervalue fn this_number_value(value: &Value, ctx: &mut Interpreter) -> Result { - match *value.data() { - ValueData::Integer(integer) => return Ok(f64::from(integer)), - ValueData::Rational(rational) => return Ok(rational), - ValueData::Object(ref object) => { + match *value { + Value::Integer(integer) => return Ok(f64::from(integer)), + Value::Rational(rational) => return Ok(rational), + Value::Object(ref object) => { if let Some(number) = object.borrow().as_number() { return Ok(number); } @@ -83,11 +83,7 @@ impl Number { /// `[[Construct]]` - Creates a Number instance /// /// `[[Call]]` - Creates a number primitive - pub(crate) fn make_number( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn make_number(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let data = match args.get(0) { Some(ref value) => ctx.to_numeric_number(value)?, None => 0.0, @@ -109,7 +105,7 @@ impl Number { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toExponential #[allow(clippy::wrong_self_convention)] pub(crate) fn to_exponential( - this: &mut Value, + this: &Value, _args: &[Value], ctx: &mut Interpreter, ) -> ResultValue { @@ -129,7 +125,7 @@ impl Number { /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tofixed /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toFixed #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_fixed(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn to_fixed(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_num = Self::this_number_value(this, ctx)?; let precision = match args.get(0) { Some(n) => match n.to_integer() { @@ -157,7 +153,7 @@ impl Number { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString #[allow(clippy::wrong_self_convention)] pub(crate) fn to_locale_string( - this: &mut Value, + this: &Value, _args: &[Value], ctx: &mut Interpreter, ) -> ResultValue { @@ -177,11 +173,7 @@ impl Number { /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.toexponential /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toPrecision #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_precision( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn to_precision(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_num = Self::this_number_value(this, ctx)?; let _num_str_len = format!("{}", this_num).len(); let _precision = match args.get(0) { @@ -345,11 +337,7 @@ impl Number { /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // 1. Let x be ? thisNumberValue(this value). let x = Self::this_number_value(this, ctx)?; @@ -402,11 +390,7 @@ impl Number { /// /// [spec]: https://tc39.es/ecma262/#sec-number.prototype.valueof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/valueOf - pub(crate) fn value_of( - this: &mut Value, - _args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn value_of(this: &Value, _args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(Value::from(Self::this_number_value(this, ctx)?)) } @@ -424,14 +408,10 @@ impl Number { /// /// [spec]: https://tc39.es/ecma262/#sec-parseint-string-radix /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseInt - pub(crate) fn parse_int( - _this: &mut Value, - args: &[Value], - _ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn parse_int(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { if let (Some(val), r) = (args.get(0), args.get(1)) { let mut radix = if let Some(rx) = r { - if let ValueData::Integer(i) = rx.data() { + if let Value::Integer(i) = rx { *i as u32 } else { // Handling a second argument that isn't an integer but was provided so cannot be defaulted. @@ -442,8 +422,8 @@ impl Number { 0 }; - match val.data() { - ValueData::String(s) => { + match val { + Value::String(s) => { // Attempt to infer radix from given string. if radix == 0 { @@ -466,8 +446,8 @@ impl Number { Ok(Value::from(f64::NAN)) } } - ValueData::Integer(i) => Ok(Value::integer(*i)), - ValueData::Rational(f) => Ok(Value::integer(*f as i32)), + Value::Integer(i) => Ok(Value::integer(*i)), + Value::Rational(f) => Ok(Value::integer(*f as i32)), _ => { // Wrong argument type to parseInt. Ok(Value::from(f64::NAN)) @@ -495,13 +475,13 @@ impl Number { /// [spec]: https://tc39.es/ecma262/#sec-parsefloat-string /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/parseFloat pub(crate) fn parse_float( - _this: &mut Value, + _this: &Value, args: &[Value], _ctx: &mut Interpreter, ) -> ResultValue { if let Some(val) = args.get(0) { - match val.data() { - ValueData::String(s) => { + match val { + Value::String(s) => { if let Ok(i) = s.parse::() { // Attempt to parse an integer first so that it can be stored as an integer // to improve performance @@ -513,8 +493,8 @@ impl Number { Ok(Value::from(f64::NAN)) } } - ValueData::Integer(i) => Ok(Value::integer(*i)), - ValueData::Rational(f) => Ok(Value::rational(*f)), + Value::Integer(i) => Ok(Value::integer(*i)), + Value::Rational(f) => Ok(Value::rational(*f)), _ => { // Wrong argument type to parseFloat. Ok(Value::from(f64::NAN)) diff --git a/boa/src/builtins/object/gcobject.rs b/boa/src/builtins/object/gcobject.rs new file mode 100644 index 0000000000..cf594e5705 --- /dev/null +++ b/boa/src/builtins/object/gcobject.rs @@ -0,0 +1,73 @@ +//! This module implements the `GcObject` structure. +//! +//! The `GcObject` is a garbage collected Object. + +use super::Object; +use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace}; +use std::fmt::{self, Display}; + +/// Garbage collected `Object`. +#[derive(Debug, Trace, Finalize, Clone)] +pub struct GcObject(Gc>); + +impl GcObject { + #[inline] + pub(crate) fn new(object: Object) -> Self { + Self(Gc::new(GcCell::new(object))) + } + + #[inline] + pub fn borrow(&self) -> GcCellRef<'_, Object> { + self.try_borrow().expect("Object already mutably borrowed") + } + + #[inline] + pub fn borrow_mut(&self) -> GcCellRefMut<'_, Object> { + self.try_borrow_mut().expect("Object already borrowed") + } + + #[inline] + pub fn try_borrow(&self) -> Result, BorrowError> { + self.0.try_borrow().map_err(|_| BorrowError) + } + + #[inline] + pub fn try_borrow_mut(&self) -> Result, BorrowMutError> { + self.0.try_borrow_mut().map_err(|_| BorrowMutError) + } + + /// Checks if the garbage collected memory is the same. + #[inline] + pub fn equals(lhs: &Self, rhs: &Self) -> bool { + std::ptr::eq(lhs.as_ref(), rhs.as_ref()) + } +} + +impl AsRef> for GcObject { + #[inline] + fn as_ref(&self) -> &GcCell { + &*self.0 + } +} + +/// An error returned by [`GcObject::try_borrow`](struct.GcObject.html#method.try_borrow). +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BorrowError; + +impl Display for BorrowError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt("Object already mutably borrowed", f) + } +} + +/// An error returned by [`GcObject::try_borrow_mut`](struct.GcObject.html#method.try_borrow_mut). +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct BorrowMutError; + +impl Display for BorrowMutError { + #[inline] + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt("Object already borrowed", f) + } +} diff --git a/boa/src/builtins/object/internal_methods.rs b/boa/src/builtins/object/internal_methods.rs index 996fddc90a..7b79c0c054 100644 --- a/boa/src/builtins/object/internal_methods.rs +++ b/boa/src/builtins/object/internal_methods.rs @@ -8,11 +8,9 @@ use crate::builtins::{ object::{Object, INSTANCE_PROTOTYPE, PROTOTYPE}, property::Property, - value::{same_value, Value, ValueData}, + value::{same_value, RcString, Value}, }; use crate::BoaProfiler; -use std::borrow::Borrow; -use std::ops::Deref; impl Object { /// Check if object has property. @@ -29,8 +27,8 @@ impl Object { if !parent.is_null() { // the parent value variant should be an object // In the unlikely event it isn't return false - return match *parent { - ValueData::Object(ref obj) => (*obj).deref().borrow().has_property(val), + return match parent { + Value::Object(ref obj) => obj.borrow().has_property(val), _ => false, }; } @@ -48,11 +46,7 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-isextensible #[inline] pub fn is_extensible(&self) -> bool { - let val = self.get_internal_slot("extensible"); - match *val.deref().borrow() { - ValueData::Boolean(b) => b, - _ => false, - } + self.extensible } /// Disable extensibility. @@ -63,7 +57,7 @@ impl Object { /// [spec]: https://tc39.es/ecma262/#sec-ordinary-object-internal-methods-and-internal-slots-preventextensions #[inline] pub fn prevent_extensions(&mut self) -> bool { - self.set_internal_slot("extensible", Value::from(false)); + self.extensible = false; true } @@ -241,7 +235,6 @@ impl Object { && !same_value( &desc.value.clone().unwrap(), ¤t.value.clone().unwrap(), - false, ) { return false; @@ -253,21 +246,13 @@ impl Object { } else { if !current.configurable.unwrap() { if desc.set.is_some() - && !same_value( - &desc.set.clone().unwrap(), - ¤t.set.clone().unwrap(), - false, - ) + && !same_value(&desc.set.clone().unwrap(), ¤t.set.clone().unwrap()) { return false; } if desc.get.is_some() - && !same_value( - &desc.get.clone().unwrap(), - ¤t.get.clone().unwrap(), - false, - ) + && !same_value(&desc.get.clone().unwrap(), ¤t.get.clone().unwrap()) { return false; } @@ -293,8 +278,8 @@ impl Object { debug_assert!(Property::is_property_key(prop)); // Prop could either be a String or Symbol - match *(*prop) { - ValueData::String(ref st) => { + match *prop { + Value::String(ref st) => { self.properties() .get(st) .map_or_else(Property::default, |v| { @@ -312,23 +297,24 @@ impl Object { d }) } - ValueData::Symbol(ref symbol) => self - .symbol_properties() - .get(&symbol.hash()) - .map_or_else(Property::default, |v| { - let mut d = Property::default(); - if v.is_data_descriptor() { - d.value = v.value.clone(); - d.writable = v.writable; - } else { - debug_assert!(v.is_accessor_descriptor()); - d.get = v.get.clone(); - d.set = v.set.clone(); - } - d.enumerable = v.enumerable; - d.configurable = v.configurable; - d - }), + Value::Symbol(ref symbol) => { + self.symbol_properties() + .get(&symbol.hash()) + .map_or_else(Property::default, |v| { + let mut d = Property::default(); + if v.is_data_descriptor() { + d.value = v.value.clone(); + d.writable = v.writable; + } else { + debug_assert!(v.is_accessor_descriptor()); + d.get = v.get.clone(); + d.set = v.set.clone(); + } + d.enumerable = v.enumerable; + d.configurable = v.configurable; + d + }) + } _ => Property::default(), } } @@ -347,11 +333,10 @@ impl Object { pub fn set_prototype_of(&mut self, val: Value) -> bool { debug_assert!(val.is_object() || val.is_null()); let current = self.get_internal_slot(PROTOTYPE); - if same_value(¤t, &val, false) { + if same_value(¤t, &val) { return true; } - let extensible = self.get_internal_slot("extensible"); - if extensible.is_null() { + if !self.is_extensible() { return false; } let mut p = val.clone(); @@ -359,7 +344,7 @@ impl Object { while !done { if p.is_null() { done = true - } else if same_value(&Value::from(self.clone()), &p, false) { + } else if same_value(&Value::from(self.clone()), &p) { return false; } else { p = p.get_internal_slot(PROTOTYPE); @@ -402,7 +387,7 @@ impl Object { #[inline] pub(crate) fn insert_property(&mut self, name: N, p: Property) where - N: Into, + N: Into, { self.properties.insert(name.into(), p); } @@ -420,7 +405,7 @@ impl Object { #[inline] pub(crate) fn insert_field(&mut self, name: N, value: Value) -> Option where - N: Into, + N: Into, { self.properties.insert( name.into(), diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 9ed8997bbe..58c2aeedaa 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -17,26 +17,26 @@ use crate::{ builtins::{ function::Function, property::Property, - value::{ResultValue, Value, ValueData}, - BigInt, Symbol, + value::{RcBigInt, RcString, RcSymbol, ResultValue, Value}, + BigInt, }, exec::Interpreter, BoaProfiler, }; use gc::{Finalize, Trace}; use rustc_hash::FxHashMap; -use std::{ - fmt::{Debug, Display, Error, Formatter}, - ops::Deref, -}; +use std::fmt::{Debug, Display, Error, Formatter}; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::builtins::value::same_value; pub use internal_state::{InternalState, InternalStateCell}; +pub mod gcobject; pub mod internal_methods; mod internal_state; +pub use gcobject::GcObject; + #[cfg(test)] mod tests; @@ -54,23 +54,25 @@ pub struct Object { /// Internal Slots internal_slots: FxHashMap, /// Properties - properties: FxHashMap, + properties: FxHashMap, /// Symbol Properties symbol_properties: FxHashMap, /// Some rust object that stores internal state state: Option, + /// Whether it can have new properties added to it. + extensible: bool, } /// Defines the different types of objects. #[derive(Debug, Trace, Finalize, Clone)] pub enum ObjectData { Array, - BigInt(BigInt), + BigInt(RcBigInt), Boolean(bool), Function(Function), - String(String), + String(RcString), Number(f64), - Symbol(Symbol), + Symbol(RcSymbol), Error, Ordinary, } @@ -99,16 +101,14 @@ impl Default for Object { /// Return a new ObjectData struct, with `kind` set to Ordinary #[inline] fn default() -> Self { - let mut object = Self { + Self { data: ObjectData::Ordinary, internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), state: None, - }; - - object.set_internal_slot("extensible", Value::from(true)); - object + extensible: true, + } } } @@ -122,16 +122,14 @@ impl Object { pub fn function(function: Function) -> Self { let _timer = BoaProfiler::global().start_event("Object::Function", "object"); - let mut object = Self { + Self { data: ObjectData::Function(function), internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), state: None, - }; - - object.set_internal_slot("extensible", Value::from(true)); - object + extensible: true, + } } /// ObjectCreate is used to specify the runtime creation of new ordinary objects. @@ -145,8 +143,6 @@ impl Object { let mut obj = Self::default(); obj.internal_slots .insert(INSTANCE_PROTOTYPE.to_string(), proto); - obj.internal_slots - .insert("extensible".to_string(), Value::from(true)); obj } @@ -158,6 +154,7 @@ impl Object { properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), state: None, + extensible: true, } } @@ -169,28 +166,34 @@ impl Object { properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), state: None, + extensible: true, } } /// Return a new `String` object whose `[[StringData]]` internal slot is set to argument. - pub fn string(value: String) -> Self { + pub fn string(value: S) -> Self + where + S: Into, + { Self { - data: ObjectData::String(value), + data: ObjectData::String(value.into()), internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), state: None, + extensible: true, } } /// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument. - pub fn bigint(value: BigInt) -> Self { + pub fn bigint(value: RcBigInt) -> Self { Self { data: ObjectData::BigInt(value), internal_slots: FxHashMap::default(), properties: FxHashMap::default(), symbol_properties: FxHashMap::default(), state: None, + extensible: true, } } @@ -201,13 +204,13 @@ impl Object { /// /// [spec]: https://tc39.es/ecma262/#sec-toobject pub fn from(value: &Value) -> Result { - match *value.data() { - ValueData::Boolean(a) => Ok(Self::boolean(a)), - ValueData::Rational(a) => Ok(Self::number(a)), - ValueData::Integer(a) => Ok(Self::number(f64::from(a))), - ValueData::String(ref a) => Ok(Self::string(a.clone())), - ValueData::BigInt(ref bigint) => Ok(Self::bigint(bigint.clone())), - ValueData::Object(ref obj) => Ok((*obj).deref().borrow().clone()), + match *value { + Value::Boolean(a) => Ok(Self::boolean(a)), + Value::Rational(a) => Ok(Self::number(a)), + Value::Integer(a) => Ok(Self::number(f64::from(a))), + Value::String(ref a) => Ok(Self::string(a.clone())), + Value::BigInt(ref bigint) => Ok(Self::bigint(bigint.clone())), + Value::Object(ref obj) => Ok(obj.borrow().clone()), _ => Err(()), } } @@ -255,9 +258,9 @@ impl Object { } #[inline] - pub fn as_string(&self) -> Option<&str> { + pub fn as_string(&self) -> Option { match self.data { - ObjectData::String(ref string) => Some(string.as_str()), + ObjectData::String(ref string) => Some(string.clone()), _ => None, } } @@ -283,9 +286,9 @@ impl Object { } #[inline] - pub fn as_symbol(&self) -> Option<&Symbol> { + pub fn as_symbol(&self) -> Option { match self.data { - ObjectData::Symbol(ref symbol) => Some(symbol), + ObjectData::Symbol(ref symbol) => Some(symbol.clone()), _ => None, } } @@ -363,12 +366,12 @@ impl Object { } #[inline] - pub fn properties(&self) -> &FxHashMap { + pub fn properties(&self) -> &FxHashMap { &self.properties } #[inline] - pub fn properties_mut(&mut self) -> &mut FxHashMap { + pub fn properties_mut(&mut self) -> &mut FxHashMap { &mut self.properties } @@ -394,7 +397,7 @@ impl Object { } /// Create a new object. -pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn make_object(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(arg) = args.get(0) { if !arg.is_null_or_undefined() { return Ok(Value::object(Object::from(arg).unwrap())); @@ -408,23 +411,21 @@ pub fn make_object(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> Resu } /// Uses the SameValue algorithm to check equality of objects -pub fn is(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn is(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let x = args.get(0).cloned().unwrap_or_else(Value::undefined); let y = args.get(1).cloned().unwrap_or_else(Value::undefined); - let result = same_value(&x, &y, false); - - Ok(Value::boolean(result)) + Ok(same_value(&x, &y).into()) } /// Get the `prototype` of an object. -pub fn get_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn get_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object"); Ok(obj.get_field(INSTANCE_PROTOTYPE)) } /// Set the `prototype` of an object. -pub fn set_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object").clone(); let proto = args.get(1).expect("Cannot get object").clone(); obj.set_internal_slot(INSTANCE_PROTOTYPE, proto); @@ -432,7 +433,7 @@ pub fn set_prototype_of(_: &mut Value, args: &[Value], _: &mut Interpreter) -> R } /// Define a property in an object -pub fn define_property(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn define_property(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let obj = args.get(0).expect("Cannot get object"); let prop = ctx.to_string(args.get(1).expect("Cannot get object"))?; let desc = Property::from(args.get(2).expect("Cannot get object")); @@ -450,7 +451,7 @@ pub fn define_property(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> /// /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/toString -pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { +pub fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { Ok(Value::from(this.to_string())) } @@ -465,7 +466,7 @@ pub fn to_string(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultVa /// /// [spec]: https://tc39.es/ecma262/#sec-object.prototype.hasownproperty /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/hasOwnProperty -pub fn has_own_property(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let prop = if args.is_empty() { None } else { @@ -475,7 +476,7 @@ pub fn has_own_property(this: &mut Value, args: &[Value], ctx: &mut Interpreter) .as_object() .as_deref() .expect("Cannot get THIS object") - .get_own_property(&Value::string(&prop.expect("cannot get prop"))); + .get_own_property(&Value::string(prop.expect("cannot get prop"))); if own_property.is_none() { Ok(Value::from(false)) } else { @@ -483,25 +484,22 @@ pub fn has_own_property(this: &mut Value, args: &[Value], ctx: &mut Interpreter) } } -pub fn property_is_enumerable( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, -) -> ResultValue { +pub fn property_is_enumerable(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let key = match args.get(0) { None => return Ok(Value::from(false)), Some(key) => key, }; - let property_key = ctx.to_property_key(&mut key.clone())?; + let property_key = ctx.to_property_key(key)?; let own_property = ctx.to_object(this).map(|obj| { obj.as_object() .expect("Unable to deref object") .get_own_property(&property_key) }); - own_property.map_or(Ok(Value::from(false)), |own_prop| { - Ok(Value::from(own_prop.enumerable.unwrap_or(false))) - }) + + Ok(own_property.map_or(Value::from(false), |own_prop| { + Value::from(own_prop.enumerable.unwrap_or(false)) + })) } /// Create a new `Object` object. diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index c697045778..fb6eca2e7b 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -9,8 +9,6 @@ //! [spec]: https://tc39.es/ecma262/#sec-regexp-constructor //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp -use std::ops::Deref; - use regex::Regex; use super::function::{make_builtin_fn, make_constructor_fn}; @@ -18,7 +16,7 @@ use crate::{ builtins::{ object::{InternalState, ObjectData}, property::Property, - value::{ResultValue, Value, ValueData}, + value::{RcString, ResultValue, Value}, }, exec::Interpreter, BoaProfiler, @@ -68,45 +66,33 @@ impl RegExp { pub(crate) const LENGTH: usize = 2; /// Create a new `RegExp` - pub(crate) fn make_regexp( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { - if args.is_empty() { - return Err(Value::undefined()); - } + pub(crate) fn make_regexp(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + let arg = args.get(0).ok_or_else(Value::undefined)?; let mut regex_body = String::new(); let mut regex_flags = String::new(); - #[allow(clippy::indexing_slicing)] // length has been checked - match args[0].deref() { - ValueData::String(ref body) => { + match arg { + Value::String(ref body) => { // first argument is a string -> use it as regex pattern - regex_body = body.into(); + regex_body = body.to_string(); } - ValueData::Object(ref obj) => { + Value::Object(ref obj) => { let obj = obj.borrow(); let slots = obj.internal_slots(); if slots.get("RegExpMatcher").is_some() { // first argument is another `RegExp` object, so copy its pattern and flags if let Some(body) = slots.get("OriginalSource") { - regex_body = ctx.to_string(body)?; + regex_body = ctx.to_string(body)?.to_string(); } if let Some(flags) = slots.get("OriginalFlags") { - regex_flags = ctx.to_string(flags)?; + regex_flags = ctx.to_string(flags)?.to_string(); } } } _ => return Err(Value::undefined()), } // if a second argument is given and it's a string, use it as flags - match args.get(1) { - None => {} - Some(flags) => { - if let ValueData::String(flags) = flags.deref() { - regex_flags = flags.into(); - } - } + if let Some(Value::String(flags)) = args.get(1) { + regex_flags = flags.to_string(); } // parse flags @@ -186,7 +172,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.dotAll /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/dotAll - fn get_dot_all(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_dot_all(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.dot_all))) } @@ -201,7 +187,7 @@ impl RegExp { /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.flags /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/flags /// [flags]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions#Advanced_searching_with_flags_2 - fn get_flags(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_flags(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.flags.clone()))) } @@ -215,7 +201,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.global /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/global - fn get_global(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_global(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.global))) } @@ -229,7 +215,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.ignorecase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/ignoreCase - fn get_ignore_case(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_ignore_case(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.ignore_case))) } @@ -243,7 +229,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.multiline /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/multiline - fn get_multiline(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_multiline(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.multiline))) } @@ -258,7 +244,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.source /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/source - fn get_source(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_source(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { Ok(this.get_internal_slot("OriginalSource")) } @@ -272,7 +258,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.sticky /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/sticky - fn get_sticky(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_sticky(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.sticky))) } @@ -287,7 +273,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-get-regexp.prototype.unicode /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/unicode - fn get_unicode(this: &mut Value, _: &[Value], _: &mut Interpreter) -> ResultValue { + fn get_unicode(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { this.with_internal_state_ref(|regex: &RegExp| Ok(Value::from(regex.unicode))) } @@ -303,7 +289,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.test /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test - pub(crate) fn test(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn test(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; let mut last_index = usize::from(&this.get_field("lastIndex")); let result = this.with_internal_state_ref(|regex: &RegExp| { @@ -336,7 +322,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.exec /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec - pub(crate) fn exec(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn exec(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; let mut last_index = usize::from(&this.get_field("lastIndex")); let result = this.with_internal_state_ref(|regex: &RegExp| { @@ -386,7 +372,7 @@ impl RegExp { /// /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype-@@match /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@match - pub(crate) fn r#match(this: &mut Value, arg: String, ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn r#match(this: &Value, arg: RcString, ctx: &mut Interpreter) -> ResultValue { let (matcher, flags) = this .with_internal_state_ref(|regex: &RegExp| (regex.matcher.clone(), regex.flags.clone())); if flags.contains('g') { @@ -414,7 +400,7 @@ impl RegExp { /// [spec]: https://tc39.es/ecma262/#sec-regexp.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let body = ctx.to_string(&this.get_internal_slot("OriginalSource"))?; let flags = this.with_internal_state_ref(|regex: &RegExp| regex.flags.clone()); Ok(Value::from(format!("/{}/{}", body, flags))) @@ -431,7 +417,7 @@ impl RegExp { /// [spec]: https://tc39.es/ecma262/#sec-regexp-prototype-matchall /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/@@matchAll // TODO: it's returning an array, it should return an iterator - pub(crate) fn match_all(this: &mut Value, arg_str: String) -> ResultValue { + pub(crate) fn match_all(this: &Value, arg_str: String) -> ResultValue { let matches: Vec = this.with_internal_state_ref(|regex: &RegExp| { let mut matches = Vec::new(); diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index d9abe9ae68..de5f046352 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -17,7 +17,7 @@ use crate::{ builtins::{ object::{Object, ObjectData}, property::Property, - value::{ResultValue, Value, ValueData}, + value::{RcString, ResultValue, Value}, RegExp, }, exec::Interpreter, @@ -28,7 +28,6 @@ use std::string::String as StdString; use std::{ cmp::{max, min}, f64::NAN, - ops::Deref, }; /// JavaScript `String` implementation. @@ -42,13 +41,13 @@ impl String { /// The amount of arguments this function object takes. pub(crate) const LENGTH: usize = 1; - fn this_string_value(this: &Value, ctx: &mut Interpreter) -> Result { - match this.data() { - ValueData::String(ref string) => return Ok(string.clone()), - ValueData::Object(ref object) => { + fn this_string_value(this: &Value, ctx: &mut Interpreter) -> Result { + match this { + Value::String(ref string) => return Ok(string.clone()), + Value::Object(ref object) => { let object = object.borrow(); if let Some(string) = object.as_string() { - return Ok(string.to_owned()); + return Ok(string); } } _ => {} @@ -61,16 +60,12 @@ impl String { /// /// [[Call]] - Returns a new native `string` /// - pub(crate) fn make_string( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn make_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // This value is used by console.log and other routines to match Obexpecty"failed to parse argument for String method"pe // to its Javascript Identifier (global constructor method name) let string = match args.get(0) { Some(ref value) => ctx.to_string(value)?, - None => StdString::new(), + None => RcString::default(), }; let length = string.chars().count(); @@ -85,7 +80,7 @@ impl String { /// Get the string value to a primitive string #[allow(clippy::wrong_self_convention)] #[inline] - pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { // Get String from String Object and send it back as a new value Ok(Value::from(Self::this_string_value(this, ctx)?)) } @@ -106,7 +101,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charAt - pub(crate) fn char_at(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn char_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -148,11 +143,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.charcodeat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/charCodeAt - pub(crate) fn char_code_at( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn char_code_at(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -192,11 +183,11 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.concat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat - pub(crate) fn concat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let object = ctx.require_object_coercible(this)?; - let mut string = ctx.to_string(object)?; + let mut string = ctx.to_string(object)?.to_string(); for arg in args { string.push_str(&ctx.to_string(arg)?); @@ -216,7 +207,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.repeat /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat - pub(crate) fn repeat(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -239,7 +230,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.slice /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/slice - pub(crate) fn slice(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn slice(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -286,11 +277,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.startswith /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith - pub(crate) fn starts_with( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn starts_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -319,7 +306,7 @@ impl String { } else { // Only use the part of the string from "start" let this_string: StdString = primitive_val.chars().skip(start as usize).collect(); - Ok(Value::from(this_string.starts_with(&search_string))) + Ok(Value::from(this_string.starts_with(search_string.as_str()))) } } @@ -333,11 +320,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.endswith /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith - pub(crate) fn ends_with( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn ends_with(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -367,7 +350,7 @@ impl String { } else { // Only use the part of the string up to "end" let this_string: StdString = primitive_val.chars().take(end as usize).collect(); - Ok(Value::from(this_string.ends_with(&search_string))) + Ok(Value::from(this_string.ends_with(search_string.as_str()))) } } @@ -381,7 +364,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.includes /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes - pub(crate) fn includes(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn includes(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -406,14 +389,14 @@ impl String { // Take the string from "this" and use only the part of it after "start" let this_string: StdString = primitive_val.chars().skip(start as usize).collect(); - Ok(Value::from(this_string.contains(&search_string))) + Ok(Value::from(this_string.contains(search_string.as_str()))) } /// Return either the string itself or the string of the regex equivalent fn get_regex_string(value: &Value) -> StdString { - match value.deref() { - ValueData::String(ref body) => body.into(), - ValueData::Object(ref obj) => { + match value { + Value::String(ref body) => body.to_string(), + Value::Object(ref obj) => { let obj = obj.borrow(); if obj.internal_slots().get("RegExpMatcher").is_some() { @@ -443,7 +426,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.replace /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace - pub(crate) fn replace(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // TODO: Support Symbol replacer let primitive_val = ctx.to_string(this)?; if args.is_empty() { @@ -460,8 +443,8 @@ impl String { let replace_value = if args.len() > 1 { // replace_object could be a string or function or not exist at all let replace_object: &Value = args.get(1).expect("second argument expected"); - match replace_object.deref() { - ValueData::String(val) => { + match replace_object { + Value::String(val) => { // https://tc39.es/ecma262/#table-45 let mut result = val.to_string(); let re = Regex::new(r"\$(\d)").unwrap(); @@ -500,7 +483,7 @@ impl String { result } - ValueData::Object(_) => { + Value::Object(_) => { // This will return the matched substring first, then captured parenthesized groups later let mut results: Vec = caps .iter() @@ -518,7 +501,7 @@ impl String { let result = ctx.call(&replace_object, this, &results).unwrap(); - ctx.to_string(&result)? + ctx.to_string(&result)?.to_string() } _ => "undefined".to_string(), } @@ -545,7 +528,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.indexof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf - pub(crate) fn index_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -573,7 +556,7 @@ impl String { // checking "starts with" the search string for index in start..length { let this_string: StdString = primitive_val.chars().skip(index as usize).collect(); - if this_string.starts_with(&search_string) { + if this_string.starts_with(search_string.as_str()) { // Explicitly return early with the index value return Ok(Value::from(index)); } @@ -595,7 +578,7 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.lastindexof /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/lastIndexOf pub(crate) fn last_index_of( - this: &mut Value, + this: &Value, args: &[Value], ctx: &mut Interpreter, ) -> ResultValue { @@ -627,7 +610,7 @@ impl String { let mut highest_index = -1; for index in start..length { let this_string: StdString = primitive_val.chars().skip(index as usize).collect(); - if this_string.starts_with(&search_string) { + if this_string.starts_with(search_string.as_str()) { highest_index = index; } } @@ -647,10 +630,9 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.match /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions - pub(crate) fn r#match(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { - let mut re = - RegExp::make_regexp(&mut Value::from(Object::default()), &[args[0].clone()], ctx)?; - RegExp::r#match(&mut re, ctx.to_string(this)?, ctx) + pub(crate) fn r#match(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + let re = RegExp::make_regexp(&Value::from(Object::default()), &[args[0].clone()], ctx)?; + RegExp::r#match(&re, ctx.to_string(this)?, ctx) } /// Abstract method `StringPad`. @@ -658,9 +640,9 @@ impl String { /// Performs the actual string padding for padStart/End. /// fn string_pad( - primitive: StdString, + primitive: RcString, max_length: i32, - fill_string: Option, + fill_string: Option, at_start: bool, ) -> ResultValue { let primitive_length = primitive.len() as i32; @@ -669,20 +651,13 @@ impl String { return Ok(Value::from(primitive)); } - let filler = match fill_string { - Some(filler) => filler, - None => " ".to_owned(), - }; - - if filler == "" { - return Ok(Value::from(primitive)); - } + let filter = fill_string.as_deref().unwrap_or(" "); let fill_len = max_length.wrapping_sub(primitive_length); let mut fill_str = StdString::new(); while fill_str.len() < fill_len as usize { - fill_str.push_str(&filler); + fill_str.push_str(filter); } // Cut to size max_length let concat_fill_str: StdString = fill_str.chars().take(fill_len as usize).collect(); @@ -706,8 +681,8 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padend /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padEnd - pub(crate) fn pad_end(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { - let primitive_val = ctx.to_string(this)?; + pub(crate) fn pad_end(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + let primitive = ctx.to_string(this)?; if args.is_empty() { return Err(Value::from("padEnd requires maxLength argument")); } @@ -716,13 +691,9 @@ impl String { .expect("failed to get argument for String method"), ); - let fill_string = if args.len() != 1 { - Some(ctx.to_string(args.get(1).expect("Could not get argument"))?) - } else { - None - }; + let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?; - Self::string_pad(primitive_val, max_length, fill_string, false) + Self::string_pad(primitive, max_length, fill_string, false) } /// `String.prototype.padStart( targetLength [, padString] )` @@ -737,12 +708,8 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.padstart /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/padStart - pub(crate) fn pad_start( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { - let primitive_val = ctx.to_string(this)?; + pub(crate) fn pad_start(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + let primitive = ctx.to_string(this)?; if args.is_empty() { return Err(Value::from("padStart requires maxLength argument")); } @@ -751,12 +718,9 @@ impl String { .expect("failed to get argument for String method"), ); - let fill_string = match args.len() { - 1 => None, - _ => Some(ctx.to_string(args.get(1).expect("Could not get argument"))?), - }; + let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?; - Self::string_pad(primitive_val, max_length, fill_string, true) + Self::string_pad(primitive, max_length, fill_string, true) } /// Helper function to check if a `char` is trimmable. @@ -790,7 +754,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trim /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim - pub(crate) fn trim(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn trim(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str = ctx.to_string(this)?; Ok(Value::from( this_str.trim_matches(Self::is_trimmable_whitespace), @@ -809,7 +773,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimstart /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart - pub(crate) fn trim_start(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn trim_start(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str = ctx.to_string(this)?; Ok(Value::from( this_str.trim_start_matches(Self::is_trimmable_whitespace), @@ -828,7 +792,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.trimend /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd - pub(crate) fn trim_end(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn trim_end(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str = ctx.to_string(this)?; Ok(Value::from( this_str.trim_end_matches(Self::is_trimmable_whitespace), @@ -846,11 +810,7 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.tolowercase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toLowerCase #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_lowercase( - this: &mut Value, - _: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn to_lowercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let this_str = ctx.to_string(this)?; @@ -872,11 +832,7 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.toUppercase /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/toUpperCase #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_uppercase( - this: &mut Value, - _: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn to_uppercase(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let this_str = ctx.to_string(this)?; @@ -895,11 +851,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substring - pub(crate) fn substring( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { + pub(crate) fn substring(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -947,7 +899,7 @@ impl String { /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.substr /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/substr /// - pub(crate) fn substr(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn substr(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // First we get it the actual string a private field stored on the object only the engine has access to. // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = ctx.to_string(this)?; @@ -1002,7 +954,7 @@ impl String { /// /// [spec]: https://tc39.es/ecma262/#sec-string.prototype.value_of /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/valueOf - pub(crate) fn value_of(this: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn value_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // Use the to_string method because it is specified to do the same thing in this case Self::to_string(this, args, ctx) } @@ -1020,22 +972,18 @@ impl String { /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions /// [cg]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions/Groups_and_Ranges // TODO: update this method to return iterator - pub(crate) fn match_all( - this: &mut Value, - args: &[Value], - ctx: &mut Interpreter, - ) -> ResultValue { - let mut re: Value = match args.get(0) { + pub(crate) fn match_all(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + let re: Value = match args.get(0) { Some(arg) => { if arg.is_null() { RegExp::make_regexp( - &mut Value::from(Object::default()), + &Value::from(Object::default()), &[Value::from(ctx.to_string(arg)?), Value::from("g")], ctx, ) } else if arg.is_undefined() { RegExp::make_regexp( - &mut Value::from(Object::default()), + &Value::from(Object::default()), &[Value::undefined(), Value::from("g")], ctx, ) @@ -1044,13 +992,13 @@ impl String { } } None => RegExp::make_regexp( - &mut Value::from(Object::default()), + &Value::from(Object::default()), &[Value::from(""), Value::from("g")], ctx, ), }?; - RegExp::match_all(&mut re, ctx.to_string(this)?) + RegExp::match_all(&re, ctx.to_string(this)?.to_string()) } /// Create a new `String` object. diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index b764199cfc..a82a0a530c 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -20,14 +20,14 @@ mod tests; use super::function::{make_builtin_fn, make_constructor_fn}; use crate::{ - builtins::value::{ResultValue, Value, ValueData}, + builtins::value::{RcString, RcSymbol, ResultValue, Value}, exec::Interpreter, BoaProfiler, }; use gc::{Finalize, Trace}; #[derive(Debug, Finalize, Trace, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Symbol(Option>, u32); +pub struct Symbol(Option, u32); impl Symbol { /// The name of the object. @@ -46,13 +46,13 @@ impl Symbol { self.1 } - fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result { - match value.data() { - ValueData::Symbol(ref symbol) => return Ok(symbol.clone()), - ValueData::Object(ref object) => { + fn this_symbol_value(value: &Value, ctx: &mut Interpreter) -> Result { + match value { + Value::Symbol(ref symbol) => return Ok(symbol.clone()), + Value::Object(ref object) => { let object = object.borrow(); if let Some(symbol) = object.as_symbol() { - return Ok(symbol.clone()); + return Ok(symbol); } } _ => {} @@ -72,11 +72,9 @@ impl Symbol { /// /// [spec]: https://tc39.es/ecma262/#sec-symbol-description /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol - pub(crate) fn call(_: &mut Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn call(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let description = match args.get(0) { - Some(ref value) if !value.is_undefined() => { - Some(ctx.to_string(value)?.into_boxed_str()) - } + Some(ref value) if !value.is_undefined() => Some(ctx.to_string(value)?), _ => None, }; @@ -94,7 +92,7 @@ impl Symbol { /// [spec]: https://tc39.es/ecma262/#sec-symbol.prototype.tostring /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toString #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_string(this: &mut Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { + pub(crate) fn to_string(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let symbol = Self::this_symbol_value(this, ctx)?; let description = symbol.description().unwrap_or(""); Ok(Value::from(format!("Symbol({})", description))) diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 36392f204a..72937a1b11 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -16,7 +16,7 @@ impl From for Value { impl From> for Value { fn from(value: Box) -> Self { - Self::string(value) + Self::string(String::from(value)) } } @@ -38,6 +38,12 @@ impl From for Value { } } +impl From for Value { + fn from(value: RcString) -> Self { + Value::String(value) + } +} + #[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] pub struct TryFromCharError; @@ -89,6 +95,12 @@ impl From for Value { } } +impl From for Value { + fn from(value: RcBigInt) -> Self { + Value::BigInt(value) + } +} + impl From for Value { fn from(value: usize) -> Value { Value::integer(value as i32) @@ -120,7 +132,7 @@ where let mut array = Object::default(); for (i, item) in value.iter().enumerate() { array.properties_mut().insert( - i.to_string(), + RcString::from(i.to_string()), Property::default().value(item.clone().into()), ); } @@ -135,9 +147,10 @@ where fn from(value: Vec) -> Self { let mut array = Object::default(); for (i, item) in value.into_iter().enumerate() { - array - .properties_mut() - .insert(i.to_string(), Property::default().value(item.into())); + array.properties_mut().insert( + RcString::from(i.to_string()), + Property::default().value(item.into()), + ); } Value::from(array) } @@ -159,17 +172,6 @@ impl Display for TryFromObjectError { } } -impl TryFrom<&Value> for Object { - type Error = TryFromObjectError; - - fn try_from(value: &Value) -> Result { - match value.data() { - ValueData::Object(ref object) => Ok(object.clone().into_inner()), - _ => Err(TryFromObjectError), - } - } -} - impl From<()> for Value { fn from(_: ()) -> Self { Value::null() diff --git a/boa/src/builtins/value/display.rs b/boa/src/builtins/value/display.rs index fef0925192..93d858295f 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -1,11 +1,5 @@ use super::*; -impl Display for Value { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - Display::fmt(&self.data(), f) - } -} - /// A helper macro for printing objects /// Can be used to print both properties and internal slots /// All of the overloads take: @@ -64,10 +58,10 @@ macro_rules! print_obj_value { }; } -pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String { +pub(crate) fn log_string_from(x: &Value, print_internals: bool) -> String { match x { // We don't want to print private (compiler) or prototype properties - ValueData::Object(ref v) => { + Value::Object(ref v) => { // Can use the private "type" field of an Object to match on // which type of Object it represents for special printing match v.borrow().data { @@ -95,7 +89,7 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String { log_string_from( &v.borrow() .properties() - .get(&i.to_string()) + .get(i.to_string().as_str()) .unwrap() .value .clone() @@ -111,16 +105,13 @@ pub(crate) fn log_string_from(x: &ValueData, print_internals: bool) -> String { _ => display_obj(&x, print_internals), } } - ValueData::Symbol(ref symbol) => match symbol.description() { - Some(ref desc) => format!("Symbol({})", desc), - None => String::from("Symbol()"), - }, + Value::Symbol(ref symbol) => symbol.to_string(), _ => format!("{}", x), } } /// A helper function for specifically printing object values -pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { +pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String { // A simple helper for getting the address of a value // TODO: Find a more general place for this, as it can be used in other situations as well fn address_of(t: &T) -> usize { @@ -133,12 +124,12 @@ pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { let mut encounters = HashSet::new(); fn display_obj_internal( - data: &ValueData, + data: &Value, encounters: &mut HashSet, indent: usize, print_internals: bool, ) -> String { - if let ValueData::Object(ref v) = *data { + if let Value::Object(ref v) = *data { // The in-memory address of the current object let addr = address_of(v.borrow().deref()); @@ -175,7 +166,7 @@ pub(crate) fn display_obj(v: &ValueData, print_internals: bool) -> String { display_obj_internal(v, &mut encounters, 4, print_internals) } -impl Display for ValueData { +impl Display for Value { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Self::Null => write!(f, "null"), diff --git a/boa/src/builtins/value/equality.rs b/boa/src/builtins/value/equality.rs index 8e0254dacd..9ea75e6000 100644 --- a/boa/src/builtins/value/equality.rs +++ b/boa/src/builtins/value/equality.rs @@ -14,20 +14,20 @@ impl Value { return false; } - match (self.data(), other.data()) { + match (self, other) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::equal(x, y). - (ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::equal(x, y), - (ValueData::Rational(x), ValueData::Rational(y)) => Number::equal(*x, *y), - (ValueData::Rational(x), ValueData::Integer(y)) => Number::equal(*x, f64::from(*y)), - (ValueData::Integer(x), ValueData::Rational(y)) => Number::equal(f64::from(*x), *y), - (ValueData::Integer(x), ValueData::Integer(y)) => x == y, + (Self::BigInt(x), Self::BigInt(y)) => BigInt::equal(x, y), + (Self::Rational(x), Self::Rational(y)) => Number::equal(*x, *y), + (Self::Rational(x), Self::Integer(y)) => Number::equal(*x, f64::from(*y)), + (Self::Integer(x), Self::Rational(y)) => Number::equal(f64::from(*x), *y), + (Self::Integer(x), Self::Integer(y)) => x == y, //Null has to be handled specially because "typeof null" returns object and if we managed //this without a special case we would compare self and other as if they were actually //objects which unfortunately fails //Specification Link: https://tc39.es/ecma262/#sec-typeof-operator - (ValueData::Null, ValueData::Null) => true, + (Self::Null, Self::Null) => true, // 3. Return ! SameValueNonNumeric(x, y). (_, _) => same_value_non_numeric(self, other), @@ -39,28 +39,28 @@ impl Value { /// This method is executed when doing abstract equality comparisons with the `==` operator. /// For more information, check #[allow(clippy::float_cmp)] - pub fn equals(&mut self, other: &mut Self, interpreter: &mut Interpreter) -> bool { + pub fn equals(&self, other: &Self, interpreter: &mut Interpreter) -> Result { // 1. If Type(x) is the same as Type(y), then // a. Return the result of performing Strict Equality Comparison x === y. if self.get_type() == other.get_type() { - return self.strict_equals(other); + return Ok(self.strict_equals(other)); } - match (self.data(), other.data()) { + Ok(match (self, other) { // 2. If x is null and y is undefined, return true. // 3. If x is undefined and y is null, return true. - _ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, + (Self::Null, Self::Undefined) | (Self::Undefined, Self::Null) => true, // 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y). // 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y. // // https://github.com/rust-lang/rust/issues/54883 - (ValueData::Integer(_), ValueData::String(_)) - | (ValueData::Rational(_), ValueData::String(_)) - | (ValueData::String(_), ValueData::Integer(_)) - | (ValueData::String(_), ValueData::Rational(_)) - | (ValueData::Rational(_), ValueData::Boolean(_)) - | (ValueData::Integer(_), ValueData::Boolean(_)) => { + (Self::Integer(_), Self::String(_)) + | (Self::Rational(_), Self::String(_)) + | (Self::String(_), Self::Integer(_)) + | (Self::String(_), Self::Rational(_)) + | (Self::Rational(_), Self::Boolean(_)) + | (Self::Integer(_), Self::Boolean(_)) => { let a: &Value = self.borrow(); let b: &Value = other.borrow(); Number::equal(f64::from(a), f64::from(b)) @@ -70,52 +70,52 @@ impl Value { // a. Let n be ! StringToBigInt(y). // b. If n is NaN, return false. // c. Return the result of the comparison x == n. - (ValueData::BigInt(ref a), ValueData::String(ref b)) => match string_to_bigint(b) { - Some(ref b) => a == b, + (Self::BigInt(ref a), Self::String(ref b)) => match string_to_bigint(b) { + Some(ref b) => a.as_inner() == b, None => false, }, // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. - (ValueData::String(ref a), ValueData::BigInt(ref b)) => match string_to_bigint(a) { - Some(ref a) => a == b, + (Self::String(ref a), Self::BigInt(ref b)) => match string_to_bigint(a) { + Some(ref a) => a == b.as_inner(), None => false, }, // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. - (ValueData::Boolean(_), _) => { - other.equals(&mut Value::from(self.to_integer()), interpreter) + (Self::Boolean(_), _) => { + return other.equals(&Value::from(self.to_integer()), interpreter) } // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y). - (_, ValueData::Boolean(_)) => { - self.equals(&mut Value::from(other.to_integer()), interpreter) + (_, Self::Boolean(_)) => { + return self.equals(&Value::from(other.to_integer()), interpreter) } // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result // of the comparison x == ? ToPrimitive(y). - (ValueData::Object(_), _) => { - let mut primitive = interpreter.to_primitive(self, PreferredType::Default); - primitive.equals(other, interpreter) + (Self::Object(_), _) => { + let primitive = interpreter.to_primitive(self, PreferredType::Default)?; + return primitive.equals(other, interpreter); } // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result // of the comparison ? ToPrimitive(x) == y. - (_, ValueData::Object(_)) => { - let mut primitive = interpreter.to_primitive(other, PreferredType::Default); - primitive.equals(self, interpreter) + (_, Self::Object(_)) => { + let primitive = interpreter.to_primitive(other, PreferredType::Default)?; + return primitive.equals(self, interpreter); } // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then // a. If x or y are any of NaN, +∞, or -∞, return false. // b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false. - (ValueData::BigInt(ref a), ValueData::Rational(ref b)) => a == b, - (ValueData::Rational(ref a), ValueData::BigInt(ref b)) => a == b, - (ValueData::BigInt(ref a), ValueData::Integer(ref b)) => a == b, - (ValueData::Integer(ref a), ValueData::BigInt(ref b)) => a == b, + (Self::BigInt(ref a), Self::Rational(ref b)) => a.as_inner() == b, + (Self::Rational(ref a), Self::BigInt(ref b)) => a == b.as_inner(), + (Self::BigInt(ref a), Self::Integer(ref b)) => a.as_inner() == b, + (Self::Integer(ref a), Self::BigInt(ref b)) => a == b.as_inner(), // 13. Return false. _ => false, - } + }) } } @@ -137,60 +137,54 @@ pub fn string_to_bigint(string: &str) -> Option { /// The internal comparison abstract operation SameValue(x, y), /// where x and y are ECMAScript language values, produces true or false. -/// Such a comparison is performed as follows: /// -/// https://tc39.es/ecma262/#sec-samevalue -/// strict mode currently compares the pointers -pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { - if strict { - // Do both Values point to the same underlying valueData? - return std::ptr::eq(x.data(), y.data()); - } - +/// More information: +/// - [ECMAScript][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-samevalue +pub fn same_value(x: &Value, y: &Value) -> bool { // 1. If Type(x) is different from Type(y), return false. if x.get_type() != y.get_type() { return false; } - match (x.data(), y.data()) { + match (x, y) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::SameValue(x, y). - (ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::same_value(x, y), - (ValueData::Rational(x), ValueData::Rational(y)) => Number::same_value(*x, *y), - (ValueData::Rational(x), ValueData::Integer(y)) => Number::same_value(*x, f64::from(*y)), - (ValueData::Integer(x), ValueData::Rational(y)) => Number::same_value(f64::from(*x), *y), - (ValueData::Integer(x), ValueData::Integer(y)) => x == y, + (Value::BigInt(x), Value::BigInt(y)) => BigInt::same_value(x, y), + (Value::Rational(x), Value::Rational(y)) => Number::same_value(*x, *y), + (Value::Rational(x), Value::Integer(y)) => Number::same_value(*x, f64::from(*y)), + (Value::Integer(x), Value::Rational(y)) => Number::same_value(f64::from(*x), *y), + (Value::Integer(x), Value::Integer(y)) => x == y, // 3. Return ! SameValueNonNumeric(x, y). (_, _) => same_value_non_numeric(x, y), } } -/// The internal comparison abstract operation SameValueZero(x, y), -/// where x and y are ECMAScript language values, produces true or false. -/// SameValueZero differs from SameValue only in its treatment of +0 and -0. +/// The internal comparison abstract operation `SameValueZero(x, y)`, +/// where `x` and `y` are ECMAScript language values, produces `true` or `false`. +/// +/// `SameValueZero` differs from SameValue only in its treatment of `+0` and `-0`. /// -/// Such a comparison is performed as follows: +/// More information: +/// - [ECMAScript][spec] /// -/// +/// [spec]: https://tc39.es/ecma262/#sec-samevaluezero pub fn same_value_zero(x: &Value, y: &Value) -> bool { if x.get_type() != y.get_type() { return false; } - match (x.data(), y.data()) { + match (x, y) { // 2. If Type(x) is Number or BigInt, then // a. Return ! Type(x)::SameValueZero(x, y). - (ValueData::BigInt(x), ValueData::BigInt(y)) => BigInt::same_value_zero(x, y), + (Value::BigInt(x), Value::BigInt(y)) => BigInt::same_value_zero(x, y), - (ValueData::Rational(x), ValueData::Rational(y)) => Number::same_value_zero(*x, *y), - (ValueData::Rational(x), ValueData::Integer(y)) => { - Number::same_value_zero(*x, f64::from(*y)) - } - (ValueData::Integer(x), ValueData::Rational(y)) => { - Number::same_value_zero(f64::from(*x), *y) - } - (ValueData::Integer(x), ValueData::Integer(y)) => x == y, + (Value::Rational(x), Value::Rational(y)) => Number::same_value_zero(*x, *y), + (Value::Rational(x), Value::Integer(y)) => Number::same_value_zero(*x, f64::from(*y)), + (Value::Integer(x), Value::Rational(y)) => Number::same_value_zero(f64::from(*x), *y), + (Value::Integer(x), Value::Integer(y)) => x == y, // 3. Return ! SameValueNonNumeric(x, y). (_, _) => same_value_non_numeric(x, y), @@ -199,11 +193,11 @@ pub fn same_value_zero(x: &Value, y: &Value) -> bool { fn same_value_non_numeric(x: &Value, y: &Value) -> bool { debug_assert!(x.get_type() == y.get_type()); - match x.get_type() { - Type::Null | Type::Undefined => true, - Type::String => x.to_string() == y.to_string(), - Type::Boolean => bool::from(x) == bool::from(y), - Type::Object => std::ptr::eq(x.data(), y.data()), + match (x, y) { + (Value::Null, Value::Null) | (Value::Undefined, Value::Undefined) => true, + (Value::String(ref x), Value::String(ref y)) => x == y, + (Value::Boolean(x), Value::Boolean(y)) => x == y, + (Value::Object(ref x), Value::Object(ref y)) => GcObject::equals(x, y), _ => false, } } diff --git a/boa/src/builtins/value/hash.rs b/boa/src/builtins/value/hash.rs index 2ddc7dc3eb..96ce6ebb5f 100644 --- a/boa/src/builtins/value/hash.rs +++ b/boa/src/builtins/value/hash.rs @@ -38,17 +38,16 @@ impl Hash for RationalHashable { impl Hash for Value { fn hash(&self, state: &mut H) { - let data = self.data(); - match data { - ValueData::Undefined => UndefinedHashable.hash(state), - ValueData::Null => NullHashable.hash(state), - ValueData::String(ref string) => string.hash(state), - ValueData::Boolean(boolean) => boolean.hash(state), - ValueData::Integer(integer) => integer.hash(state), - ValueData::BigInt(ref bigint) => bigint.hash(state), - ValueData::Rational(rational) => RationalHashable(*rational).hash(state), - ValueData::Symbol(ref symbol) => Hash::hash(symbol, state), - ValueData::Object(_) => std::ptr::hash(data, state), + match self { + Self::Undefined => UndefinedHashable.hash(state), + Self::Null => NullHashable.hash(state), + Self::String(ref string) => string.hash(state), + Self::Boolean(boolean) => boolean.hash(state), + Self::Integer(integer) => integer.hash(state), + Self::BigInt(ref bigint) => bigint.hash(state), + Self::Rational(rational) => RationalHashable(*rational).hash(state), + Self::Symbol(ref symbol) => Hash::hash(symbol, state), + Self::Object(ref object) => std::ptr::hash(object.as_ref(), state), } } } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index c91e0c7cfa..bec4a45621 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -11,13 +11,16 @@ pub use crate::builtins::value::val_type::Type; use crate::builtins::{ function::Function, - object::{InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, + object::{ + GcObject, InternalState, InternalStateCell, Object, ObjectData, INSTANCE_PROTOTYPE, + PROTOTYPE, + }, property::Property, BigInt, Symbol, }; use crate::exec::Interpreter; use crate::BoaProfiler; -use gc::{Finalize, Gc, GcCell, GcCellRef, GcCellRefMut, Trace}; +use gc::{Finalize, GcCellRef, GcCellRefMut, Trace}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use std::{ any::Any, @@ -34,32 +37,57 @@ pub mod display; pub mod equality; pub mod hash; pub mod operations; +pub mod rcbigint; +pub mod rcstring; +pub mod rcsymbol; pub use conversions::*; pub(crate) use display::display_obj; pub use equality::*; pub use hash::*; pub use operations::*; +pub use rcbigint::RcBigInt; +pub use rcstring::RcString; +pub use rcsymbol::RcSymbol; /// The result of a Javascript expression is represented like this so it can succeed (`Ok`) or fail (`Err`) #[must_use] pub type ResultValue = Result; -/// A Garbage-collected Javascript value as represented in the interpreter. -#[derive(Debug, Clone, Trace, Finalize, Default)] -pub struct Value(Gc); +/// A Javascript value +#[derive(Trace, Finalize, Debug, Clone)] +pub enum Value { + /// `null` - A null value, for when a value doesn't exist. + Null, + /// `undefined` - An undefined value, for when a field or index doesn't exist. + Undefined, + /// `boolean` - A `true` / `false` value, for if a certain criteria is met. + Boolean(bool), + /// `String` - A UTF-8 string, such as `"Hello, world"`. + String(RcString), + /// `Number` - A 64-bit floating point number, such as `3.1415` + Rational(f64), + /// `Number` - A 32-bit integer, such as `42`. + Integer(i32), + /// `BigInt` - holds any arbitrary large signed integer. + BigInt(RcBigInt), + /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values. + Object(GcObject), + /// `Symbol` - A Symbol Primitive type. + Symbol(RcSymbol), +} impl Value { /// Creates a new `undefined` value. #[inline] pub fn undefined() -> Self { - Self(Gc::new(ValueData::Undefined)) + Self::Undefined } /// Creates a new `null` value. #[inline] pub fn null() -> Self { - Self(Gc::new(ValueData::Null)) + Self::Null } /// Creates a new number with `NaN` value. @@ -72,9 +100,9 @@ impl Value { #[inline] pub fn string(value: S) -> Self where - S: Into, + S: Into, { - Self(Gc::new(ValueData::String(value.into()))) + Self::String(value.into()) } /// Creates a new number value. @@ -83,7 +111,7 @@ impl Value { where N: Into, { - Self(Gc::new(ValueData::Rational(value.into()))) + Self::Rational(value.into()) } /// Creates a new number value. @@ -92,7 +120,7 @@ impl Value { where I: Into, { - Self(Gc::new(ValueData::Integer(value.into()))) + Self::Integer(value.into()) } /// Creates a new number value. @@ -106,38 +134,35 @@ impl Value { /// Creates a new bigint value. #[inline] - pub fn bigint(value: BigInt) -> Self { - Self(Gc::new(ValueData::BigInt(value))) + pub fn bigint(value: B) -> Self + where + B: Into, + { + Self::BigInt(value.into()) } /// Creates a new boolean value. #[inline] pub fn boolean(value: bool) -> Self { - Self(Gc::new(ValueData::Boolean(value))) + Self::Boolean(value) } /// Creates a new object value. #[inline] pub fn object(object: Object) -> Self { - Self(Gc::new(ValueData::Object(Box::new(GcCell::new(object))))) + Self::Object(GcObject::new(object)) } /// Creates a new symbol value. #[inline] - pub fn symbol(symbol: Symbol) -> Self { - Self(Gc::new(ValueData::Symbol(symbol))) - } - - /// Gets the underlying `ValueData` structure. - #[inline] - pub fn data(&self) -> &ValueData { - &*self.0 + pub(crate) fn symbol(symbol: Symbol) -> Self { + Self::Symbol(RcSymbol::from(symbol)) } /// Helper function to convert the `Value` to a number and compute its power. pub fn as_num_to_power(&self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => Self::bigint(a.clone().pow(b)), + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(a.as_inner().clone().pow(b)), (a, b) => Self::rational(a.to_number().powf(b.to_number())), } } @@ -145,6 +170,7 @@ impl Value { /// Returns a new empty object pub fn new_object(global: Option<&Value>) -> Self { let _timer = BoaProfiler::global().start_event("new_object", "value"); + if let Some(global) = global { let object_prototype = global.get_field("Object").get_field(PROTOTYPE); @@ -225,10 +251,10 @@ impl Value { /// Conversts the `Value` to `JSON`. pub fn to_json(&self, interpreter: &mut Interpreter) -> Result { - match *self.data() { - ValueData::Null => Ok(JSONValue::Null), - ValueData::Boolean(b) => Ok(JSONValue::Bool(b)), - ValueData::Object(ref obj) => { + match *self { + Self::Null => Ok(JSONValue::Null), + Self::Boolean(b) => Ok(JSONValue::Bool(b)), + Self::Object(ref obj) => { if obj.borrow().is_array() { let mut arr: Vec = Vec::new(); for k in obj.borrow().properties().keys() { @@ -248,59 +274,26 @@ impl Value { let key = k.clone(); let value = self.get_field(k.to_string()); if !value.is_undefined() && !value.is_function() { - new_obj.insert(key, value.to_json(interpreter)?); + new_obj.insert(key.to_string(), value.to_json(interpreter)?); } } Ok(JSONValue::Object(new_obj)) } } - ValueData::String(ref str) => Ok(JSONValue::String(str.clone())), - ValueData::Rational(num) => Ok(JSONNumber::from_f64(num) + Self::String(ref str) => Ok(JSONValue::String(str.to_string())), + Self::Rational(num) => Ok(JSONNumber::from_f64(num) .map(JSONValue::Number) .unwrap_or(JSONValue::Null)), - ValueData::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))), - ValueData::BigInt(_) => { + Self::Integer(val) => Ok(JSONValue::Number(JSONNumber::from(val))), + Self::BigInt(_) => { Err(interpreter.construct_type_error("BigInt value can't be serialized in JSON")) } - ValueData::Symbol(_) | ValueData::Undefined => { + Self::Symbol(_) | Self::Undefined => { unreachable!("Symbols and Undefined JSON Values depend on parent type"); } } } -} - -impl Deref for Value { - type Target = ValueData; - - fn deref(&self) -> &Self::Target { - self.data() - } -} - -/// A Javascript value -#[derive(Trace, Finalize, Debug, Clone)] -pub enum ValueData { - /// `null` - A null value, for when a value doesn't exist. - Null, - /// `undefined` - An undefined value, for when a field or index doesn't exist. - Undefined, - /// `boolean` - A `true` / `false` value, for if a certain criteria is met. - Boolean(bool), - /// `String` - A UTF-8 string, such as `"Hello, world"`. - String(String), - /// `Number` - A 64-bit floating point number, such as `3.1415` - Rational(f64), - /// `Number` - A 32-bit integer, such as `42`. - Integer(i32), - /// `BigInt` - holds any arbitrary large signed integer. - BigInt(BigInt), - /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values. - Object(Box>), - /// `Symbol` - A Symbol Primitive type. - Symbol(Symbol), -} -impl ValueData { /// This will tell us if we can exten an object or not, not properly implemented yet /// /// For now always returns true. @@ -430,7 +423,7 @@ impl ValueData { Self::Rational(n) if n != 0.0 && !n.is_nan() => true, Self::Integer(n) if n != 0 => true, Self::Boolean(v) => v, - Self::BigInt(ref n) if *n != 0 => true, + Self::BigInt(ref n) if *n.as_inner() != 0 => true, _ => false, } } @@ -488,7 +481,7 @@ impl ValueData { Self::String(ref s) if !s.is_empty() => true, Self::Rational(n) if n != 0.0 && !n.is_nan() => true, Self::Integer(n) if n != 0 => true, - Self::BigInt(ref n) if *n != 0 => true, + Self::BigInt(ref n) if *n.as_inner() != 0 => true, Self::Boolean(v) => v, _ => false, } @@ -568,12 +561,12 @@ impl ValueData { /// Resolve the property in the object and get its value, or undefined if this is not an object or the field doesn't exist /// get_field recieves a Property from get_prop(). It should then return the [[Get]] result value if that's set, otherwise fall back to [[Value]] /// TODO: this function should use the get Value if its set - pub fn get_field(&self, field: F) -> Value + pub fn get_field(&self, field: F) -> Self where F: Into, { let _timer = BoaProfiler::global().start_event("Value::get_field", "value"); - match *field.into() { + match field.into() { // Our field will either be a String or a Symbol Self::String(ref s) => { match self.get_property(s) { @@ -727,7 +720,7 @@ impl ValueData { /// Set the property in the value. pub fn set_property(&self, field: S, property: Property) -> Property where - S: Into, + S: Into, { if let Some(mut object) = self.as_object_mut() { object @@ -758,7 +751,7 @@ impl ValueData { } } -impl Default for ValueData { +impl Default for Value { fn default() -> Self { Self::Undefined } diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index a0a529ef82..95c6c2e1c6 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -3,14 +3,14 @@ use super::*; impl Add for Value { type Output = Self; fn add(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::String(ref s), ref o) => { + match (self, other) { + (Self::String(ref s), ref o) => { Self::string(format!("{}{}", s.clone(), &o.to_string())) } - (ValueData::BigInt(ref n1), ValueData::BigInt(ref n2)) => { - Self::bigint(n1.clone() + n2.clone()) + (Self::BigInt(ref n1), Self::BigInt(ref n2)) => { + Self::bigint(n1.as_inner().clone() + n2.as_inner().clone()) } - (ref s, ValueData::String(ref o)) => Self::string(format!("{}{}", s.to_string(), o)), + (ref s, Self::String(ref o)) => Self::string(format!("{}{}", s.to_string(), o)), (ref s, ref o) => Self::rational(s.to_number() + o.to_number()), } } @@ -18,9 +18,9 @@ impl Add for Value { impl Sub for Value { type Output = Self; fn sub(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() - b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() - b.as_inner().clone()) } (a, b) => Self::rational(a.to_number() - b.to_number()), } @@ -29,9 +29,9 @@ impl Sub for Value { impl Mul for Value { type Output = Self; fn mul(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() * b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() * b.as_inner().clone()) } (a, b) => Self::rational(a.to_number() * b.to_number()), } @@ -40,9 +40,9 @@ impl Mul for Value { impl Div for Value { type Output = Self; fn div(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() / b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() / b.as_inner().clone()) } (a, b) => Self::rational(a.to_number() / b.to_number()), } @@ -51,9 +51,9 @@ impl Div for Value { impl Rem for Value { type Output = Self; fn rem(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() % b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() % b.as_inner().clone()) } (a, b) => Self::rational(a.to_number() % b.to_number()), } @@ -62,9 +62,9 @@ impl Rem for Value { impl BitAnd for Value { type Output = Self; fn bitand(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() & b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() & b.as_inner().clone()) } (a, b) => Self::integer(a.to_integer() & b.to_integer()), } @@ -73,9 +73,9 @@ impl BitAnd for Value { impl BitOr for Value { type Output = Self; fn bitor(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() | b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() | b.as_inner().clone()) } (a, b) => Self::integer(a.to_integer() | b.to_integer()), } @@ -84,9 +84,9 @@ impl BitOr for Value { impl BitXor for Value { type Output = Self; fn bitxor(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() ^ b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() ^ b.as_inner().clone()) } (a, b) => Self::integer(a.to_integer() ^ b.to_integer()), } @@ -96,9 +96,9 @@ impl BitXor for Value { impl Shl for Value { type Output = Self; fn shl(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() << b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() << b.as_inner().clone()) } (a, b) => Self::integer(a.to_integer() << b.to_integer()), } @@ -107,9 +107,9 @@ impl Shl for Value { impl Shr for Value { type Output = Self; fn shr(self, other: Self) -> Self { - match (self.data(), other.data()) { - (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { - Self::bigint(a.clone() >> b.clone()) + match (self, other) { + (Self::BigInt(ref a), Self::BigInt(ref b)) => { + Self::bigint(a.as_inner().clone() >> b.as_inner().clone()) } (a, b) => Self::integer(a.to_integer() >> b.to_integer()), } @@ -126,19 +126,17 @@ impl Neg for Value { type Output = Self; fn neg(self) -> Self::Output { - match self.data() { - ValueData::Object(_) | ValueData::Symbol(_) | ValueData::Undefined => { - Self::rational(NAN) - } - ValueData::String(ref str) => Self::rational(match f64::from_str(str) { + match self { + Self::Object(_) | Self::Symbol(_) | Self::Undefined => Self::rational(NAN), + Self::String(ref str) => Self::rational(match f64::from_str(str) { Ok(num) => -num, Err(_) => NAN, }), - ValueData::Rational(num) => Self::rational(-num), - ValueData::Integer(num) => Self::rational(-f64::from(*num)), - ValueData::Boolean(true) => Self::integer(1), - ValueData::Boolean(false) | ValueData::Null => Self::integer(0), - ValueData::BigInt(ref num) => Self::bigint(-num.clone()), + Self::Rational(num) => Self::rational(-num), + Self::Integer(num) => Self::rational(-f64::from(num)), + Self::Boolean(true) => Self::integer(1), + Self::Boolean(false) | Self::Null => Self::integer(0), + Self::BigInt(ref num) => Self::bigint(-num.as_inner().clone()), } } } diff --git a/boa/src/builtins/value/rcbigint.rs b/boa/src/builtins/value/rcbigint.rs new file mode 100644 index 0000000000..3bf8ce6e26 --- /dev/null +++ b/boa/src/builtins/value/rcbigint.rs @@ -0,0 +1,42 @@ +use crate::builtins::BigInt; + +use std::fmt::{self, Display}; +use std::ops::Deref; +use std::rc::Rc; + +use gc::{unsafe_empty_trace, Finalize, Trace}; + +#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RcBigInt(Rc); + +unsafe impl Trace for RcBigInt { + unsafe_empty_trace!(); +} + +impl RcBigInt { + pub(crate) fn as_inner(&self) -> &BigInt { + &self.0 + } +} + +impl Display for RcBigInt { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl Deref for RcBigInt { + type Target = BigInt; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for RcBigInt { + #[inline] + fn from(bigint: BigInt) -> Self { + Self(Rc::from(bigint)) + } +} diff --git a/boa/src/builtins/value/rcstring.rs b/boa/src/builtins/value/rcstring.rs new file mode 100644 index 0000000000..f5fab54732 --- /dev/null +++ b/boa/src/builtins/value/rcstring.rs @@ -0,0 +1,87 @@ +use std::borrow::Borrow; +use std::fmt::{self, Display}; +use std::ops::Deref; +use std::rc::Rc; + +use gc::{unsafe_empty_trace, Finalize, Trace}; + +#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RcString(Rc); + +unsafe impl Trace for RcString { + unsafe_empty_trace!(); +} + +impl RcString { + #[inline] + pub fn as_str(&self) -> &str { + &self.0 + } +} + +impl Default for RcString { + #[inline] + fn default() -> Self { + Self(Rc::from(String::new())) + } +} + +impl Display for RcString { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + Display::fmt(&self.0, f) + } +} + +impl PartialEq for RcString { + fn eq(&self, other: &str) -> bool { + self.as_str() == other + } +} + +impl PartialEq for str { + fn eq(&self, other: &RcString) -> bool { + self == other.as_str() + } +} + +impl PartialEq<&str> for RcString { + fn eq(&self, other: &&str) -> bool { + self.as_str() == *other + } +} + +impl PartialEq for &str { + fn eq(&self, other: &RcString) -> bool { + *self == other.as_str() + } +} + +impl Deref for RcString { + type Target = str; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl Borrow for RcString { + #[inline] + fn borrow(&self) -> &str { + self.0.borrow() + } +} + +impl From for RcString { + #[inline] + fn from(string: String) -> Self { + Self(Rc::from(string)) + } +} + +impl From<&str> for RcString { + #[inline] + fn from(string: &str) -> Self { + Self(Rc::from(string)) + } +} diff --git a/boa/src/builtins/value/rcsymbol.rs b/boa/src/builtins/value/rcsymbol.rs new file mode 100644 index 0000000000..7fa1288d42 --- /dev/null +++ b/boa/src/builtins/value/rcsymbol.rs @@ -0,0 +1,39 @@ +use crate::builtins::Symbol; + +use std::fmt::{self, Display}; +use std::ops::Deref; +use std::rc::Rc; + +use gc::{unsafe_empty_trace, Finalize, Trace}; + +#[derive(Debug, Finalize, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct RcSymbol(Rc); + +unsafe impl Trace for RcSymbol { + unsafe_empty_trace!(); +} + +impl Display for RcSymbol { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self.0.description() { + Some(desc) => write!(f, "Symbol({})", desc), + None => write!(f, "Symbol()"), + } + } +} + +impl Deref for RcSymbol { + type Target = Symbol; + + #[inline] + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl From for RcSymbol { + #[inline] + fn from(symbol: Symbol) -> Self { + Self(Rc::from(symbol)) + } +} diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 008b04076a..c3bd570ca2 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -20,7 +20,7 @@ fn check_string_to_value() { #[test] fn check_undefined() { - let u = ValueData::Undefined; + let u = Value::Undefined; assert_eq!(u.get_type(), Type::Undefined); assert_eq!(u.to_string(), "undefined"); } diff --git a/boa/src/builtins/value/val_type.rs b/boa/src/builtins/value/val_type.rs index 9f49fdea9f..134a81cd00 100644 --- a/boa/src/builtins/value/val_type.rs +++ b/boa/src/builtins/value/val_type.rs @@ -1,4 +1,4 @@ -use crate::builtins::value::ValueData; +use crate::builtins::value::Value; use std::ops::Deref; /// Possible types of val as defined at https://tc39.es/ecma262/#sec-typeof-operator. @@ -32,7 +32,7 @@ impl Type { } } -impl ValueData { +impl Value { /// Get the type of the value. /// /// This is similar to typeof as described at https://tc39.es/ecma262/#sec-typeof-operator but instead of diff --git a/boa/src/environment/global_environment_record.rs b/boa/src/environment/global_environment_record.rs index 7f79566c2e..b04ec34d48 100644 --- a/boa/src/environment/global_environment_record.rs +++ b/boa/src/environment/global_environment_record.rs @@ -21,9 +21,9 @@ use rustc_hash::FxHashSet; #[derive(Debug, Trace, Finalize, Clone)] pub struct GlobalEnvironmentRecord { - pub object_record: Box, + pub object_record: ObjectEnvironmentRecord, pub global_this_binding: Value, - pub declarative_record: Box, + pub declarative_record: DeclarativeEnvironmentRecord, pub var_names: FxHashSet, } diff --git a/boa/src/environment/lexical_environment.rs b/boa/src/environment/lexical_environment.rs index 57112aa3cb..9bb933c8f6 100644 --- a/boa/src/environment/lexical_environment.rs +++ b/boa/src/environment/lexical_environment.rs @@ -260,7 +260,7 @@ pub fn new_object_environment(object: Value, environment: Option) - } pub fn new_global_environment(global: Value, this_value: Value) -> Environment { - let obj_rec = Box::new(ObjectEnvironmentRecord { + let obj_rec = ObjectEnvironmentRecord { bindings: global, outer_env: None, /// Object Environment Records created for with statements (13.11) @@ -269,12 +269,12 @@ pub fn new_global_environment(global: Value, this_value: Value) -> Environment { /// with each object Environment Record. By default, the value of withEnvironment is false /// for any object Environment Record. with_environment: false, - }); + }; - let dcl_rec = Box::new(DeclarativeEnvironmentRecord { + let dcl_rec = DeclarativeEnvironmentRecord { env_rec: FxHashMap::default(), outer_env: None, - }); + }; Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord { object_record: obj_rec, @@ -311,7 +311,7 @@ mod tests { { const bar = "bar"; } - + try{ bar; } catch (err) { diff --git a/boa/src/exec/expression/mod.rs b/boa/src/exec/expression/mod.rs index 59a59e1b5e..8d77eff856 100644 --- a/boa/src/exec/expression/mod.rs +++ b/boa/src/exec/expression/mod.rs @@ -4,7 +4,7 @@ use super::{Executable, Interpreter, InterpreterState}; use crate::{ builtins::{ object::{ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, - value::{ResultValue, Type, Value, ValueData}, + value::{ResultValue, Type, Value}, }, syntax::ast::node::{Call, New, Node}, BoaProfiler, @@ -13,7 +13,7 @@ use crate::{ impl Executable for Call { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { let _timer = BoaProfiler::global().start_event("Call", "exec"); - let (mut this, func) = match self.expr() { + let (this, func) = match self.expr() { Node::GetConstField(ref get_const_field) => { let mut obj = get_const_field.obj().run(interpreter)?; if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol { @@ -45,7 +45,7 @@ impl Executable for Call { } // execute the function call itself - let fnct_result = interpreter.call(&func, &mut this, &v_args); + let fnct_result = interpreter.call(&func, &this, &v_args); // unset the early return flag interpreter.set_current_state(InterpreterState::Executing); @@ -66,15 +66,15 @@ impl Executable for New { for arg in self.args() { v_args.push(arg.run(interpreter)?); } - let mut this = Value::new_object(None); + let this = Value::new_object(None); // Create a blank object, then set its __proto__ property to the [Constructor].prototype this.set_internal_slot(INSTANCE_PROTOTYPE, func_object.get_field(PROTOTYPE)); - match func_object.data() { - ValueData::Object(ref obj) => { - let obj = (**obj).borrow(); + match func_object { + Value::Object(ref obj) => { + let obj = obj.borrow(); if let ObjectData::Function(ref func) = obj.data { - return func.construct(func_object.clone(), &mut this, &v_args, interpreter); + return func.construct(func_object.clone(), &this, &v_args, interpreter); } interpreter.throw_type_error("not a constructor") } diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 0082422d61..b8ac1d8b05 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -26,7 +26,7 @@ use crate::{ function::{Function as FunctionObject, FunctionBody, ThisMode}, object::{Object, ObjectData, INSTANCE_PROTOTYPE, PROTOTYPE}, property::Property, - value::{ResultValue, Type, Value, ValueData}, + value::{RcBigInt, RcString, ResultValue, Type, Value}, BigInt, Number, }, realm::Realm, @@ -37,7 +37,7 @@ use crate::{ BoaProfiler, }; use std::convert::TryFrom; -use std::{borrow::Borrow, ops::Deref}; +use std::ops::Deref; pub trait Executable { /// Runs this executable in the given executor. @@ -156,12 +156,12 @@ impl Interpreter { pub(crate) fn call( &mut self, f: &Value, - this: &mut Value, + this: &Value, arguments_list: &[Value], ) -> ResultValue { - match *f.data() { - ValueData::Object(ref obj) => { - let obj = (**obj).borrow(); + match *f { + Value::Object(ref obj) => { + let obj = obj.borrow(); if let ObjectData::Function(ref func) = obj.data { return func.call(f.clone(), this, arguments_list, self); } @@ -173,20 +173,18 @@ impl Interpreter { /// Converts a value into a rust heap allocated string. #[allow(clippy::wrong_self_convention)] - pub fn to_string(&mut self, value: &Value) -> Result { - match value.data() { - ValueData::Null => Ok("null".to_owned()), - ValueData::Undefined => Ok("undefined".to_owned()), - ValueData::Boolean(boolean) => Ok(boolean.to_string()), - ValueData::Rational(rational) => Ok(Number::to_native_string(*rational)), - ValueData::Integer(integer) => Ok(integer.to_string()), - ValueData::String(string) => Ok(string.clone()), - ValueData::Symbol(_) => { - Err(self.construct_type_error("can't convert symbol to string")) - } - ValueData::BigInt(ref bigint) => Ok(bigint.to_string()), - ValueData::Object(_) => { - let primitive = self.to_primitive(&mut value.clone(), PreferredType::String); + pub fn to_string(&mut self, value: &Value) -> Result { + match value { + Value::Null => Ok(RcString::from("null")), + Value::Undefined => Ok(RcString::from("undefined".to_owned())), + Value::Boolean(boolean) => Ok(RcString::from(boolean.to_string())), + Value::Rational(rational) => Ok(RcString::from(Number::to_native_string(*rational))), + Value::Integer(integer) => Ok(RcString::from(integer.to_string())), + Value::String(string) => Ok(string.clone()), + Value::Symbol(_) => Err(self.construct_type_error("can't convert symbol to string")), + Value::BigInt(ref bigint) => Ok(RcString::from(bigint.to_string())), + Value::Object(_) => { + let primitive = self.to_primitive(value, PreferredType::String)?; self.to_string(&primitive) } } @@ -194,33 +192,31 @@ impl Interpreter { /// Helper function. #[allow(clippy::wrong_self_convention)] - pub fn to_bigint(&mut self, value: &Value) -> Result { - match value.data() { - ValueData::Null => Err(self.construct_type_error("cannot convert null to a BigInt")), - ValueData::Undefined => { + pub fn to_bigint(&mut self, value: &Value) -> Result { + match value { + Value::Null => Err(self.construct_type_error("cannot convert null to a BigInt")), + Value::Undefined => { Err(self.construct_type_error("cannot convert undefined to a BigInt")) } - ValueData::String(ref string) => Ok(BigInt::from_string(string, self)?), - ValueData::Boolean(true) => Ok(BigInt::from(1)), - ValueData::Boolean(false) => Ok(BigInt::from(0)), - ValueData::Integer(num) => Ok(BigInt::from(*num)), - ValueData::Rational(num) => { + Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, self)?)), + Value::Boolean(true) => Ok(RcBigInt::from(BigInt::from(1))), + Value::Boolean(false) => Ok(RcBigInt::from(BigInt::from(0))), + Value::Integer(num) => Ok(RcBigInt::from(BigInt::from(*num))), + Value::Rational(num) => { if let Ok(bigint) = BigInt::try_from(*num) { - return Ok(bigint); + return Ok(RcBigInt::from(bigint)); } Err(self.construct_type_error(format!( "The number {} cannot be converted to a BigInt because it is not an integer", num ))) } - ValueData::BigInt(b) => Ok(b.clone()), - ValueData::Object(_) => { - let primitive = self.to_primitive(&mut value.clone(), PreferredType::Number); + Value::BigInt(b) => Ok(b.clone()), + Value::Object(_) => { + let primitive = self.to_primitive(value, PreferredType::Number)?; self.to_bigint(&primitive) } - ValueData::Symbol(_) => { - Err(self.construct_type_error("cannot convert Symbol to a BigInt")) - } + Value::Symbol(_) => Err(self.construct_type_error("cannot convert Symbol to a BigInt")), } } @@ -265,19 +261,19 @@ impl Interpreter { /// See: https://tc39.es/ecma262/#sec-tonumber #[allow(clippy::wrong_self_convention)] pub fn to_number(&mut self, value: &Value) -> Result { - match *value.data() { - ValueData::Null => Ok(0.0), - ValueData::Undefined => Ok(f64::NAN), - ValueData::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }), + match *value { + Value::Null => Ok(0.0), + Value::Undefined => Ok(f64::NAN), + Value::Boolean(b) => Ok(if b { 1.0 } else { 0.0 }), // TODO: this is probably not 100% correct, see https://tc39.es/ecma262/#sec-tonumber-applied-to-the-string-type - ValueData::String(ref string) => Ok(string.parse().unwrap_or(f64::NAN)), - ValueData::Rational(number) => Ok(number), - ValueData::Integer(integer) => Ok(f64::from(integer)), - ValueData::Symbol(_) => Err(self.construct_type_error("argument must not be a symbol")), - ValueData::BigInt(_) => Err(self.construct_type_error("argument must not be a bigint")), - ValueData::Object(_) => { - let prim_value = self.to_primitive(&mut (value.clone()), PreferredType::Number); - self.to_number(&prim_value) + Value::String(ref string) => Ok(string.parse().unwrap_or(f64::NAN)), + Value::Rational(number) => Ok(number), + Value::Integer(integer) => Ok(f64::from(integer)), + Value::Symbol(_) => Err(self.construct_type_error("argument must not be a symbol")), + Value::BigInt(_) => Err(self.construct_type_error("argument must not be a bigint")), + Value::Object(_) => { + let primitive = self.to_primitive(value, PreferredType::Number)?; + self.to_number(&primitive) } } } @@ -287,7 +283,7 @@ impl Interpreter { /// See: https://tc39.es/ecma262/#sec-tonumeric #[allow(clippy::wrong_self_convention)] pub fn to_numeric(&mut self, value: &Value) -> ResultValue { - let primitive = self.to_primitive(&mut value.clone(), PreferredType::Number); + let primitive = self.to_primitive(value, PreferredType::Number)?; if primitive.is_bigint() { return Ok(primitive); } @@ -301,7 +297,7 @@ impl Interpreter { /// See: https://tc39.es/ecma262/#sec-tonumeric #[allow(clippy::wrong_self_convention)] pub(crate) fn to_numeric_number(&mut self, value: &Value) -> Result { - let primitive = self.to_primitive(&mut value.clone(), PreferredType::Number); + let primitive = self.to_primitive(value, PreferredType::Number)?; if let Some(ref bigint) = primitive.as_bigint() { return Ok(bigint.to_f64()); } @@ -312,11 +308,11 @@ impl Interpreter { /// /// This is useful for the spread operator, for any other object an `Err` is returned pub(crate) fn extract_array_properties(&mut self, value: &Value) -> Result, ()> { - if let ValueData::Object(ref x) = *value.deref().borrow() { + if let Value::Object(ref x) = value { // Check if object is array if let ObjectData::Array = x.deref().borrow().data { - let length: i32 = self.value_to_rust_number(&value.get_field("length")) as i32; - let values: Vec = (0..length) + let length = i32::from(&value.get_field("length")); + let values = (0..length) .map(|idx| value.get_field(idx.to_string())) .collect(); return Ok(values); @@ -328,58 +324,73 @@ impl Interpreter { Err(()) } - /// - pub(crate) fn ordinary_to_primitive(&mut self, o: &mut Value, hint: PreferredType) -> Value { + /// Converts an object to a primitive. + /// + /// More information: + /// - [ECMAScript][spec] + /// + /// [spec]: https://tc39.es/ecma262/#sec-ordinarytoprimitive + pub(crate) fn ordinary_to_primitive(&mut self, o: &Value, hint: PreferredType) -> ResultValue { + // 1. Assert: Type(O) is Object. debug_assert!(o.get_type() == Type::Object); + // 2. Assert: Type(hint) is String and its value is either "string" or "number". debug_assert!(hint == PreferredType::String || hint == PreferredType::Number); - let method_names: Vec<&str> = if hint == PreferredType::String { - vec!["toString", "valueOf"] + + // 3. If hint is "string", then + // a. Let methodNames be « "toString", "valueOf" ». + // 4. Else, + // a. Let methodNames be « "valueOf", "toString" ». + let method_names = if hint == PreferredType::String { + ["toString", "valueOf"] } else { - vec!["valueOf", "toString"] + ["valueOf", "toString"] }; - for name in method_names.iter() { + + // 5. For each name in methodNames in List order, do + for name in &method_names { + // a. Let method be ? Get(O, name). let method: Value = o.get_field(*name); + // b. If IsCallable(method) is true, then if method.is_function() { - let result = self.call(&method, o, &[]); - match result { - Ok(val) => { - if val.is_object() { - // TODO: throw exception - continue; - } else { - return val; - } - } - Err(_) => continue, + // i. Let result be ? Call(method, O). + let result = self.call(&method, &o, &[])?; + // ii. If Type(result) is not Object, return result. + if !result.is_object() { + return Ok(result); } } } - Value::undefined() + // 6. Throw a TypeError exception. + self.throw_type_error("cannot convert object to primitive value") } /// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. + /// /// #[allow(clippy::wrong_self_convention)] pub(crate) fn to_primitive( &mut self, - input: &mut Value, + input: &Value, preferred_type: PreferredType, - ) -> Value { - let mut hint: PreferredType; - match (*input).deref() { - ValueData::Object(_) => { - hint = preferred_type; - - // Skip d, e we don't support Symbols yet - // TODO: add when symbols are supported - if hint == PreferredType::Default { - hint = PreferredType::Number; - }; - - self.ordinary_to_primitive(input, hint) - } - _ => input.clone(), + ) -> ResultValue { + // 1. Assert: input is an ECMAScript language value. (always a value not need to check) + // 2. If Type(input) is Object, then + if let Value::Object(_) = input { + let mut hint = preferred_type; + + // Skip d, e we don't support Symbols yet + // TODO: add when symbols are supported + // TODO: Add other steps. + if hint == PreferredType::Default { + hint = PreferredType::Number; + }; + + // g. Return ? OrdinaryToPrimitive(input, hint). + self.ordinary_to_primitive(input, hint) + } else { + // 3. Return input. + Ok(input.clone()) } } @@ -387,8 +398,8 @@ impl Interpreter { /// /// https://tc39.es/ecma262/#sec-topropertykey #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_property_key(&mut self, value: &mut Value) -> ResultValue { - let key = self.to_primitive(value, PreferredType::String); + pub(crate) fn to_property_key(&mut self, value: &Value) -> ResultValue { + let key = self.to_primitive(value, PreferredType::String)?; if key.is_symbol() { Ok(key) } else { @@ -397,7 +408,7 @@ impl Interpreter { } /// https://tc39.es/ecma262/#sec-hasproperty - pub(crate) fn has_property(&self, obj: &mut Value, key: &Value) -> bool { + pub(crate) fn has_property(&self, obj: &Value, key: &Value) -> bool { if let Some(obj) = obj.as_object() { if !Property::is_property_key(key) { false @@ -413,9 +424,9 @@ impl Interpreter { /// https://tc39.es/ecma262/#sec-toobject #[allow(clippy::wrong_self_convention)] pub(crate) fn to_object(&mut self, value: &Value) -> ResultValue { - match value.data() { - ValueData::Undefined | ValueData::Null => Err(Value::undefined()), - ValueData::Boolean(boolean) => { + match value { + Value::Undefined | Value::Null => Err(Value::undefined()), + Value::Boolean(boolean) => { let proto = self .realm .environment @@ -428,7 +439,7 @@ impl Interpreter { ObjectData::Boolean(*boolean), )) } - ValueData::Integer(integer) => { + Value::Integer(integer) => { let proto = self .realm .environment @@ -440,7 +451,7 @@ impl Interpreter { ObjectData::Number(f64::from(*integer)), )) } - ValueData::Rational(rational) => { + Value::Rational(rational) => { let proto = self .realm .environment @@ -453,7 +464,7 @@ impl Interpreter { ObjectData::Number(*rational), )) } - ValueData::String(ref string) => { + Value::String(ref string) => { let proto = self .realm .environment @@ -466,7 +477,7 @@ impl Interpreter { ObjectData::String(string.clone()), )) } - ValueData::Symbol(ref symbol) => { + Value::Symbol(ref symbol) => { let proto = self .realm .environment @@ -479,7 +490,7 @@ impl Interpreter { ObjectData::Symbol(symbol.clone()), )) } - ValueData::BigInt(ref bigint) => { + Value::BigInt(ref bigint) => { let proto = self .realm .environment @@ -490,36 +501,7 @@ impl Interpreter { Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone())); Ok(bigint_obj) } - ValueData::Object(_) => Ok(value.clone()), - } - } - - pub(crate) fn value_to_rust_number(&mut self, value: &Value) -> f64 { - match *value.deref().borrow() { - ValueData::Null => f64::from(0), - ValueData::Boolean(boolean) => { - if boolean { - f64::from(1) - } else { - f64::from(0) - } - } - ValueData::Rational(num) => num, - ValueData::Integer(num) => f64::from(num), - ValueData::String(ref string) => string.parse::().unwrap(), - ValueData::BigInt(ref bigint) => bigint.to_f64(), - ValueData::Object(_) => { - let prim_value = self.to_primitive(&mut (value.clone()), PreferredType::Number); - self.to_string(&prim_value) - .expect("cannot convert value to string") - .parse::() - .expect("cannot parse value to f64") - } - ValueData::Undefined => f64::NAN, - _ => { - // TODO: Make undefined? - f64::from(0) - } + Value::Object(_) => Ok(value.clone()), } } diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index 86953a923e..e7643902a8 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -12,7 +12,6 @@ use crate::{ }, BoaProfiler, }; -use std::borrow::BorrowMut; impl Executable for Assign { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { @@ -78,11 +77,11 @@ impl Executable for BinOp { }) } op::BinOp::Comp(op) => { - let mut v_a = self.lhs().run(interpreter)?; - let mut v_b = self.rhs().run(interpreter)?; + let v_a = self.lhs().run(interpreter)?; + let v_b = self.rhs().run(interpreter)?; Ok(Value::from(match op { - CompOp::Equal => v_a.equals(v_b.borrow_mut(), interpreter), - CompOp::NotEqual => !v_a.equals(v_b.borrow_mut(), interpreter), + CompOp::Equal => v_a.equals(&v_b, interpreter)?, + CompOp::NotEqual => !v_a.equals(&v_b, interpreter)?, CompOp::StrictEqual => v_a.strict_equals(&v_b), CompOp::StrictNotEqual => !v_a.strict_equals(&v_b), CompOp::GreaterThan => v_a.to_number() > v_b.to_number(), @@ -96,8 +95,8 @@ impl Executable for BinOp { v_b.get_type().as_str() )); } - let key = interpreter.to_property_key(&mut v_a)?; - interpreter.has_property(&mut v_b, &key) + let key = interpreter.to_property_key(&v_a)?; + interpreter.has_property(&v_b, &key) } })) } diff --git a/boa/src/realm.rs b/boa/src/realm.rs index b6cfc4decf..1119ef7bca 100644 --- a/boa/src/realm.rs +++ b/boa/src/realm.rs @@ -8,7 +8,7 @@ use crate::{ builtins::{ self, function::{Function, NativeFunctionData}, - value::{Value, ValueData}, + value::Value, }, environment::{ declarative_environment_record::DeclarativeEnvironmentRecord, @@ -27,7 +27,7 @@ use rustc_hash::{FxHashMap, FxHashSet}; #[derive(Debug)] pub struct Realm { pub global_obj: Value, - pub global_env: Gc>>, + pub global_env: Gc>, pub environment: LexicalEnvironment, } @@ -65,18 +65,15 @@ impl Realm { pub fn register_global_func(self, func_name: &str, func: NativeFunctionData) -> Self { let func = Function::builtin(Vec::new(), func); self.global_obj - .set_field(Value::from(func_name), ValueData::from_func(func)); + .set_field(Value::from(func_name), Value::from_func(func)); self } } // Similar to new_global_environment in lexical_environment, except we need to return a GlobalEnvirionment -fn new_global_environment( - global: Value, - this_value: Value, -) -> Gc>> { - let obj_rec = Box::new(ObjectEnvironmentRecord { +fn new_global_environment(global: Value, this_value: Value) -> Gc> { + let obj_rec = ObjectEnvironmentRecord { bindings: global, outer_env: None, /// Object Environment Records created for with statements (13.11) @@ -85,17 +82,17 @@ fn new_global_environment( /// with each object Environment Record. By default, the value of withEnvironment is false /// for any object Environment Record. with_environment: false, - }); + }; - let dcl_rec = Box::new(DeclarativeEnvironmentRecord { + let dcl_rec = DeclarativeEnvironmentRecord { env_rec: FxHashMap::default(), outer_env: None, - }); + }; - Gc::new(GcCell::new(Box::new(GlobalEnvironmentRecord { + Gc::new(GcCell::new(GlobalEnvironmentRecord { object_record: obj_rec, global_this_binding: this_value, declarative_record: dcl_rec, var_names: FxHashSet::default(), - }))) + })) }