From 6cb8859e7c702490b9462ce3d1e824a07c8b8f99 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 17:37:55 +0200 Subject: [PATCH 01/18] Moved `Interpreter::to_primitive()` to `Value::to_primitive()` --- boa/src/builtins/date/mod.rs | 6 ++-- boa/src/builtins/date/tests.rs | 10 +++--- boa/src/builtins/value/equality.rs | 6 ++-- boa/src/builtins/value/mod.rs | 35 ++++++++++++++++++++ boa/src/builtins/value/operations.rs | 18 +++++----- boa/src/builtins/value/tests.rs | 1 + boa/src/exec/mod.rs | 49 ++++------------------------ 7 files changed, 64 insertions(+), 61 deletions(-) diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index 08591516f6a..bab18302825 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -5,9 +5,9 @@ use crate::{ builtins::{ function::{make_builtin_fn, make_constructor_fn}, object::ObjectData, + value::PreferredType, ResultValue, Value, }, - exec::PreferredType, BoaProfiler, Interpreter, }; use chrono::{prelude::*, Duration, LocalResult}; @@ -306,8 +306,8 @@ impl Date { let value = &args[0]; let tv = match this_time_value(value, ctx) { Ok(dt) => dt.0, - _ => match &ctx.to_primitive(value, PreferredType::Default)? { - Value::String(str) => match chrono::DateTime::parse_from_rfc3339(&str) { + _ => match value.to_primitive(ctx, PreferredType::Default)? { + Value::String(ref str) => match chrono::DateTime::parse_from_rfc3339(&str) { Ok(dt) => Some(dt.naive_utc()), _ => None, }, diff --git a/boa/src/builtins/date/tests.rs b/boa/src/builtins/date/tests.rs index 45c2b16abc0..1196f94d770 100644 --- a/boa/src/builtins/date/tests.rs +++ b/boa/src/builtins/date/tests.rs @@ -1,10 +1,12 @@ +#![allow(clippy::zero_prefixed_literal)] + use crate::{ builtins::{object::ObjectData, Value}, forward, forward_val, Interpreter, Realm, }; use chrono::prelude::*; -// NB: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of +// NOTE: Javascript Uses 0-based months, where chrono uses 1-based months. Many of the assertions look wrong because of // this. fn forward_dt_utc(engine: &mut Interpreter, src: &str) -> Option { @@ -20,13 +22,11 @@ fn forward_dt_utc(engine: &mut Interpreter, src: &str) -> Option panic!("expected object") }; - let date_time = if let ObjectData::Date(date_time) = &date_time.borrow().data { + if let ObjectData::Date(date_time) = &date_time.borrow().data { date_time.0 } else { panic!("expected date") - }; - - date_time.clone() + } } fn forward_dt_local(engine: &mut Interpreter, src: &str) -> Option { diff --git a/boa/src/builtins/value/equality.rs b/boa/src/builtins/value/equality.rs index 9ea75e60007..ae475c01dbf 100644 --- a/boa/src/builtins/value/equality.rs +++ b/boa/src/builtins/value/equality.rs @@ -1,5 +1,5 @@ use super::*; -use crate::{builtins::Number, exec::PreferredType, Interpreter}; +use crate::{builtins::Number, Interpreter}; use std::borrow::Borrow; @@ -94,14 +94,14 @@ impl Value { // 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). (Self::Object(_), _) => { - let primitive = interpreter.to_primitive(self, PreferredType::Default)?; + let primitive = self.to_primitive(interpreter, 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. (_, Self::Object(_)) => { - let primitive = interpreter.to_primitive(other, PreferredType::Default)?; + let primitive = other.to_primitive(interpreter, PreferredType::Default)?; return primitive.equals(self, interpreter); } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index b2016f31495..bdc4248efa8 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -704,6 +704,34 @@ impl Value { new_func_val.set_field("length", Value::from(length)); new_func_val } + + /// The abstract operation ToPrimitive takes an input argument and an optional argument PreferredType. + /// + /// + pub fn to_primitive( + &self, + ctx: &mut Interpreter, + preferred_type: PreferredType, + ) -> 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(_) = self { + 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). + ctx.ordinary_to_primitive(self, hint) + } else { + // 3. Return input. + Ok(self.clone()) + } + } } impl Default for Value { @@ -711,3 +739,10 @@ impl Default for Value { Self::Undefined } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PreferredType { + String, + Number, + Default, +} diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index c8ad4c39b53..3659fe7e527 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -1,6 +1,8 @@ use super::*; -use crate::builtins::number::{f64_to_int32, f64_to_uint32, Number}; -use crate::exec::PreferredType; +use crate::builtins::{ + number::{f64_to_int32, f64_to_uint32, Number}, + value::PreferredType, +}; impl Value { #[inline] @@ -20,8 +22,8 @@ impl Value { // Slow path: (_, _) => match ( - ctx.to_primitive(self, PreferredType::Default)?, - ctx.to_primitive(other, PreferredType::Default)?, + self.to_primitive(ctx, PreferredType::Default)?, + other.to_primitive(ctx, PreferredType::Default)?, ) { (Self::String(ref x), ref y) => Self::string(format!("{}{}", x, ctx.to_string(y)?)), (ref x, Self::String(ref y)) => Self::string(format!("{}{}", ctx.to_string(x)?, y)), @@ -440,13 +442,13 @@ impl Value { // Slow path: (_, _) => { let (px, py) = if left_first { - let px = ctx.to_primitive(self, PreferredType::Number)?; - let py = ctx.to_primitive(other, PreferredType::Number)?; + let px = self.to_primitive(ctx, PreferredType::Number)?; + let py = other.to_primitive(ctx, PreferredType::Number)?; (px, py) } else { // NOTE: The order of evaluation needs to be reversed to preserve left to right evaluation. - let py = ctx.to_primitive(other, PreferredType::Number)?; - let px = ctx.to_primitive(self, PreferredType::Number)?; + let py = other.to_primitive(ctx, PreferredType::Number)?; + let px = self.to_primitive(ctx, PreferredType::Number)?; (px, py) }; diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 6401b72cbef..e0a978b1c4d 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -359,6 +359,7 @@ fn bitand_rational_and_rational() { } #[test] +#[allow(clippy::float_cmp)] fn pow_number_and_number() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index c33117cb748..481c1e751dd 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -29,7 +29,7 @@ use crate::{ number::{f64_to_int32, f64_to_uint32}, object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, - value::{RcBigInt, RcString, ResultValue, Type, Value}, + value::{PreferredType, RcBigInt, RcString, ResultValue, Type, Value}, BigInt, Console, Number, }, realm::Realm, @@ -54,12 +54,6 @@ pub(crate) enum InterpreterState { Return, Break(Option), } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PreferredType { - String, - Number, - Default, -} /// A Javascript intepreter #[derive(Debug)] @@ -210,7 +204,7 @@ impl Interpreter { 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)?; + let primitive = value.to_primitive(self, PreferredType::String)?; self.to_string(&primitive) } } @@ -239,7 +233,7 @@ impl Interpreter { } Value::BigInt(b) => Ok(b.clone()), Value::Object(_) => { - let primitive = self.to_primitive(value, PreferredType::Number)?; + let primitive = value.to_primitive(self, PreferredType::Number)?; self.to_bigint(&primitive) } Value::Symbol(_) => Err(self.construct_type_error("cannot convert Symbol to a BigInt")), @@ -352,7 +346,7 @@ impl Interpreter { 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)?; + let primitive = value.to_primitive(self, PreferredType::Number)?; self.to_number(&primitive) } } @@ -363,7 +357,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(value, PreferredType::Number)?; + let primitive = value.to_primitive(self, PreferredType::Number)?; if primitive.is_bigint() { return Ok(primitive); } @@ -377,7 +371,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(value, PreferredType::Number)?; + let primitive = value.to_primitive(self, PreferredType::Number)?; if let Some(ref bigint) = primitive.as_bigint() { return Ok(bigint.to_f64()); } @@ -476,41 +470,12 @@ impl Interpreter { 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: &Value, - preferred_type: PreferredType, - ) -> 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()) - } - } - /// The abstract operation ToPropertyKey takes argument argument. It converts argument to a value that can be used as a property key. /// /// https://tc39.es/ecma262/#sec-topropertykey #[allow(clippy::wrong_self_convention)] pub(crate) fn to_property_key(&mut self, value: &Value) -> Result { - let key = self.to_primitive(value, PreferredType::String)?; + let key = value.to_primitive(self, PreferredType::String)?; if let Value::Symbol(ref symbol) = key { Ok(PropertyKey::from(symbol.clone())) } else { From 58ee11b97e249dbd82870ce7cad27076b41233ad Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 18:09:51 +0200 Subject: [PATCH 02/18] Moved `Interpreter::to_bigint()` to `Value::to_bigint()` --- boa/src/builtins/bigint/mod.rs | 4 ++-- boa/src/builtins/date/tests.rs | 14 ++++++-------- boa/src/builtins/value/mod.rs | 29 ++++++++++++++++++++++++++++ boa/src/exec/mod.rs | 35 ++-------------------------------- boa/src/exec/tests.rs | 10 +++++----- 5 files changed, 44 insertions(+), 48 deletions(-) diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 45a82a1a71f..a272a3c3149 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -93,7 +93,7 @@ impl BigInt { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt 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)?, + Some(ref value) => value.to_bigint(ctx)?, None => RcBigInt::from(Self::from(0)), }; Ok(Value::from(data)) @@ -187,7 +187,7 @@ impl BigInt { let bits = ctx.to_index(bits_arg)?; let bits = u32::try_from(bits).unwrap_or(u32::MAX); - let bigint = ctx.to_bigint(bigint_arg)?; + let bigint = bigint_arg.to_bigint(ctx)?; Ok(( bigint diff --git a/boa/src/builtins/date/tests.rs b/boa/src/builtins/date/tests.rs index 1196f94d770..fb005c3f0db 100644 --- a/boa/src/builtins/date/tests.rs +++ b/boa/src/builtins/date/tests.rs @@ -16,16 +16,14 @@ fn forward_dt_utc(engine: &mut Interpreter, src: &str) -> Option panic!("expected success") }; - let date_time = if let Value::Object(date_time) = &date_time { - date_time + if let Value::Object(ref date_time) = date_time { + if let ObjectData::Date(ref date_time) = date_time.borrow().data { + date_time.0 + } else { + panic!("expected date") + } } else { panic!("expected object") - }; - - if let ObjectData::Date(date_time) = &date_time.borrow().data { - date_time.0 - } else { - panic!("expected date") } } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index bdc4248efa8..2fb10023c93 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -732,6 +732,35 @@ impl Value { Ok(self.clone()) } } + + /// Helper function. + pub fn to_bigint(&self, ctx: &mut Interpreter) -> Result { + match self { + Value::Null => Err(ctx.construct_type_error("cannot convert null to a BigInt")), + Value::Undefined => { + Err(ctx.construct_type_error("cannot convert undefined to a BigInt")) + } + Value::String(ref string) => Ok(RcBigInt::from(BigInt::from_string(string, ctx)?)), + 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(RcBigInt::from(bigint)); + } + Err(ctx.construct_type_error(format!( + "The number {} cannot be converted to a BigInt because it is not an integer", + num + ))) + } + Value::BigInt(b) => Ok(b.clone()), + Value::Object(_) => { + let primitive = self.to_primitive(ctx, PreferredType::Number)?; + primitive.to_bigint(ctx) + } + Value::Symbol(_) => Err(ctx.construct_type_error("cannot convert Symbol to a BigInt")), + } + } } impl Default for Value { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 481c1e751dd..7a480692dfa 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -29,8 +29,8 @@ use crate::{ number::{f64_to_int32, f64_to_uint32}, object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, - value::{PreferredType, RcBigInt, RcString, ResultValue, Type, Value}, - BigInt, Console, Number, + value::{PreferredType, RcString, ResultValue, Type, Value}, + Console, Number, }, realm::Realm, syntax::ast::{ @@ -40,7 +40,6 @@ use crate::{ BoaProfiler, }; use std::borrow::Borrow; -use std::convert::TryFrom; use std::ops::Deref; pub trait Executable { @@ -210,36 +209,6 @@ impl Interpreter { } } - /// Helper function. - #[allow(clippy::wrong_self_convention)] - 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")) - } - 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(RcBigInt::from(bigint)); - } - Err(self.construct_type_error(format!( - "The number {} cannot be converted to a BigInt because it is not an integer", - num - ))) - } - Value::BigInt(b) => Ok(b.clone()), - Value::Object(_) => { - let primitive = value.to_primitive(self, PreferredType::Number)?; - self.to_bigint(&primitive) - } - Value::Symbol(_) => Err(self.construct_type_error("cannot convert Symbol to a BigInt")), - } - } - /// Converts a value to a non-negative integer if it is a valid integer index value. /// /// See: https://tc39.es/ecma262/#sec-toindex diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 0a4b089e918..32d863cfc95 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -879,11 +879,11 @@ fn to_bigint() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); - assert!(engine.to_bigint(&Value::null()).is_err()); - assert!(engine.to_bigint(&Value::undefined()).is_err()); - assert!(engine.to_bigint(&Value::integer(55)).is_ok()); - assert!(engine.to_bigint(&Value::rational(10.0)).is_ok()); - assert!(engine.to_bigint(&Value::string("100")).is_ok()); + assert!(Value::null().to_bigint(&mut engine).is_err()); + assert!(Value::undefined().to_bigint(&mut engine).is_err()); + assert!(Value::integer(55).to_bigint(&mut engine).is_ok()); + assert!(Value::rational(10.0).to_bigint(&mut engine).is_ok()); + assert!(Value::string("100").to_bigint(&mut engine).is_ok()); } #[test] From 96ba3a2f09d280491e5fdaed204428b9878b5924 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 19:32:06 +0200 Subject: [PATCH 03/18] Added `Value::display()` which implements Display --- boa/src/builtins/console/mod.rs | 6 ++--- boa/src/builtins/error/mod.rs | 6 ++++- boa/src/builtins/error/syntax.rs | 2 +- boa/src/builtins/function/mod.rs | 2 +- boa/src/builtins/object/mod.rs | 5 ++-- boa/src/builtins/symbol/tests.rs | 2 +- boa/src/builtins/value/conversions.rs | 2 +- boa/src/builtins/value/display.rs | 35 +++++++++++++++------------ boa/src/builtins/value/mod.rs | 5 ++++ boa/src/builtins/value/tests.rs | 18 +++++++------- boa/src/exec/call/mod.rs | 3 ++- boa/src/lib.rs | 6 +++-- boa_cli/src/main.rs | 10 +++++--- boa_wasm/src/lib.rs | 4 +-- 14 files changed, 63 insertions(+), 43 deletions(-) diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 54935719582..913b650d6a8 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -87,7 +87,7 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result /* object, FIXME: how to render this properly? */ 'o' | 'O' => { let arg = data.get(arg_index).cloned().unwrap_or_default(); - formatted.push_str(&format!("{}", arg)); + formatted.push_str(&format!("{}", arg.display())); arg_index += 1 } /* string */ @@ -153,7 +153,7 @@ impl Console { } else if !args[0].is_string() { args.insert(0, Value::from(message)); } else { - let concat = format!("{}: {}", message, args[0]); + let concat = format!("{}: {}", message, args[0].display()); args[0] = Value::from(concat); } @@ -384,7 +384,7 @@ impl Console { let time = Self::system_time_in_ms(); let mut concat = format!("{}: {} ms", label, time - t); for msg in args.iter().skip(1) { - concat = concat + " " + &msg.to_string(); + concat = concat + " " + &msg.display().to_string(); } logger(LogMessage::Log(concat), ctx.console()); } else { diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index 51024a8cc04..cc75116dc14 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -71,7 +71,11 @@ impl Error { 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))) + Ok(Value::from(format!( + "{}: {}", + name.display(), + message.display() + ))) } /// Initialise the global object with the `Error` object. diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 508015770f3..08b8d7f7779 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -59,7 +59,7 @@ impl SyntaxError { pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let name = this.get_field("name"); let message = this.get_field("message"); - Ok(format!("{}: {}", name, message).into()) + Ok(format!("{}: {}", name.display(), message.display()).into()) } /// Initialise the global object with the `SyntaxError` object. diff --git a/boa/src/builtins/function/mod.rs b/boa/src/builtins/function/mod.rs index fb701dd6b10..ad34e9dabc0 100644 --- a/boa/src/builtins/function/mod.rs +++ b/boa/src/builtins/function/mod.rs @@ -337,7 +337,7 @@ impl Function { } } } else { - let name = this.get_field("name").to_string(); + let name = this.get_field("name").display().to_string(); panic!("TypeError: {} is not a constructor", name); } } diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 89d6af50942..6498763634f 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -478,7 +478,7 @@ pub fn create(_: &Value, args: &[Value], interpreter: &mut Interpreter) -> Resul )), _ => interpreter.throw_type_error(format!( "Object prototype may only be an Object or null: {}", - prototype + prototype.display() )), } } @@ -527,7 +527,8 @@ pub fn define_property(_: &Value, args: &[Value], ctx: &mut Interpreter) -> Resu /// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - Ok(Value::from(this.to_string())) + // FIXME: it should not display the object. + Ok(Value::from(this.display().to_string())) } /// `Object.prototype.hasOwnPrototype( property )` diff --git a/boa/src/builtins/symbol/tests.rs b/boa/src/builtins/symbol/tests.rs index 518632a4ef9..a85b351abac 100644 --- a/boa/src/builtins/symbol/tests.rs +++ b/boa/src/builtins/symbol/tests.rs @@ -21,5 +21,5 @@ fn print_symbol_expect_description() { "#; eprintln!("{}", forward(&mut engine, init)); let sym = forward_val(&mut engine, "sym.toString()").unwrap(); - assert_eq!(sym.to_string(), "\"Symbol(Hello)\""); + assert_eq!(sym.display().to_string(), "\"Symbol(Hello)\""); } diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 3a0a04872fa..38e78cc4176 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -71,7 +71,7 @@ impl TryFrom<&Value> for char { type Error = TryFromCharError; fn try_from(value: &Value) -> Result { - if let Some(c) = value.to_string().chars().next() { + if let Some(c) = value.display().to_string().chars().next() { Ok(c) } else { Err(TryFromCharError) diff --git a/boa/src/builtins/value/display.rs b/boa/src/builtins/value/display.rs index 8b0f1e3a2cf..3fff3f5b12e 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -1,5 +1,10 @@ use super::*; +#[derive(Debug, Clone, Copy)] +pub struct ValueDisplay<'value> { + pub(super) value: &'value Value, +} + /// A helper macro for printing objects /// Can be used to print both properties and internal slots /// All of the overloads take: @@ -34,7 +39,7 @@ macro_rules! print_obj_value { vec![format!( "{:>width$}: {}", "__proto__", - object.prototype(), + object.prototype().display(), width = $indent, )] } @@ -158,7 +163,7 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: } } Value::Symbol(ref symbol) => symbol.to_string(), - _ => format!("{}", x), + _ => format!("{}", x.display()), } } @@ -179,7 +184,7 @@ pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String { if object.borrow().is_error() { let name = v.get_field("name"); let message = v.get_field("message"); - return format!("{}: {}", name, message); + return format!("{}: {}", name.display(), message.display()); } } @@ -219,28 +224,28 @@ pub(crate) fn display_obj(v: &Value, print_internals: bool) -> String { format!("{{\n{}\n{}}}", result, closing_indent) } else { // Every other type of data is printed with the display method - format!("{}", data) + format!("{}", data.display()) } } display_obj_internal(v, &mut encounters, 4, print_internals) } -impl Display for Value { +impl Display for ValueDisplay<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match self { - Self::Null => write!(f, "null"), - Self::Undefined => write!(f, "undefined"), - Self::Boolean(v) => write!(f, "{}", v), - Self::Symbol(ref symbol) => match symbol.description() { + match self.value { + Value::Null => write!(f, "null"), + Value::Undefined => write!(f, "undefined"), + Value::Boolean(v) => write!(f, "{}", v), + Value::Symbol(ref symbol) => match symbol.description() { Some(description) => write!(f, "Symbol({})", description), None => write!(f, "Symbol()"), }, - Self::String(ref v) => write!(f, "\"{}\"", v), - Self::Rational(v) => format_rational(*v, f), - Self::Object(_) => write!(f, "{}", log_string_from(self, true, true)), - Self::Integer(v) => write!(f, "{}", v), - Self::BigInt(ref num) => write!(f, "{}n", num), + Value::String(ref v) => write!(f, "\"{}\"", v), + Value::Rational(v) => format_rational(*v, f), + Value::Object(_) => write!(f, "{}", log_string_from(self.value, true, true)), + Value::Integer(v) => write!(f, "{}", v), + Value::BigInt(ref num) => write!(f, "{}n", num), } } } diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 2fb10023c93..294b87fee1e 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -36,6 +36,7 @@ mod r#type; pub use conversions::*; pub(crate) use display::display_obj; +pub use display::ValueDisplay; pub use equality::*; pub use hash::*; pub use operations::*; @@ -761,6 +762,10 @@ impl Value { Value::Symbol(_) => Err(ctx.construct_type_error("cannot convert Symbol to a BigInt")), } } + + pub fn display(&self) -> ValueDisplay<'_> { + ValueDisplay { value: self } + } } impl Default for Value { diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index e0a978b1c4d..74b3e40758c 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -22,7 +22,7 @@ fn string_to_value() { fn undefined() { let u = Value::Undefined; assert_eq!(u.get_type(), Type::Undefined); - assert_eq!(u.to_string(), "undefined"); + assert_eq!(u.display().to_string(), "undefined"); } #[test] @@ -31,7 +31,7 @@ fn get_set_field() { // Create string and convert it to a Value let s = Value::from("bar"); obj.set_field("foo", s); - assert_eq!(obj.get_field("foo").to_string(), "\"bar\""); + assert_eq!(obj.get_field("foo").display().to_string(), "\"bar\""); } #[test] @@ -216,7 +216,7 @@ fn get_types() { #[test] fn to_string() { - let f64_to_str = |f| Value::Rational(f).to_string(); + let f64_to_str = |f| Value::Rational(f).display().to_string(); assert_eq!(f64_to_str(f64::NAN), "NaN"); assert_eq!(f64_to_str(0.0), "0"); @@ -401,7 +401,7 @@ fn assign_pow_number_and_string() { fn display_string() { let s = String::from("Hello"); let v = Value::from(s); - assert_eq!(v.to_string(), "\"Hello\""); + assert_eq!(v.display().to_string(), "\"Hello\""); } #[test] @@ -410,7 +410,7 @@ fn display_array_string() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "[\"Hello\"]").unwrap(); - assert_eq!(value.to_string(), "[ \"Hello\" ]"); + assert_eq!(value.display().to_string(), "[ \"Hello\" ]"); } #[test] @@ -422,7 +422,7 @@ fn display_boolean_object() { bool "#; let value = forward_val(&mut engine, d_obj).unwrap(); - assert_eq!(value.to_string(), "Boolean { false }") + assert_eq!(value.display().to_string(), "Boolean { false }") } #[test] @@ -434,7 +434,7 @@ fn display_number_object() { num "#; let value = forward_val(&mut engine, d_obj).unwrap(); - assert_eq!(value.to_string(), "Number { 3.14 }") + assert_eq!(value.display().to_string(), "Number { 3.14 }") } #[test] @@ -446,7 +446,7 @@ fn display_negative_zero_object() { num "#; let value = forward_val(&mut engine, d_obj).unwrap(); - assert_eq!(value.to_string(), "Number { -0 }") + assert_eq!(value.display().to_string(), "Number { -0 }") } #[test] @@ -460,7 +460,7 @@ fn display_object() { "#; let value = forward_val(&mut engine, d_obj).unwrap(); assert_eq!( - value.to_string(), + value.display().to_string(), r#"{ a: "a", __proto__: { diff --git a/boa/src/exec/call/mod.rs b/boa/src/exec/call/mod.rs index 363c165327a..946c838be38 100644 --- a/boa/src/exec/call/mod.rs +++ b/boa/src/exec/call/mod.rs @@ -21,7 +21,8 @@ impl Executable for Call { Node::GetField(ref get_field) => { let obj = get_field.obj().run(interpreter)?; let field = get_field.field().run(interpreter)?; - (obj.clone(), obj.get_field(field.to_string())) + // FIXME: This should not call `.display()` + (obj.clone(), obj.get_field(field.display().to_string())) } _ => ( interpreter.realm().global_obj.clone(), diff --git a/boa/src/lib.rs b/boa/src/lib.rs index 4314d77f794..55a6006d3ff 100644 --- a/boa/src/lib.rs +++ b/boa/src/lib.rs @@ -66,8 +66,10 @@ pub fn forward(engine: &mut Interpreter, src: &str) -> String { Ok(res) => res, Err(e) => return e, }; - expr.run(engine) - .map_or_else(|e| format!("Error: {}", e), |v| v.to_string()) + expr.run(engine).map_or_else( + |e| format!("Error: {}", e.display()), + |v| v.display().to_string(), + ) } /// Execute the code using an existing Interpreter. diff --git a/boa_cli/src/main.rs b/boa_cli/src/main.rs index ddb2def6375..80ff1766503 100644 --- a/boa_cli/src/main.rs +++ b/boa_cli/src/main.rs @@ -194,8 +194,8 @@ pub fn main() -> Result<(), std::io::Error> { } } else { match forward_val(&mut engine, &buffer) { - Ok(v) => print!("{}", v), - Err(v) => eprint!("{}", v), + Ok(v) => print!("{}", v.display()), + Err(v) => eprint!("{}", v.display()), } } } @@ -230,8 +230,10 @@ pub fn main() -> Result<(), std::io::Error> { } } else { match forward_val(&mut engine, line.trim_end()) { - Ok(v) => println!("{}", v), - Err(v) => eprintln!("{}: {}", "Uncaught".red(), v.to_string().red()), + Ok(v) => println!("{}", v.display()), + Err(v) => { + eprintln!("{}: {}", "Uncaught".red(), v.display().to_string().red()) + } } } } diff --git a/boa_wasm/src/lib.rs b/boa_wasm/src/lib.rs index 97eab2e5c61..6c58529e8bc 100644 --- a/boa_wasm/src/lib.rs +++ b/boa_wasm/src/lib.rs @@ -19,6 +19,6 @@ pub fn evaluate(src: &str) -> Result { // Setup executor expr.run(&mut engine) - .map_err(|e| JsValue::from(format!("Error: {}", e))) - .map(|v| v.to_string()) + .map_err(|e| JsValue::from(format!("Error: {}", e.display()))) + .map(|v| v.display().to_string()) } From df1575c975e3700c5329b2227f1391d4c67f0cf2 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 20:20:29 +0200 Subject: [PATCH 04/18] Moved `Interpreter::to_string()` to `Value::to_string()` --- boa/src/builtins/array/mod.rs | 6 +- boa/src/builtins/console/mod.rs | 21 ++++--- boa/src/builtins/date/mod.rs | 2 +- boa/src/builtins/error/mod.rs | 2 +- boa/src/builtins/error/range.rs | 6 +- boa/src/builtins/error/reference.rs | 6 +- boa/src/builtins/error/syntax.rs | 3 +- boa/src/builtins/error/type.rs | 6 +- boa/src/builtins/json/mod.rs | 9 ++- boa/src/builtins/object/mod.rs | 4 +- boa/src/builtins/regexp/mod.rs | 10 ++- boa/src/builtins/string/mod.rs | 94 +++++++++++++++------------- boa/src/builtins/symbol/mod.rs | 2 +- boa/src/builtins/value/mod.rs | 20 +++++- boa/src/builtins/value/operations.rs | 8 +-- boa/src/builtins/value/tests.rs | 6 +- boa/src/exec/field/mod.rs | 2 +- boa/src/exec/mod.rs | 23 +------ boa/src/exec/operator/mod.rs | 2 +- boa/src/exec/tests.rs | 16 +++-- 20 files changed, 137 insertions(+), 111 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 6976a17b9d2..dfafe80e9ef 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -299,14 +299,16 @@ impl Array { let separator = if args.is_empty() { String::from(",") } else { - ctx.to_string(args.get(0).expect("Could not get argument"))? + args.get(0) + .expect("Could not get argument") + .to_string(ctx)? .to_string() }; let mut elem_strs = Vec::new(); let length = i32::from(&this.get_field("length")); for n in 0..length { - let elem_str = ctx.to_string(&this.get_field(n.to_string()))?.to_string(); + let elem_str = this.get_field(n.to_string()).to_string(ctx)?.to_string(); elem_strs.push(elem_str); } diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 913b650d6a8..f1fe819917a 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -60,7 +60,7 @@ pub(crate) fn logger(msg: LogMessage, console_state: &Console) { /// This represents the `console` formatter. pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result { - let target = ctx.to_string(&data.get(0).cloned().unwrap_or_default())?; + let target = data.get(0).cloned().unwrap_or_default().to_string(ctx)?; match data.len() { 0 => Ok(String::new()), 1 => Ok(target.to_string()), @@ -92,8 +92,11 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result } /* string */ 's' => { - let arg = - ctx.to_string(&data.get(arg_index).cloned().unwrap_or_default())?; + let arg = data + .get(arg_index) + .cloned() + .unwrap_or_default() + .to_string(ctx)?; formatted.push_str(&arg); arg_index += 1 } @@ -111,7 +114,7 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result /* unformatted data */ for rest in data.iter().skip(arg_index) { - formatted.push_str(&format!(" {}", ctx.to_string(rest)?)) + formatted.push_str(&format!(" {}", rest.to_string(ctx)?)) } Ok(formatted) @@ -289,7 +292,7 @@ impl Console { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/count pub(crate) fn count(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { - Some(value) => ctx.to_string(value)?, + Some(value) => value.to_string(ctx)?, None => "default".into(), }; @@ -313,7 +316,7 @@ impl Console { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/countReset pub(crate) fn count_reset(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { - Some(value) => ctx.to_string(value)?, + Some(value) => value.to_string(ctx)?, None => "default".into(), }; @@ -347,7 +350,7 @@ impl Console { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/time pub(crate) fn time(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { - Some(value) => ctx.to_string(value)?, + Some(value) => value.to_string(ctx)?, None => "default".into(), }; @@ -376,7 +379,7 @@ impl Console { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeLog pub(crate) fn time_log(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { - Some(value) => ctx.to_string(value)?, + Some(value) => value.to_string(ctx)?, None => "default".into(), }; @@ -409,7 +412,7 @@ impl Console { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/API/console/timeEnd pub(crate) fn time_end(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let label = match args.get(0) { - Some(value) => ctx.to_string(value)?, + Some(value) => value.to_string(ctx)?, None => "default".into(), }; diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index bab18302825..73def1a674e 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -1188,7 +1188,7 @@ impl Date { return Ok(Value::number(f64::NAN)); } - match DateTime::parse_from_rfc3339(&ctx.to_string(&args[0])?) { + match DateTime::parse_from_rfc3339(&args[0].to_string(ctx)?) { Ok(v) => Ok(Value::number(v.naive_utc().timestamp_millis() as f64)), _ => Ok(Value::number(f64::NAN)), } diff --git a/boa/src/builtins/error/mod.rs b/boa/src/builtins/error/mod.rs index cc75116dc14..7fea89045f3 100644 --- a/boa/src/builtins/error/mod.rs +++ b/boa/src/builtins/error/mod.rs @@ -48,7 +48,7 @@ impl Error { /// Create a new error object. pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(message) = args.get(0) { - this.set_field("message", ctx.to_string(message)?); + this.set_field("message", message.to_string(ctx)?); } // This value is used by console.log and other routines to match Object type diff --git a/boa/src/builtins/error/range.rs b/boa/src/builtins/error/range.rs index 87c6dc2740e..0318bf4424f 100644 --- a/boa/src/builtins/error/range.rs +++ b/boa/src/builtins/error/range.rs @@ -34,7 +34,7 @@ impl RangeError { /// Create a new error object. pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(message) = args.get(0) { - this.set_field("message", ctx.to_string(message)?); + this.set_field("message", message.to_string(ctx)?); } // This value is used by console.log and other routines to match Object type @@ -55,8 +55,8 @@ impl RangeError { /// [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: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { - let name = ctx.to_string(&this.get_field("name"))?; - let message = ctx.to_string(&this.get_field("message"))?; + let name = this.get_field("name").to_string(ctx)?; + let message = this.get_field("message").to_string(ctx)?; Ok(Value::from(format!("{}: {}", name, message))) } diff --git a/boa/src/builtins/error/reference.rs b/boa/src/builtins/error/reference.rs index baa79d31371..d9ae09ef57c 100644 --- a/boa/src/builtins/error/reference.rs +++ b/boa/src/builtins/error/reference.rs @@ -33,7 +33,7 @@ impl ReferenceError { /// Create a new error object. pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(message) = args.get(0) { - this.set_field("message", ctx.to_string(message)?); + this.set_field("message", message.to_string(ctx)?); } // This value is used by console.log and other routines to match Object type @@ -54,8 +54,8 @@ impl ReferenceError { /// [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: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { - let name = ctx.to_string(&this.get_field("name"))?; - let message = ctx.to_string(&this.get_field("message"))?; + let name = this.get_field("name").to_string(ctx)?; + let message = this.get_field("message").to_string(ctx)?; Ok(Value::from(format!("{}: {}", name, message))) } diff --git a/boa/src/builtins/error/syntax.rs b/boa/src/builtins/error/syntax.rs index 08b8d7f7779..2d7ab27bf10 100644 --- a/boa/src/builtins/error/syntax.rs +++ b/boa/src/builtins/error/syntax.rs @@ -36,7 +36,7 @@ impl SyntaxError { /// Create a new error object. pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(message) = args.get(0) { - this.set_field("message", ctx.to_string(message)?); + this.set_field("message", message.to_string(ctx)?); } // This value is used by console.log and other routines to match Object type @@ -59,6 +59,7 @@ impl SyntaxError { pub(crate) fn to_string(this: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { let name = this.get_field("name"); let message = this.get_field("message"); + // FIXME: This should not use `.display()` Ok(format!("{}: {}", name.display(), message.display()).into()) } diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index 5e61e91a593..7d13075cd5e 100644 --- a/boa/src/builtins/error/type.rs +++ b/boa/src/builtins/error/type.rs @@ -40,7 +40,7 @@ impl TypeError { /// Create a new error object. pub(crate) fn make_error(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { if let Some(message) = args.get(0) { - this.set_field("message", ctx.to_string(message)?); + this.set_field("message", message.to_string(ctx)?); } // This value is used by console.log and other routines to match Object type @@ -61,8 +61,8 @@ impl TypeError { /// [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: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { - let name = ctx.to_string(&this.get_field("name"))?; - let message = ctx.to_string(&this.get_field("message"))?; + let name = this.get_field("name").to_string(ctx)?; + let message = this.get_field("message").to_string(ctx)?; Ok(Value::from(format!("{}: {}", name, message))) } diff --git a/boa/src/builtins/json/mod.rs b/boa/src/builtins/json/mod.rs index 1393b483856..31d0e9627f0 100644 --- a/boa/src/builtins/json/mod.rs +++ b/boa/src/builtins/json/mod.rs @@ -46,7 +46,10 @@ impl Json { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse 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"))?, + &args + .get(0) + .expect("cannot get argument for JSON.parse") + .to_string(ctx)?, ) { Ok(json) => { let j = Value::from_json(json, ctx); @@ -157,11 +160,11 @@ impl Json { }); for field in fields { if let Some(value) = object - .get_property(&ctx.to_string(&field)?) + .get_property(&field.to_string(ctx)?) .and_then(|prop| prop.value.as_ref().map(|v| v.to_json(ctx))) .transpose()? { - obj_to_return.insert(ctx.to_string(&field)?.to_string(), value); + obj_to_return.insert(field.to_string(ctx)?.to_string(), value); } } Ok(Value::from(JSONValue::Object(obj_to_return).to_string())) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 6498763634f..efe38e731b7 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -510,7 +510,7 @@ pub fn set_prototype_of(_: &Value, args: &[Value], _: &mut Interpreter) -> Resul /// Define a property in an object 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 prop = args.get(1).expect("Cannot get object").to_string(ctx)?; let desc = Property::from(args.get(2).expect("Cannot get object")); obj.set_property(prop, desc); Ok(Value::undefined()) @@ -546,7 +546,7 @@ pub fn has_own_property(this: &Value, args: &[Value], ctx: &mut Interpreter) -> let prop = if args.is_empty() { None } else { - Some(ctx.to_string(args.get(0).expect("Cannot get object"))?) + Some(args.get(0).expect("Cannot get object").to_string(ctx)?) }; let own_property = this .as_object() diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 579ec609308..a49285d8ef4 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -289,7 +289,10 @@ 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: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { - let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; + let arg_str = args + .get(0) + .expect("could not get argument") + .to_string(ctx)?; let mut last_index = usize::from(&this.get_field("lastIndex")); let result = if let Some(object) = this.as_object() { let regex = object.as_regexp().unwrap(); @@ -325,7 +328,10 @@ 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: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { - let arg_str = ctx.to_string(args.get(0).expect("could not get argument"))?; + let arg_str = args + .get(0) + .expect("could not get argument") + .to_string(ctx)?; let mut last_index = usize::from(&this.get_field("lastIndex")); let result = if let Some(object) = this.as_object() { let regex = object.as_regexp().unwrap(); diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 80bc7909d75..45eca9cd964 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -71,7 +71,7 @@ impl String { // 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)?, + Some(ref value) => value.to_string(ctx)?, None => RcString::default(), }; @@ -111,7 +111,7 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; let pos = i32::from( args.get(0) .expect("failed to get argument for String method"), @@ -153,7 +153,7 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. @@ -192,10 +192,10 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/concat pub(crate) fn concat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let object = ctx.require_object_coercible(this)?; - let mut string = ctx.to_string(object)?.to_string(); + let mut string = object.to_string(ctx)?.to_string(); for arg in args { - string.push_str(&ctx.to_string(arg)?); + string.push_str(&arg.to_string(ctx)?); } Ok(Value::from(string)) @@ -214,7 +214,7 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat pub(crate) fn repeat(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let object = ctx.require_object_coercible(this)?; - let string = ctx.to_string(object)?; + let string = object.to_string(ctx)?; if let Some(arg) = args.get(0) { let n = ctx.to_integer(arg)?; @@ -249,7 +249,7 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; let start = i32::from( args.get(0) @@ -296,13 +296,13 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; // TODO: Should throw TypeError if pattern is regular expression - let search_string = ctx.to_string( - args.get(0) - .expect("failed to get argument for String method"), - )?; + let search_string = args + .get(0) + .expect("failed to get argument for String method") + .to_string(ctx)?; let length = primitive_val.chars().count() as i32; let search_length = search_string.chars().count() as i32; @@ -339,13 +339,13 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; // TODO: Should throw TypeError if search_string is regular expression - let search_string = ctx.to_string( - args.get(0) - .expect("failed to get argument for String method"), - )?; + let search_string = args + .get(0) + .expect("failed to get argument for String method") + .to_string(ctx)?; let length = primitive_val.chars().count() as i32; let search_length = search_string.chars().count() as i32; @@ -383,13 +383,13 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; // TODO: Should throw TypeError if search_string is regular expression - let search_string = ctx.to_string( - args.get(0) - .expect("failed to get argument for String method"), - )?; + let search_string = args + .get(0) + .expect("failed to get argument for String method") + .to_string(ctx)?; let length = primitive_val.chars().count() as i32; @@ -442,7 +442,7 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/replace pub(crate) fn replace(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { // TODO: Support Symbol replacer - let primitive_val = ctx.to_string(this)?; + let primitive_val = this.to_string(ctx)?; if args.is_empty() { return Ok(Value::from(primitive_val)); } @@ -515,7 +515,7 @@ impl String { let result = ctx.call(&replace_object, this, &results).unwrap(); - ctx.to_string(&result)?.to_string() + result.to_string(ctx)?.to_string() } _ => "undefined".to_string(), } @@ -545,10 +545,13 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/indexOf pub(crate) fn index_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let this = ctx.require_object_coercible(this)?; - let string = ctx.to_string(this)?; + let string = this.to_string(ctx)?; - let search_string = - ctx.to_string(&args.get(0).cloned().unwrap_or_else(Value::undefined))?; + let search_string = args + .get(0) + .cloned() + .unwrap_or_else(Value::undefined) + .to_string(ctx)?; let length = string.chars().count(); let start = args @@ -589,10 +592,13 @@ impl String { ctx: &mut Interpreter, ) -> ResultValue { let this = ctx.require_object_coercible(this)?; - let string = ctx.to_string(this)?; + let string = this.to_string(ctx)?; - let search_string = - ctx.to_string(&args.get(0).cloned().unwrap_or_else(Value::undefined))?; + let search_string = args + .get(0) + .cloned() + .unwrap_or_else(Value::undefined) + .to_string(ctx)?; let length = string.chars().count(); let start = args @@ -627,7 +633,7 @@ impl String { /// [regex]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions 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) + RegExp::r#match(&re, this.to_string(ctx)?, ctx) } /// Abstract method `StringPad`. @@ -677,7 +683,7 @@ 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: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { - let primitive = ctx.to_string(this)?; + let primitive = this.to_string(ctx)?; if args.is_empty() { return Err(Value::from("padEnd requires maxLength argument")); } @@ -686,7 +692,7 @@ impl String { .expect("failed to get argument for String method"), ); - let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?; + let fill_string = args.get(1).map(|arg| arg.to_string(ctx)).transpose()?; Self::string_pad(primitive, max_length, fill_string, false) } @@ -704,7 +710,7 @@ 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: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { - let primitive = ctx.to_string(this)?; + let primitive = this.to_string(ctx)?; if args.is_empty() { return Err(Value::from("padStart requires maxLength argument")); } @@ -713,7 +719,7 @@ impl String { .expect("failed to get argument for String method"), ); - let fill_string = args.get(1).map(|arg| ctx.to_string(arg)).transpose()?; + let fill_string = args.get(1).map(|arg| arg.to_string(ctx)).transpose()?; Self::string_pad(primitive, max_length, fill_string, true) } @@ -752,7 +758,7 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trim pub(crate) fn trim(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this = ctx.require_object_coercible(this)?; - let string = ctx.to_string(this)?; + let string = this.to_string(ctx)?; Ok(Value::from( string.trim_matches(Self::is_trimmable_whitespace), )) @@ -772,7 +778,7 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimStart pub(crate) fn trim_start(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this = ctx.require_object_coercible(this)?; - let string = ctx.to_string(this)?; + let string = this.to_string(ctx)?; Ok(Value::from( string.trim_start_matches(Self::is_trimmable_whitespace), )) @@ -792,7 +798,7 @@ impl String { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/trimEnd pub(crate) fn trim_end(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this = ctx.require_object_coercible(this)?; - let string = ctx.to_string(this)?; + let string = this.to_string(ctx)?; Ok(Value::from( string.trim_end_matches(Self::is_trimmable_whitespace), )) @@ -812,7 +818,7 @@ impl String { 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)?; + let this_str = this.to_string(ctx)?; // The Rust String is mapped to uppercase using the builtin .to_lowercase(). // There might be corner cases where it does not behave exactly like Javascript expects Ok(Value::from(this_str.to_lowercase())) @@ -834,7 +840,7 @@ impl String { 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)?; + let this_str = this.to_string(ctx)?; // The Rust String is mapped to uppercase using the builtin .to_uppercase(). // There might be corner cases where it does not behave exactly like Javascript expects Ok(Value::from(this_str.to_uppercase())) @@ -853,7 +859,7 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; // If no args are specified, start is 'undefined', defaults to 0 let start = if args.is_empty() { 0 @@ -901,7 +907,7 @@ impl String { 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)?; + let primitive_val = this.to_string(ctx)?; // If no args are specified, start is 'undefined', defaults to 0 let mut start = if args.is_empty() { 0 @@ -977,7 +983,7 @@ impl String { if arg.is_null() { RegExp::make_regexp( &Value::from(Object::default()), - &[Value::from(ctx.to_string(arg)?), Value::from("g")], + &[Value::from(arg.to_string(ctx)?), Value::from("g")], ctx, ) } else if arg.is_undefined() { @@ -997,7 +1003,7 @@ impl String { ), }?; - RegExp::match_all(&re, ctx.to_string(this)?.to_string()) + RegExp::match_all(&re, this.to_string(ctx)?.to_string()) } /// Initialise the `String` object on the global object. diff --git a/boa/src/builtins/symbol/mod.rs b/boa/src/builtins/symbol/mod.rs index 853aa90d869..4d47b1a0d11 100644 --- a/boa/src/builtins/symbol/mod.rs +++ b/boa/src/builtins/symbol/mod.rs @@ -74,7 +74,7 @@ impl Symbol { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/Symbol 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)?), + Some(ref value) if !value.is_undefined() => Some(value.to_string(ctx)?), _ => None, }; diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 294b87fee1e..3c64158dcbd 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -9,7 +9,7 @@ use crate::builtins::{ function::Function, object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE}, property::{Attribute, Property, PropertyKey}, - BigInt, Symbol, + BigInt, Number, Symbol, }; use crate::exec::Interpreter; use crate::BoaProfiler; @@ -766,6 +766,24 @@ impl Value { pub fn display(&self) -> ValueDisplay<'_> { ValueDisplay { value: self } } + + /// Converts a value into a rust heap allocated string. + pub fn to_string(&self, ctx: &mut Interpreter) -> Result { + match self { + Value::Null => Ok("null".into()), + Value::Undefined => Ok("undefined".into()), + Value::Boolean(boolean) => Ok(boolean.to_string().into()), + Value::Rational(rational) => Ok(Number::to_native_string(*rational).into()), + Value::Integer(integer) => Ok(integer.to_string().into()), + Value::String(string) => Ok(string.clone()), + Value::Symbol(_) => Err(ctx.construct_type_error("can't convert symbol to string")), + Value::BigInt(ref bigint) => Ok(bigint.to_string().into()), + Value::Object(_) => { + let primitive = self.to_primitive(ctx, PreferredType::String)?; + primitive.to_string(ctx) + } + } + } } impl Default for Value { diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index 3659fe7e527..0ad8932d2f6 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -14,8 +14,8 @@ impl Value { (Self::Integer(x), Self::Rational(y)) => Self::rational(f64::from(*x) + y), (Self::Rational(x), Self::Integer(y)) => Self::rational(x + f64::from(*y)), - (Self::String(ref x), ref y) => Self::string(format!("{}{}", x, ctx.to_string(y)?)), - (ref x, Self::String(ref y)) => Self::string(format!("{}{}", ctx.to_string(x)?, y)), + (Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(ctx)?)), + (ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(ctx)?, y)), (Self::BigInt(ref n1), Self::BigInt(ref n2)) => { Self::bigint(n1.as_inner().clone() + n2.as_inner().clone()) } @@ -25,8 +25,8 @@ impl Value { self.to_primitive(ctx, PreferredType::Default)?, other.to_primitive(ctx, PreferredType::Default)?, ) { - (Self::String(ref x), ref y) => Self::string(format!("{}{}", x, ctx.to_string(y)?)), - (ref x, Self::String(ref y)) => Self::string(format!("{}{}", ctx.to_string(x)?, y)), + (Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(ctx)?)), + (ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(ctx)?, y)), (x, y) => match (ctx.to_numeric(&x)?, ctx.to_numeric(&y)?) { (Self::Rational(x), Self::Rational(y)) => Self::rational(x + y), (Self::BigInt(ref n1), Self::BigInt(ref n2)) => { diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 74b3e40758c..6c86fc631c1 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -264,7 +264,7 @@ fn add_number_and_string() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "1 + \" + 2 = 3\"").unwrap(); - let value = engine.to_string(&value).unwrap(); + let value = value.to_string(&mut engine).unwrap(); assert_eq!(value, "1 + 2 = 3"); } @@ -274,7 +274,7 @@ fn add_string_and_string() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "\"Hello\" + \", world\"").unwrap(); - let value = engine.to_string(&value).unwrap(); + let value = value.to_string(&mut engine).unwrap(); assert_eq!(value, "Hello, world"); } @@ -294,7 +294,7 @@ fn add_number_object_and_string_object() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "new Number(10) + new String(\"0\")").unwrap(); - let value = engine.to_string(&value).unwrap(); + let value = value.to_string(&mut engine).unwrap(); assert_eq!(value, "100"); } diff --git a/boa/src/exec/field/mod.rs b/boa/src/exec/field/mod.rs index 45e0a2ab424..be3de9ca9f3 100644 --- a/boa/src/exec/field/mod.rs +++ b/boa/src/exec/field/mod.rs @@ -23,6 +23,6 @@ impl Executable for GetField { } let field = self.field().run(interpreter)?; - Ok(obj.get_field(interpreter.to_string(&field)?)) + Ok(obj.get_field(field.to_string(interpreter)?)) } } diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 7a480692dfa..90adbe1581b 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -29,7 +29,7 @@ use crate::{ number::{f64_to_int32, f64_to_uint32}, object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, - value::{PreferredType, RcString, ResultValue, Type, Value}, + value::{PreferredType, ResultValue, Type, Value}, Console, Number, }, realm::Realm, @@ -190,25 +190,6 @@ 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 { - 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 = value.to_primitive(self, PreferredType::String)?; - self.to_string(&primitive) - } - } - } - /// Converts a value to a non-negative integer if it is a valid integer index value. /// /// See: https://tc39.es/ecma262/#sec-toindex @@ -448,7 +429,7 @@ impl Interpreter { if let Value::Symbol(ref symbol) = key { Ok(PropertyKey::from(symbol.clone())) } else { - let string = self.to_string(&key)?; + let string = key.to_string(self)?; Ok(PropertyKey::from(string)) } } diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index 0b5a8c9671b..a9ffec70a8a 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -211,7 +211,7 @@ impl Executable for UnaryOp { Node::GetField(ref get_field) => { let obj = get_field.obj().run(interpreter)?; let field = &get_field.field().run(interpreter)?; - let res = obj.remove_property(interpreter.to_string(field)?.as_str()); + let res = obj.remove_property(field.to_string(interpreter)?.as_str()); return Ok(Value::boolean(res)); } Node::Identifier(_) => Value::boolean(false), diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 32d863cfc95..b82dd763274 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -1078,11 +1078,17 @@ fn to_string() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); - assert_eq!(engine.to_string(&Value::null()).unwrap(), "null"); - assert_eq!(engine.to_string(&Value::undefined()).unwrap(), "undefined"); - assert_eq!(engine.to_string(&Value::integer(55)).unwrap(), "55"); - assert_eq!(engine.to_string(&Value::rational(55.0)).unwrap(), "55"); - assert_eq!(engine.to_string(&Value::string("hello")).unwrap(), "hello"); + assert_eq!(Value::null().to_string(&mut engine).unwrap(), "null"); + assert_eq!( + Value::undefined().to_string(&mut engine).unwrap(), + "undefined" + ); + assert_eq!(Value::integer(55).to_string(&mut engine).unwrap(), "55"); + assert_eq!(Value::rational(55.0).to_string(&mut engine).unwrap(), "55"); + assert_eq!( + Value::string("hello").to_string(&mut engine).unwrap(), + "hello" + ); } #[test] From 2919a22ad3f96447fa9bee87d6e518be7ea2c193 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 20:34:26 +0200 Subject: [PATCH 05/18] Moved `Interpreter::to_object()` to `Value::to_object()` --- boa/src/builtins/array/mod.rs | 4 +- boa/src/builtins/object/mod.rs | 2 +- boa/src/builtins/value/mod.rs | 86 +++++++++++++++++++++++++++++++++ boa/src/exec/call/mod.rs | 6 +-- boa/src/exec/field/mod.rs | 8 ++-- boa/src/exec/mod.rs | 87 ---------------------------------- boa/src/exec/tests.rs | 9 ++-- 7 files changed, 101 insertions(+), 101 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index dfafe80e9ef..aebb879ac25 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -973,7 +973,7 @@ impl Array { args: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { - let this = interpreter.to_object(this)?; + let this = this.to_object(interpreter)?; let callback = match args.get(0) { Some(value) if value.is_function() => value, _ => return interpreter.throw_type_error("Reduce was called without a callback"), @@ -1040,7 +1040,7 @@ impl Array { args: &[Value], interpreter: &mut Interpreter, ) -> ResultValue { - let this = interpreter.to_object(this)?; + let this = this.to_object(interpreter)?; let callback = match args.get(0) { Some(value) if value.is_function() => value, _ => return interpreter.throw_type_error("reduceRight was called without a callback"), diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index efe38e731b7..e04d696656b 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -567,7 +567,7 @@ pub fn property_is_enumerable(this: &Value, args: &[Value], ctx: &mut Interprete }; let property_key = ctx.to_property_key(key)?; - let own_property = ctx.to_object(this).map(|obj| { + let own_property = this.to_object(ctx).map(|obj| { obj.as_object() .expect("Unable to deref object") .get_own_property(&property_key) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 3c64158dcbd..802949a9292 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -784,6 +784,92 @@ impl Value { } } } + + /// The abstract operation ToObject converts argument to a value of type Object + /// https://tc39.es/ecma262/#sec-toobject + pub fn to_object(&self, ctx: &mut Interpreter) -> ResultValue { + match self { + Value::Undefined | Value::Null => { + ctx.throw_type_error("cannot convert 'null' or 'undefined' to object") + } + Value::Boolean(boolean) => { + let proto = ctx + .realm + .environment + .get_binding_value("Boolean") + .expect("Boolean was not initialized") + .get_field(PROTOTYPE); + + Ok(Value::new_object_from_prototype( + proto, + ObjectData::Boolean(*boolean), + )) + } + Value::Integer(integer) => { + let proto = ctx + .realm + .environment + .get_binding_value("Number") + .expect("Number was not initialized") + .get_field(PROTOTYPE); + Ok(Value::new_object_from_prototype( + proto, + ObjectData::Number(f64::from(*integer)), + )) + } + Value::Rational(rational) => { + let proto = ctx + .realm + .environment + .get_binding_value("Number") + .expect("Number was not initialized") + .get_field(PROTOTYPE); + + Ok(Value::new_object_from_prototype( + proto, + ObjectData::Number(*rational), + )) + } + Value::String(ref string) => { + let proto = ctx + .realm + .environment + .get_binding_value("String") + .expect("String was not initialized") + .get_field(PROTOTYPE); + + Ok(Value::new_object_from_prototype( + proto, + ObjectData::String(string.clone()), + )) + } + Value::Symbol(ref symbol) => { + let proto = ctx + .realm + .environment + .get_binding_value("Symbol") + .expect("Symbol was not initialized") + .get_field(PROTOTYPE); + + Ok(Value::new_object_from_prototype( + proto, + ObjectData::Symbol(symbol.clone()), + )) + } + Value::BigInt(ref bigint) => { + let proto = ctx + .realm + .environment + .get_binding_value("BigInt") + .expect("BigInt was not initialized") + .get_field(PROTOTYPE); + let bigint_obj = + Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone())); + Ok(bigint_obj) + } + Value::Object(_) => Ok(self.clone()), + } + } } impl Default for Value { diff --git a/boa/src/exec/call/mod.rs b/boa/src/exec/call/mod.rs index 946c838be38..46a2eaa1568 100644 --- a/boa/src/exec/call/mod.rs +++ b/boa/src/exec/call/mod.rs @@ -11,10 +11,8 @@ impl Executable for Call { 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 { - obj = interpreter - .to_object(&obj) - .expect("failed to convert to object"); + if obj.get_type() != Type::Object { + obj = obj.to_object(interpreter)?; } (obj.clone(), obj.get_field(get_const_field.field())) } diff --git a/boa/src/exec/field/mod.rs b/boa/src/exec/field/mod.rs index be3de9ca9f3..bba610bbbfc 100644 --- a/boa/src/exec/field/mod.rs +++ b/boa/src/exec/field/mod.rs @@ -7,8 +7,8 @@ use crate::{ impl Executable for GetConstField { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { let mut obj = self.obj().run(interpreter)?; - if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol { - obj = interpreter.to_object(&obj)?; + if obj.get_type() != Type::Object { + obj = obj.to_object(interpreter)?; } Ok(obj.get_field(self.field())) @@ -18,8 +18,8 @@ impl Executable for GetConstField { impl Executable for GetField { fn run(&self, interpreter: &mut Interpreter) -> ResultValue { let mut obj = self.obj().run(interpreter)?; - if obj.get_type() != Type::Object || obj.get_type() != Type::Symbol { - obj = interpreter.to_object(&obj)?; + if obj.get_type() != Type::Object { + obj = obj.to_object(interpreter)?; } let field = self.field().run(interpreter)?; diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 90adbe1581b..9f15c8be175 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -443,93 +443,6 @@ impl Interpreter { } } - /// The abstract operation ToObject converts argument to a value of type Object - /// https://tc39.es/ecma262/#sec-toobject - #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_object(&mut self, value: &Value) -> ResultValue { - match value { - Value::Undefined | Value::Null => { - self.throw_type_error("cannot convert 'null' or 'undefined' to object") - } - Value::Boolean(boolean) => { - let proto = self - .realm - .environment - .get_binding_value("Boolean") - .expect("Boolean was not initialized") - .get_field(PROTOTYPE); - - Ok(Value::new_object_from_prototype( - proto, - ObjectData::Boolean(*boolean), - )) - } - Value::Integer(integer) => { - let proto = self - .realm - .environment - .get_binding_value("Number") - .expect("Number was not initialized") - .get_field(PROTOTYPE); - Ok(Value::new_object_from_prototype( - proto, - ObjectData::Number(f64::from(*integer)), - )) - } - Value::Rational(rational) => { - let proto = self - .realm - .environment - .get_binding_value("Number") - .expect("Number was not initialized") - .get_field(PROTOTYPE); - - Ok(Value::new_object_from_prototype( - proto, - ObjectData::Number(*rational), - )) - } - Value::String(ref string) => { - let proto = self - .realm - .environment - .get_binding_value("String") - .expect("String was not initialized") - .get_field(PROTOTYPE); - - Ok(Value::new_object_from_prototype( - proto, - ObjectData::String(string.clone()), - )) - } - Value::Symbol(ref symbol) => { - let proto = self - .realm - .environment - .get_binding_value("Symbol") - .expect("Symbol was not initialized") - .get_field(PROTOTYPE); - - Ok(Value::new_object_from_prototype( - proto, - ObjectData::Symbol(symbol.clone()), - )) - } - Value::BigInt(ref bigint) => { - let proto = self - .realm - .environment - .get_binding_value("BigInt") - .expect("BigInt was not initialized") - .get_field(PROTOTYPE); - let bigint_obj = - Value::new_object_from_prototype(proto, ObjectData::BigInt(bigint.clone())); - Ok(bigint_obj) - } - Value::Object(_) => Ok(value.clone()), - } - } - fn set_value(&mut self, node: &Node, value: Value) -> ResultValue { match node { Node::Identifier(ref name) => { diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index b82dd763274..2b27c2f9e1c 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -1111,11 +1111,14 @@ fn to_object() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); - assert!(engine - .to_object(&Value::undefined()) + assert!(Value::undefined() + .to_object(&mut engine) + .unwrap_err() + .is_object()); + assert!(Value::null() + .to_object(&mut engine) .unwrap_err() .is_object()); - assert!(engine.to_object(&Value::null()).unwrap_err().is_object()); } #[test] From 62b3dd1650f275e1a2f88462fc1850cde53a3ebf Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 20:47:54 +0200 Subject: [PATCH 06/18] Moved `Interpreter::to_property_key()` to `Value::to_property_key()` --- boa/src/builtins/object/mod.rs | 4 ++-- boa/src/builtins/value/mod.rs | 13 +++++++++++++ boa/src/exec/mod.rs | 16 +--------------- boa/src/exec/operator/mod.rs | 4 ++-- 4 files changed, 18 insertions(+), 19 deletions(-) diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index e04d696656b..39d9656a0cb 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -566,11 +566,11 @@ pub fn property_is_enumerable(this: &Value, args: &[Value], ctx: &mut Interprete Some(key) => key, }; - let property_key = ctx.to_property_key(key)?; + let key = key.to_property_key(ctx)?; let own_property = this.to_object(ctx).map(|obj| { obj.as_object() .expect("Unable to deref object") - .get_own_property(&property_key) + .get_own_property(&key) }); Ok(own_property.map_or(Value::from(false), |own_prop| { diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 802949a9292..28b39589c83 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -870,6 +870,19 @@ impl Value { Value::Object(_) => Ok(self.clone()), } } + + /// The abstract operation ToPropertyKey takes argument argument. It converts argument to a value that can be used as a property key. + /// + /// https://tc39.es/ecma262/#sec-topropertykey + pub fn to_property_key(&self, ctx: &mut Interpreter) -> Result { + let key = self.to_primitive(ctx, PreferredType::String)?; + if let Value::Symbol(ref symbol) = key { + Ok(PropertyKey::from(symbol.clone())) + } else { + let string = key.to_string(ctx)?; + Ok(PropertyKey::from(string)) + } + } } impl Default for Value { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 9f15c8be175..2dc2de68c24 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -420,20 +420,6 @@ impl Interpreter { self.throw_type_error("cannot convert object to primitive value") } - /// The abstract operation ToPropertyKey takes argument argument. It converts argument to a value that can be used as a property key. - /// - /// https://tc39.es/ecma262/#sec-topropertykey - #[allow(clippy::wrong_self_convention)] - pub(crate) fn to_property_key(&mut self, value: &Value) -> Result { - let key = value.to_primitive(self, PreferredType::String)?; - if let Value::Symbol(ref symbol) = key { - Ok(PropertyKey::from(symbol.clone())) - } else { - let string = key.to_string(self)?; - Ok(PropertyKey::from(string)) - } - } - /// https://tc39.es/ecma262/#sec-hasproperty pub(crate) fn has_property(&self, obj: &Value, key: &PropertyKey) -> bool { if let Some(obj) = obj.as_object() { @@ -457,7 +443,7 @@ impl Interpreter { .set_field(get_const_field_node.field(), value)), Node::GetField(ref get_field) => { let field = get_field.field().run(self)?; - let key = self.to_property_key(&field)?; + let key = field.to_property_key(self)?; Ok(get_field.obj().run(self)?.set_field(key, value)) } _ => panic!("TypeError: invalid assignment to {}", node), diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index a9ffec70a8a..d2d56678398 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -40,7 +40,7 @@ impl Executable for Assign { Node::GetField(ref get_field) => { let object = get_field.obj().run(interpreter)?; let field = get_field.field().run(interpreter)?; - let key = interpreter.to_property_key(&field)?; + let key = field.to_property_key(interpreter)?; object.set_field(key, val.clone()); } _ => (), @@ -95,7 +95,7 @@ impl Executable for BinOp { y.get_type().as_str() )); } - let key = interpreter.to_property_key(&x)?; + let key = x.to_property_key(interpreter)?; interpreter.has_property(&y, &key) } })) From f2aeb98cc1942bc10e8c7a228400d5613768f687 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 20:57:52 +0200 Subject: [PATCH 07/18] Moved `Interpreter::to_numeric()` to `Value::to_numeric()` --- boa/src/builtins/value/mod.rs | 11 +++++++++++ boa/src/builtins/value/operations.rs | 26 +++++++++++++------------- boa/src/exec/mod.rs | 12 ------------ 3 files changed, 24 insertions(+), 25 deletions(-) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 28b39589c83..9a286dcc4ba 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -883,6 +883,17 @@ impl Value { Ok(PropertyKey::from(string)) } } + + /// It returns value converted to a numeric value of type Number or BigInt. + /// + /// See: https://tc39.es/ecma262/#sec-tonumeric + pub fn to_numeric(&self, ctx: &mut Interpreter) -> ResultValue { + let primitive = self.to_primitive(ctx, PreferredType::Number)?; + if primitive.is_bigint() { + return Ok(primitive); + } + Ok(Value::from(ctx.to_number(self)?)) + } } impl Default for Value { diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index 0ad8932d2f6..d9eee3e6db0 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -27,7 +27,7 @@ impl Value { ) { (Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(ctx)?)), (ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(ctx)?, y)), - (x, y) => match (ctx.to_numeric(&x)?, ctx.to_numeric(&y)?) { + (x, y) => match (x.to_numeric(ctx)?, y.to_numeric(ctx)?) { (Self::Rational(x), Self::Rational(y)) => Self::rational(x + y), (Self::BigInt(ref n1), Self::BigInt(ref n2)) => { Self::bigint(n1.as_inner().clone() + n2.as_inner().clone()) @@ -56,7 +56,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => Self::rational(a - b), (Self::BigInt(ref a), Self::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() - b.as_inner().clone()) @@ -84,7 +84,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => Self::rational(a * b), (Self::BigInt(ref a), Self::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() * b.as_inner().clone()) @@ -112,7 +112,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => Self::rational(a / b), (Self::BigInt(ref a), Self::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() / b.as_inner().clone()) @@ -140,7 +140,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => Self::rational(a % b), (Self::BigInt(ref a), Self::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() % b.as_inner().clone()) @@ -166,7 +166,7 @@ impl Value { (Self::BigInt(ref a), Self::BigInt(ref b)) => Self::bigint(a.as_inner().clone().pow(b)), // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => Self::rational(a.powf(b)), (Self::BigInt(ref a), Self::BigInt(ref b)) => { Self::bigint(a.as_inner().clone().pow(b)) @@ -196,7 +196,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => { Self::integer(f64_to_int32(a) & f64_to_int32(b)) } @@ -228,7 +228,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => { Self::integer(f64_to_int32(a) | f64_to_int32(b)) } @@ -260,7 +260,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(a), Self::Rational(b)) => { Self::integer(f64_to_int32(a) ^ f64_to_int32(b)) } @@ -296,7 +296,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(x), Self::Rational(y)) => { Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y))) } @@ -332,7 +332,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(x), Self::Rational(y)) => { Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y))) } @@ -366,7 +366,7 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { (Self::Rational(x), Self::Rational(y)) => { Self::rational(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y))) } @@ -481,7 +481,7 @@ impl Value { AbstractRelation::Undefined } } - (px, py) => match (ctx.to_numeric(&px)?, ctx.to_numeric(&py)?) { + (px, py) => match (px.to_numeric(ctx)?, py.to_numeric(ctx)?) { (Value::Rational(x), Value::Rational(y)) => Number::less_than(x, y), (Value::BigInt(ref x), Value::BigInt(ref y)) => (x < y).into(), (Value::BigInt(ref x), Value::Rational(y)) => { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 2dc2de68c24..d2990e898d6 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -302,18 +302,6 @@ impl Interpreter { } } - /// It returns value converted to a numeric value of type Number or BigInt. - /// - /// See: https://tc39.es/ecma262/#sec-tonumeric - #[allow(clippy::wrong_self_convention)] - pub fn to_numeric(&mut self, value: &Value) -> ResultValue { - let primitive = value.to_primitive(self, PreferredType::Number)?; - if primitive.is_bigint() { - return Ok(primitive); - } - Ok(Value::from(self.to_number(&primitive)?)) - } - /// This is a more specialized version of `to_numeric`. /// /// It returns value converted to a numeric value of type `Number`. From 803894dd6cb7ab332e881fc9626a25e82892d65b Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 21:09:50 +0200 Subject: [PATCH 08/18] Moved `Interpreter::to_uint32()` to `Value::to_uint32()` --- boa/src/builtins/math/mod.rs | 6 +++--- boa/src/builtins/value/mod.rs | 14 ++++++++++++++ boa/src/exec/mod.rs | 24 +++--------------------- 3 files changed, 20 insertions(+), 24 deletions(-) diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index fa8af32ee13..44469fa0574 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -215,7 +215,7 @@ impl Math { pub(crate) fn clz32(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_uint32(x)) + .map(|x| x.to_uint32(ctx)) .transpose()? .map(u32::leading_zeros) .unwrap_or(32) @@ -353,8 +353,8 @@ impl Math { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul pub(crate) fn imul(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(match ( - args.get(0).map(|x| ctx.to_uint32(x)).transpose()?, - args.get(1).map(|x| ctx.to_uint32(x)).transpose()?, + args.get(0).map(|x| x.to_uint32(ctx)).transpose()?, + args.get(1).map(|x| x.to_uint32(ctx)).transpose()?, ) { (Some(x), Some(y)) => x.wrapping_mul(y) as i32, (_, _) => 0, diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 9a286dcc4ba..645db2d8cf1 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -5,6 +5,7 @@ #[cfg(test)] mod tests; +use super::number::f64_to_uint32; use crate::builtins::{ function::Function, object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE}, @@ -894,6 +895,19 @@ impl Value { } Ok(Value::from(ctx.to_number(self)?)) } + + /// Converts a value to an integral 32 bit unsigned integer. + /// + /// See: https://tc39.es/ecma262/#sec-toint32 + pub fn to_uint32(&self, ctx: &mut Interpreter) -> Result { + // This is the fast path, if the value is Integer we can just return it. + if let Value::Integer(number) = *self { + return Ok(number as u32); + } + let number = ctx.to_number(self)?; + + Ok(f64_to_uint32(number)) + } } impl Default for Value { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index d2990e898d6..8c8126fd09f 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -26,7 +26,7 @@ use crate::{ builtins, builtins::{ function::{Function as FunctionObject, FunctionBody, ThisMode}, - number::{f64_to_int32, f64_to_uint32}, + number::f64_to_int32, object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, value::{PreferredType, ResultValue, Type, Value}, @@ -39,8 +39,6 @@ use crate::{ }, BoaProfiler, }; -use std::borrow::Borrow; -use std::ops::Deref; pub trait Executable { /// Runs this executable in the given executor. @@ -249,20 +247,6 @@ impl Interpreter { Ok(f64_to_int32(number)) } - /// Converts a value to an integral 32 bit unsigned integer. - /// - /// See: https://tc39.es/ecma262/#sec-toint32 - #[allow(clippy::wrong_self_convention)] - pub fn to_uint32(&mut self, value: &Value) -> Result { - // This is the fast path, if the value is Integer we can just return it. - if let Value::Integer(number) = *value { - return Ok(number as u32); - } - let number = self.to_number(value)?; - - Ok(f64_to_uint32(number)) - } - /// Converts argument to an integer suitable for use as the length of an array-like object. /// /// See: https://tc39.es/ecma262/#sec-tolength @@ -322,7 +306,7 @@ impl Interpreter { pub(crate) fn extract_array_properties(&mut self, value: &Value) -> Result, ()> { if let Value::Object(ref x) = value { // Check if object is array - if let ObjectData::Array = x.deref().borrow().data { + if let ObjectData::Array = x.borrow().data { let length = i32::from(&value.get_field("length")); let values = (0..length) .map(|idx| value.get_field(idx.to_string())) @@ -330,9 +314,8 @@ impl Interpreter { return Ok(values); } // Check if object is a Map - else if let ObjectData::Map(ref map) = x.deref().borrow().data { + else if let ObjectData::Map(ref map) = x.borrow().data { let values = map - .borrow() .iter() .map(|(key, value)| { // Construct a new array containing the key-value pair @@ -349,7 +332,6 @@ impl Interpreter { .environment .get_binding_value("Array") .expect("Array was not initialized") - .borrow() .get_field(PROTOTYPE), ); array.set_field("0", key); From 5f0132996ee6a6d9c734dae61d7caee395827d27 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 21:17:07 +0200 Subject: [PATCH 09/18] Moved `Interpreter::to_int32()` to `Value::to_int32()` --- boa/src/builtins/value/mod.rs | 15 ++++++++++++++- boa/src/builtins/value/tests.rs | 14 +++++++------- boa/src/exec/mod.rs | 15 --------------- boa/src/exec/tests.rs | 2 +- 4 files changed, 22 insertions(+), 24 deletions(-) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 645db2d8cf1..75c27fb5a7e 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -5,7 +5,7 @@ #[cfg(test)] mod tests; -use super::number::f64_to_uint32; +use super::number::{f64_to_int32, f64_to_uint32}; use crate::builtins::{ function::Function, object::{GcObject, InternalState, InternalStateCell, Object, ObjectData, PROTOTYPE}, @@ -908,6 +908,19 @@ impl Value { Ok(f64_to_uint32(number)) } + + /// Converts a value to an integral 32 bit signed integer. + /// + /// See: https://tc39.es/ecma262/#sec-toint32 + pub fn to_int32(&self, ctx: &mut Interpreter) -> Result { + // This is the fast path, if the value is Integer we can just return it. + if let Value::Integer(number) = *self { + return Ok(number); + } + let number = ctx.to_number(self)?; + + Ok(f64_to_int32(number)) + } } impl Default for Value { diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 6c86fc631c1..1a667d78a5a 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -254,7 +254,7 @@ fn add_number_and_number() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "1 + 2").unwrap(); - let value = engine.to_int32(&value).unwrap(); + let value = value.to_int32(&mut engine).unwrap(); assert_eq!(value, 3); } @@ -284,7 +284,7 @@ fn add_number_object_and_number() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "new Number(10) + 6").unwrap(); - let value = engine.to_int32(&value).unwrap(); + let value = value.to_int32(&mut engine).unwrap(); assert_eq!(value, 16); } @@ -304,7 +304,7 @@ fn sub_number_and_number() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "1 - 999").unwrap(); - let value = engine.to_int32(&value).unwrap(); + let value = value.to_int32(&mut engine).unwrap(); assert_eq!(value, -998); } @@ -314,7 +314,7 @@ fn sub_number_object_and_number_object() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "new Number(1) - new Number(999)").unwrap(); - let value = engine.to_int32(&value).unwrap(); + let value = value.to_int32(&mut engine).unwrap(); assert_eq!(value, -998); } @@ -334,7 +334,7 @@ fn bitand_integer_and_integer() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "0xFFFF & 0xFF").unwrap(); - let value = engine.to_int32(&value).unwrap(); + let value = value.to_int32(&mut engine).unwrap(); assert_eq!(value, 255); } @@ -344,7 +344,7 @@ fn bitand_integer_and_rational() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "0xFFFF & 255.5").unwrap(); - let value = engine.to_int32(&value).unwrap(); + let value = value.to_int32(&mut engine).unwrap(); assert_eq!(value, 255); } @@ -354,7 +354,7 @@ fn bitand_rational_and_rational() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "255.772 & 255.5").unwrap(); - let value = engine.to_int32(&value).unwrap(); + let value = value.to_int32(&mut engine).unwrap(); assert_eq!(value, 255); } diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 8c8126fd09f..b2aae600ef5 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -26,7 +26,6 @@ use crate::{ builtins, builtins::{ function::{Function as FunctionObject, FunctionBody, ThisMode}, - number::f64_to_int32, object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, value::{PreferredType, ResultValue, Type, Value}, @@ -233,20 +232,6 @@ impl Interpreter { Ok(number.trunc() + 0.0) // We add 0.0 to convert -0.0 to +0.0 } - /// Converts a value to an integral 32 bit signed integer. - /// - /// See: https://tc39.es/ecma262/#sec-toint32 - #[allow(clippy::wrong_self_convention)] - pub fn to_int32(&mut self, value: &Value) -> Result { - // This is the fast path, if the value is Integer we can just return it. - if let Value::Integer(number) = *value { - return Ok(number); - } - let number = self.to_number(value)?; - - Ok(f64_to_int32(number)) - } - /// Converts argument to an integer suitable for use as the length of an array-like object. /// /// See: https://tc39.es/ecma262/#sec-tolength diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 2b27c2f9e1c..078accc66fd 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -966,7 +966,7 @@ fn to_int32() { macro_rules! check_to_int32 { ($from:expr => $to:expr) => { - assert_eq!(engine.to_int32(&Value::number($from)).unwrap(), $to); + assert_eq!(Value::from($from).to_int32(&mut engine).unwrap(), $to); }; }; From 7010e2b732b7bbe271f942393a3769e2dd2b7ff7 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 21:23:23 +0200 Subject: [PATCH 10/18] Moved `Interpreter::to_index()` to `Value::to_index()` --- boa/src/builtins/bigint/mod.rs | 2 +- boa/src/builtins/value/mod.rs | 21 +++++++++++++++++++++ boa/src/exec/mod.rs | 22 ---------------------- boa/src/exec/tests.rs | 4 ++-- 4 files changed, 24 insertions(+), 25 deletions(-) diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index a272a3c3149..89bdbd1582d 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -184,7 +184,7 @@ impl BigInt { let bits_arg = args.get(0).unwrap_or(&undefined_value); let bigint_arg = args.get(1).unwrap_or(&undefined_value); - let bits = ctx.to_index(bits_arg)?; + let bits = bits_arg.to_index(ctx)?; let bits = u32::try_from(bits).unwrap_or(u32::MAX); let bigint = bigint_arg.to_bigint(ctx)?; diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 75c27fb5a7e..d823195e5ba 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -921,6 +921,27 @@ impl Value { Ok(f64_to_int32(number)) } + + /// Converts a value to a non-negative integer if it is a valid integer index value. + /// + /// See: https://tc39.es/ecma262/#sec-toindex + pub fn to_index(&self, ctx: &mut Interpreter) -> Result { + if self.is_undefined() { + return Ok(0); + } + + let integer_index = ctx.to_integer(self)?; + + if integer_index < 0.0 { + return Err(ctx.construct_range_error("Integer index must be >= 0")); + } + + if integer_index > Number::MAX_SAFE_INTEGER { + return Err(ctx.construct_range_error("Integer index must be less than 2**(53) - 1")); + } + + Ok(integer_index as usize) + } } impl Default for Value { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index b2aae600ef5..d061cc638d5 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -187,28 +187,6 @@ impl Interpreter { } } - /// Converts a value to a non-negative integer if it is a valid integer index value. - /// - /// See: https://tc39.es/ecma262/#sec-toindex - #[allow(clippy::wrong_self_convention)] - pub fn to_index(&mut self, value: &Value) -> Result { - if value.is_undefined() { - return Ok(0); - } - - let integer_index = self.to_integer(value)?; - - if integer_index < 0.0 { - return Err(self.construct_range_error("Integer index must be >= 0")); - } - - if integer_index > Number::MAX_SAFE_INTEGER { - return Err(self.construct_range_error("Integer index must be less than 2**(53) - 1")); - } - - Ok(integer_index as usize) - } - /// Converts a value to an integral Number value. /// /// See: https://tc39.es/ecma262/#sec-tointeger diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 078accc66fd..4a7cf7912f5 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -891,8 +891,8 @@ fn to_index() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); - assert_eq!(engine.to_index(&Value::undefined()).unwrap(), 0); - assert!(engine.to_index(&Value::integer(-1)).is_err()); + assert_eq!(Value::undefined().to_index(&mut engine).unwrap(), 0); + assert!(Value::integer(-1).to_index(&mut engine).is_err()); } #[test] From 41948deed167331c50a46ea72a43441656efa057 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 11 Aug 2020 21:33:30 +0200 Subject: [PATCH 11/18] Moved `Interpreter::to_length()` to `Value::to_length()` --- boa/src/builtins/array/mod.rs | 8 ++++---- boa/src/builtins/value/mod.rs | 16 ++++++++++++++++ boa/src/exec/mod.rs | 19 +------------------ boa/src/exec/tests.rs | 22 +++++++++++++--------- 4 files changed, 34 insertions(+), 31 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index aebb879ac25..ab202dc0779 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -979,7 +979,7 @@ impl Array { _ => return interpreter.throw_type_error("Reduce was called without a callback"), }; let initial_value = args.get(1).cloned().unwrap_or_else(Value::undefined); - let mut length = interpreter.to_length(&this.get_field("length"))?; + let mut length = this.get_field("length").to_length(interpreter)?; if length == 0 && initial_value.is_undefined() { return interpreter .throw_type_error("Reduce was called on an empty array and with no initial value"); @@ -1017,7 +1017,7 @@ impl Array { /* We keep track of possibly shortened length in order to prevent unnecessary iteration. It may also be necessary to do this since shortening the array length does not delete array elements. See: https://github.com/boa-dev/boa/issues/557 */ - length = min(length, interpreter.to_length(&this.get_field("length"))?); + length = min(length, this.get_field("length").to_length(interpreter)?); } k += 1; } @@ -1046,7 +1046,7 @@ impl Array { _ => return interpreter.throw_type_error("reduceRight was called without a callback"), }; let initial_value = args.get(1).cloned().unwrap_or_else(Value::undefined); - let mut length = interpreter.to_length(&this.get_field("length"))?; + let mut length = this.get_field("length").to_length(interpreter)?; if length == 0 { if initial_value.is_undefined() { return interpreter.throw_type_error( @@ -1094,7 +1094,7 @@ impl Array { /* We keep track of possibly shortened length in order to prevent unnecessary iteration. It may also be necessary to do this since shortening the array length does not delete array elements. See: https://github.com/boa-dev/boa/issues/557 */ - length = min(length, interpreter.to_length(&this.get_field("length"))?); + length = min(length, this.get_field("length").to_length(interpreter)?); // move k to the last defined element if necessary or return if the length was set to 0 if k >= length { diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index d823195e5ba..37c0f061811 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -942,6 +942,22 @@ impl Value { Ok(integer_index as usize) } + + /// Converts argument to an integer suitable for use as the length of an array-like object. + /// + /// See: https://tc39.es/ecma262/#sec-tolength + pub fn to_length(&self, ctx: &mut Interpreter) -> Result { + // 1. Let len be ? ToInteger(argument). + let len = ctx.to_integer(self)?; + + // 2. If len ≤ +0, return +0. + if len < 0.0 { + return Ok(0); + } + + // 3. Return min(len, 2^53 - 1). + Ok(len.min(Number::MAX_SAFE_INTEGER) as usize) + } } impl Default for Value { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index d061cc638d5..9960fc97814 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -29,7 +29,7 @@ use crate::{ object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, value::{PreferredType, ResultValue, Type, Value}, - Console, Number, + Console, }, realm::Realm, syntax::ast::{ @@ -210,23 +210,6 @@ impl Interpreter { Ok(number.trunc() + 0.0) // We add 0.0 to convert -0.0 to +0.0 } - /// Converts argument to an integer suitable for use as the length of an array-like object. - /// - /// See: https://tc39.es/ecma262/#sec-tolength - #[allow(clippy::wrong_self_convention)] - pub fn to_length(&mut self, value: &Value) -> Result { - // 1. Let len be ? ToInteger(argument). - let len = self.to_integer(value)?; - - // 2. If len ≤ +0, return +0. - if len < 0.0 { - return Ok(0); - } - - // 3. Return min(len, 2^53 - 1). - Ok(len.min(Number::MAX_SAFE_INTEGER) as usize) - } - /// Converts a value to a double precision floating point. /// /// See: https://tc39.es/ecma262/#sec-tonumber diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 4a7cf7912f5..76ae8bf5bb8 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -936,25 +936,29 @@ fn to_length() { let realm = Realm::create(); let mut engine = Interpreter::new(realm); - assert_eq!(engine.to_length(&Value::number(f64::NAN)).unwrap(), 0); + assert_eq!(Value::number(f64::NAN).to_length(&mut engine).unwrap(), 0); assert_eq!( - engine.to_length(&Value::number(f64::NEG_INFINITY)).unwrap(), + Value::number(f64::NEG_INFINITY) + .to_length(&mut engine) + .unwrap(), 0 ); assert_eq!( - engine.to_length(&Value::number(f64::INFINITY)).unwrap(), + Value::number(f64::INFINITY).to_length(&mut engine).unwrap(), Number::MAX_SAFE_INTEGER as usize ); - assert_eq!(engine.to_length(&Value::number(0.0)).unwrap(), 0); - assert_eq!(engine.to_length(&Value::number(-0.0)).unwrap(), 0); - assert_eq!(engine.to_length(&Value::number(20.9)).unwrap(), 20); - assert_eq!(engine.to_length(&Value::number(-20.9)).unwrap(), 0); + assert_eq!(Value::number(0.0).to_length(&mut engine).unwrap(), 0); + assert_eq!(Value::number(-0.0).to_length(&mut engine).unwrap(), 0); + assert_eq!(Value::number(20.9).to_length(&mut engine).unwrap(), 20); + assert_eq!(Value::number(-20.9).to_length(&mut engine).unwrap(), 0); assert_eq!( - engine.to_length(&Value::number(100000000000.0)).unwrap(), + Value::number(100000000000.0) + .to_length(&mut engine) + .unwrap(), 100000000000 ); assert_eq!( - engine.to_length(&Value::number(4010101101.0)).unwrap(), + Value::number(4010101101.0).to_length(&mut engine).unwrap(), 4010101101 ); } From 1a96e082fca5a3166f279fe298bca60346106559 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 12 Aug 2020 18:40:13 +0200 Subject: [PATCH 12/18] Moved `Interpreter::to_integer()` to `Value::to_integer()` --- boa/src/builtins/array/mod.rs | 62 +++++++++++--------- boa/src/builtins/bigint/mod.rs | 2 +- boa/src/builtins/console/mod.rs | 6 +- boa/src/builtins/map/mod.rs | 4 +- boa/src/builtins/number/mod.rs | 14 +++-- boa/src/builtins/regexp/mod.rs | 4 +- boa/src/builtins/string/mod.rs | 83 +++++++++++++++------------ boa/src/builtins/value/conversions.rs | 11 ---- boa/src/builtins/value/display.rs | 38 ++++++------ boa/src/builtins/value/equality.rs | 8 +-- boa/src/builtins/value/mod.rs | 58 +++++++++++-------- boa/src/exec/mod.rs | 25 +------- boa/src/exec/tests.rs | 18 +++--- 13 files changed, 166 insertions(+), 167 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index ab202dc0779..a351fda8f4f 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -69,7 +69,7 @@ impl Array { let array_obj_ptr = array_obj.clone(); // Wipe existing contents of the array object - let orig_length = i32::from(&array_obj.get_field("length")); + let orig_length = array_obj.get_field("length").as_number().unwrap() as i32; for n in 0..orig_length { array_obj_ptr.remove_property(&n.to_string()); } @@ -90,7 +90,7 @@ impl Array { /// Utility function which takes an existing array object and puts additional /// values on the end, correctly rewriting the length pub(crate) fn add_to_array_object(array_ptr: &Value, add_values: &[Value]) -> ResultValue { - let orig_length = i32::from(&array_ptr.get_field("length")); + let orig_length = array_ptr.get_field("length").as_number().unwrap() as i32; for (n, value) in add_values.iter().enumerate() { let new_index = orig_length.wrapping_add(n as i32); @@ -124,7 +124,7 @@ impl Array { let mut length = args.len() as i32; match args.len() { 1 if args[0].is_integer() => { - length = i32::from(&args[0]); + length = args[0].as_number().unwrap() as i32; // TODO: It should not create an array of undefineds, but an empty array ("holy" array in V8) with length `n`. for n in 0..length { this.set_field(n.to_string(), Value::undefined()); @@ -195,13 +195,13 @@ impl Array { // one) let mut new_values: Vec = Vec::new(); - let this_length = i32::from(&this.get_field("length")); + let this_length = this.get_field("length").as_number().unwrap() as i32; for n in 0..this_length { new_values.push(this.get_field(n.to_string())); } for concat_array in args { - let concat_length = i32::from(&concat_array.get_field("length")); + let concat_length = concat_array.get_field("length").as_number().unwrap() as i32; for n in 0..concat_length { new_values.push(concat_array.get_field(n.to_string())); } @@ -238,7 +238,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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let curr_length = i32::from(&this.get_field("length")); + let curr_length = this.get_field("length").as_number().unwrap() as i32; if curr_length < 1 { return Ok(Value::undefined()); } @@ -271,7 +271,7 @@ impl Array { let callback_arg = args.get(0).expect("Could not get `callbackFn` argument."); let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length = i32::from(&this.get_field("length")); + let length = this.get_field("length").as_number().unwrap() as i32; for i in 0..length { let element = this.get_field(i.to_string()); @@ -306,7 +306,7 @@ impl Array { }; let mut elem_strs = Vec::new(); - let length = i32::from(&this.get_field("length")); + let length = this.get_field("length").as_number().unwrap() as i32; for n in 0..length { let elem_str = this.get_field(n.to_string()).to_string(ctx)?.to_string(); elem_strs.push(elem_str); @@ -369,7 +369,7 @@ impl Array { /// [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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let len = i32::from(&this.get_field("length")); + let len = this.get_field("length").as_number().unwrap() as i32; let middle: i32 = len.wrapping_div(2); for lower in 0..middle { @@ -407,7 +407,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: &Value, _: &[Value], _: &mut Interpreter) -> ResultValue { - let len = i32::from(&this.get_field("length")); + let len = this.get_field("length").as_number().unwrap() as i32; if len == 0 { this.set_field("length", 0); @@ -449,7 +449,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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - let len = i32::from(&this.get_field("length")); + let len = this.get_field("length").as_number().unwrap() as i32; let arg_c: i32 = args.len() as i32; if arg_c > 0 { @@ -509,7 +509,7 @@ impl Array { Value::undefined() }; let mut i = 0; - let max_len = i32::from(&this.get_field("length")); + let max_len = this.get_field("length").as_number().unwrap() as i32; let mut len = max_len; while i < len { let element = this.get_field(i.to_string()); @@ -518,7 +518,10 @@ impl Array { if !result.to_boolean() { return Ok(Value::from(false)); } - len = min(max_len, i32::from(&this.get_field("length"))); + len = min( + max_len, + this.get_field("length").as_number().unwrap() as i32, + ); i += 1; } Ok(Value::from(true)) @@ -545,7 +548,7 @@ impl Array { let callback = args.get(0).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")); + let length = this.get_field("length").as_number().unwrap() as i32; let new = Self::new_array(interpreter)?; @@ -589,11 +592,11 @@ impl Array { } let search_element = args[0].clone(); - let len = i32::from(&this.get_field("length")); + let len = this.get_field("length").as_number().unwrap() as i32; let mut idx = match args.get(1) { Some(from_idx_ptr) => { - let from_idx = i32::from(from_idx_ptr); + let from_idx = from_idx_ptr.as_number().unwrap() as i32; if from_idx < 0 { len + from_idx @@ -642,11 +645,11 @@ impl Array { } let search_element = args[0].clone(); - let len = i32::from(&this.get_field("length")); + let len = this.get_field("length").as_number().unwrap() as i32; let mut idx = match args.get(1) { Some(from_idx_ptr) => { - let from_idx = i32::from(from_idx_ptr); + let from_idx = from_idx_ptr.as_number().unwrap() as i32; if from_idx >= 0 { min(from_idx, len - 1) @@ -690,7 +693,7 @@ impl Array { } let callback = &args[0]; let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let len = i32::from(&this.get_field("length")); + let len = this.get_field("length").as_number().unwrap() as i32; for i in 0..len { let element = this.get_field(i.to_string()); let arguments = [element.clone(), Value::from(i), this.clone()]; @@ -729,7 +732,7 @@ impl Array { let this_arg = args.get(1).cloned().unwrap_or_else(Value::undefined); - let length = i32::from(&this.get_field("length")); + let length = this.get_field("length").as_number().unwrap() as i32; for i in 0..length { let element = this.get_field(i.to_string()); @@ -757,7 +760,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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { - let len: i32 = i32::from(&this.get_field("length")); + let len: i32 = this.get_field("length").as_number().unwrap() as i32; let default_value = Value::undefined(); let value = args.get(0).unwrap_or(&default_value); let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32; @@ -798,7 +801,7 @@ impl Array { 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")); + let length = this.get_field("length").as_number().unwrap() as i32; for idx in 0..length { let check_element = this.get_field(idx.to_string()).clone(); @@ -831,14 +834,14 @@ impl Array { interpreter: &mut Interpreter, ) -> ResultValue { let new_array = Self::new_array(interpreter)?; - let len = i32::from(&this.get_field("length")); + let len = this.get_field("length").as_number().unwrap() as i32; let start = match args.get(0) { - Some(v) => i32::from(v), + Some(v) => v.as_number().unwrap() as i32, None => 0, }; let end = match args.get(1) { - Some(v) => i32::from(v), + Some(v) => v.as_number().unwrap() as i32, None => len, }; @@ -888,7 +891,7 @@ impl Array { let callback = args.get(0).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")); + let length = this.get_field("length").as_number().unwrap() as i32; let new = Self::new_array(interpreter)?; @@ -941,7 +944,7 @@ impl Array { Value::undefined() }; let mut i = 0; - let max_len = i32::from(&this.get_field("length")); + let max_len = this.get_field("length").as_number().unwrap() as i32; let mut len = max_len; while i < len { let element = this.get_field(i.to_string()); @@ -951,7 +954,10 @@ impl Array { return Ok(Value::from(true)); } // the length of the array must be updated because the callback can mutate it. - len = min(max_len, i32::from(&this.get_field("length"))); + len = min( + max_len, + this.get_field("length").as_number().unwrap() as i32, + ); i += 1; } Ok(Value::from(false)) diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 89bdbd1582d..d2cdffc8531 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -112,7 +112,7 @@ impl BigInt { #[allow(clippy::wrong_self_convention)] pub(crate) fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let radix = if !args.is_empty() { - args[0].to_integer() + args[0].to_integer(ctx)? as i32 } else { 10 }; diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index f1fe819917a..16452ff2d79 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -74,7 +74,11 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result match fmt { /* integer */ 'd' | 'i' => { - let arg = get_arg_at_index::(data, arg_index).unwrap_or_default(); + let arg = data + .get(arg_index) + .cloned() + .unwrap_or_default() + .to_integer(ctx)?; formatted.push_str(&format!("{}", arg)); arg_index += 1; } diff --git a/boa/src/builtins/map/mod.rs b/boa/src/builtins/map/mod.rs index a6eaaf6033b..9e7a72d2761 100644 --- a/boa/src/builtins/map/mod.rs +++ b/boa/src/builtins/map/mod.rs @@ -215,7 +215,7 @@ impl Map { fn get_key_value(value: &Value) -> Option<(Value, Value)> { if let Value::Object(object) = value { if object.borrow().is_array() { - let (key, value) = match i32::from(&value.get_field("length")) { + let (key, value) = match value.get_field("length").as_number().unwrap() as i32 { 0 => (Value::Undefined, Value::Undefined), 1 => (value.get_field("0"), Value::Undefined), _ => (value.get_field("0"), value.get_field("1")), @@ -250,7 +250,7 @@ impl Map { map } else if object.is_array() { let mut map = OrderedMap::new(); - let len = i32::from(&args[0].get_field("length")); + let len = args[0].get_field("length").to_integer(ctx)? as i32; for i in 0..len { let val = &args[0].get_field(i.to_string()); let (key, value) = Self::get_key_value(val).ok_or_else(|| { diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index 14409c44cf2..a5b799bccce 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -178,8 +178,8 @@ impl Number { 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() { - x if x > 0 => n.to_integer() as usize, + Some(n) => match n.to_integer(ctx)? as i32 { + x if x > 0 => n.to_integer(ctx)? as usize, _ => 0, }, None => 0, @@ -227,8 +227,8 @@ impl Number { let this_num = Self::this_number_value(this, ctx)?; let _num_str_len = format!("{}", this_num).len(); let _precision = match args.get(0) { - Some(n) => match n.to_integer() { - x if x > 0 => n.to_integer() as usize, + Some(n) => match n.to_integer(ctx)? as i32 { + x if x > 0 => n.to_integer(ctx)? as usize, _ => 0, }, None => 0, @@ -383,7 +383,11 @@ impl Number { // 2. If radix is undefined, let radixNumber be 10. // 3. Else, let radixNumber be ? ToInteger(radix). - let radix = args.get(0).map_or(10, |arg| arg.to_integer()) as u8; + let radix = args + .get(0) + .map(|arg| arg.to_integer(ctx)) + .transpose()? + .unwrap_or(10.0) as u8; // 4. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. if radix < 2 || radix > 36 { diff --git a/boa/src/builtins/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index a49285d8ef4..7ce8eb4850d 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -293,7 +293,7 @@ impl RegExp { .get(0) .expect("could not get argument") .to_string(ctx)?; - let mut last_index = usize::from(&this.get_field("lastIndex")); + let mut last_index = this.get_field("lastIndex").to_index(ctx)?; let result = if let Some(object) = this.as_object() { let regex = object.as_regexp().unwrap(); let result = if let Some(m) = regex.matcher.find_at(arg_str.as_str(), last_index) { @@ -332,7 +332,7 @@ impl RegExp { .get(0) .expect("could not get argument") .to_string(ctx)?; - let mut last_index = usize::from(&this.get_field("lastIndex")); + let mut last_index = this.get_field("lastIndex").to_index(ctx)?; let result = if let Some(object) = this.as_object() { let regex = object.as_regexp().unwrap(); let mut locations = regex.matcher.capture_locations(); diff --git a/boa/src/builtins/string/mod.rs b/boa/src/builtins/string/mod.rs index 45eca9cd964..c88f44b6376 100644 --- a/boa/src/builtins/string/mod.rs +++ b/boa/src/builtins/string/mod.rs @@ -112,10 +112,10 @@ impl String { // 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 = this.to_string(ctx)?; - let pos = i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ); + let pos = args + .get(0) + .expect("failed to get argument for String method") + .to_integer(ctx)? as i32; // Calling .len() on a string would give the wrong result, as they are bytes not the number of // unicode code points @@ -158,10 +158,10 @@ impl String { // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. let length = primitive_val.chars().count(); - let pos = i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ); + let pos = args + .get(0) + .expect("failed to get argument for String method") + .to_integer(ctx)? as i32; if pos >= length as i32 || pos < 0 { return Ok(Value::from(NAN)); @@ -217,7 +217,7 @@ impl String { let string = object.to_string(ctx)?; if let Some(arg) = args.get(0) { - let n = ctx.to_integer(arg)?; + let n = arg.to_integer(ctx)?; if n < 0.0 { return ctx.throw_range_error("repeat count cannot be a negative number"); } @@ -251,12 +251,15 @@ impl String { // Then we convert it into a Rust String by wrapping it in from_value let primitive_val = this.to_string(ctx)?; - let start = i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ); + let start = args + .get(0) + .expect("failed to get argument for String method") + .to_integer(ctx)? as i32; - let end = i32::from(args.get(1).expect("failed to get argument in slice")); + let end = args + .get(1) + .expect("failed to get argument in slice") + .to_integer(ctx)? as i32; // Calling .len() on a string would give the wrong result, as they are bytes not the number of unicode code points // Note that this is an O(N) operation (because UTF-8 is complex) while getting the number of bytes is an O(1) operation. @@ -311,7 +314,7 @@ impl String { let position = if args.len() < 2 { 0 } else { - i32::from(args.get(1).expect("failed to get arg")) + args.get(1).expect("failed to get arg").to_integer(ctx)? as i32 }; let start = min(max(position, 0), length); @@ -355,7 +358,9 @@ impl String { let end_position = if args.len() < 2 { length } else { - i32::from(args.get(1).expect("Could not get argumetn")) + args.get(1) + .expect("Could not get argumetn") + .to_integer(ctx)? as i32 }; let end = min(max(end_position, 0), length); @@ -397,7 +402,9 @@ impl String { let position = if args.len() < 2 { 0 } else { - i32::from(args.get(1).expect("Could not get argument")) + args.get(1) + .expect("Could not get argument") + .to_integer(ctx)? as i32 }; let start = min(max(position, 0), length); @@ -556,7 +563,7 @@ impl String { let length = string.chars().count(); let start = args .get(1) - .map(|position| ctx.to_integer(position)) + .map(|position| position.to_integer(ctx)) .transpose()? .map_or(0, |position| position.max(0.0).min(length as f64) as usize); @@ -603,7 +610,7 @@ impl String { let length = string.chars().count(); let start = args .get(1) - .map(|position| ctx.to_integer(position)) + .map(|position| position.to_integer(ctx)) .transpose()? .map_or(0, |position| position.max(0.0).min(length as f64) as usize); @@ -687,10 +694,10 @@ impl String { if args.is_empty() { return Err(Value::from("padEnd requires maxLength argument")); } - let max_length = i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ); + let max_length = args + .get(0) + .expect("failed to get argument for String method") + .to_integer(ctx)? as i32; let fill_string = args.get(1).map(|arg| arg.to_string(ctx)).transpose()?; @@ -714,10 +721,10 @@ impl String { if args.is_empty() { return Err(Value::from("padStart requires maxLength argument")); } - let max_length = i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ); + let max_length = args + .get(0) + .expect("failed to get argument for String method") + .to_integer(ctx)? as i32; let fill_string = args.get(1).map(|arg| arg.to_string(ctx)).transpose()?; @@ -864,17 +871,18 @@ impl String { let start = if args.is_empty() { 0 } else { - i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ) + args.get(0) + .expect("failed to get argument for String method") + .to_integer(ctx)? as i32 }; let length = primitive_val.chars().count() as i32; // If less than 2 args specified, end is the length of the this object converted to a String let end = if args.len() < 2 { length } else { - i32::from(args.get(1).expect("Could not get argument")) + args.get(1) + .expect("Could not get argument") + .to_integer(ctx)? as i32 }; // Both start and end args replaced by 0 if they were negative // or by the length of the String if they were greater @@ -912,10 +920,9 @@ impl String { let mut start = if args.is_empty() { 0 } else { - i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ) + args.get(0) + .expect("failed to get argument for String method") + .to_integer(ctx)? as i32 }; let length = primitive_val.chars().count() as i32; // If less than 2 args specified, end is +infinity, the maximum number value. @@ -925,7 +932,9 @@ impl String { let end = if args.len() < 2 { i32::max_value() } else { - i32::from(args.get(1).expect("Could not get argument")) + args.get(1) + .expect("Could not get argument") + .to_integer(ctx)? as i32 }; // If start is negative it become the number of code units from the end of the string if start < 0 { diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 38e78cc4176..7db9e91ea15 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -108,12 +108,6 @@ impl From for Value { } } -impl From<&Value> for i32 { - fn from(value: &Value) -> i32 { - value.to_integer() - } -} - impl From for Value { fn from(value: BigInt) -> Self { Value::bigint(value) @@ -131,11 +125,6 @@ impl From for Value { Value::integer(value as i32) } } -impl From<&Value> for usize { - fn from(value: &Value) -> usize { - value.to_integer() as Self - } -} impl From for Value { fn from(value: bool) -> Self { diff --git a/boa/src/builtins/value/display.rs b/boa/src/builtins/value/display.rs index 3fff3f5b12e..d7c07a7d933 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -91,15 +91,16 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: } } ObjectData::Array => { - let len = i32::from( - &v.borrow() - .properties() - .get("length") - .expect("Could not get Array's length property") - .value - .clone() - .expect("Could not borrow value"), - ); + let len = v + .borrow() + .properties() + .get("length") + .expect("Could not get Array's length property") + .value + .clone() + .expect("Could not borrow value") + .as_number() + .unwrap() as i32; if print_children { if len == 0 { @@ -131,15 +132,16 @@ pub(crate) fn log_string_from(x: &Value, print_internals: bool, print_children: } } ObjectData::Map(ref map) => { - let size = i32::from( - &v.borrow() - .properties() - .get("size") - .unwrap() - .value - .clone() - .expect("Could not borrow value"), - ); + let size = v + .borrow() + .properties() + .get("size") + .unwrap() + .value + .clone() + .expect("Could not borrow value") + .as_number() + .unwrap() as i32; if size == 0 { return String::from("Map(0)"); } diff --git a/boa/src/builtins/value/equality.rs b/boa/src/builtins/value/equality.rs index ae475c01dbf..73f29e3f5ee 100644 --- a/boa/src/builtins/value/equality.rs +++ b/boa/src/builtins/value/equality.rs @@ -82,14 +82,10 @@ impl Value { }, // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. - (Self::Boolean(_), _) => { - return other.equals(&Value::from(self.to_integer()), interpreter) - } + (Self::Boolean(x), _) => return other.equals(&Value::from(*x as i32), interpreter), // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y). - (_, Self::Boolean(_)) => { - return self.equals(&Value::from(other.to_integer()), interpreter) - } + (_, Self::Boolean(y)) => return self.equals(&Value::from(*y as i32), 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). diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 37c0f061811..0805c040648 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -385,6 +385,15 @@ impl Value { matches!(self, Self::Rational(_) | Self::Integer(_)) } + #[inline] + pub fn as_number(&self) -> Option { + match *self { + Self::Integer(integer) => Some(integer.into()), + Self::Rational(rational) => Some(rational), + _ => None, + } + } + /// Returns true if the value is a string. #[inline] pub fn is_string(&self) -> bool { @@ -445,27 +454,6 @@ impl Value { } } - /// Converts the value into a 32-bit integer - pub fn to_integer(&self) -> i32 { - match *self { - Self::Object(_) - | Self::Undefined - | Self::Symbol(_) - | Self::Null - | Self::Boolean(false) => 0, - Self::String(ref str) => match FromStr::from_str(str) { - Ok(num) => num, - Err(_) => 0, - }, - Self::Rational(num) => num as i32, - Self::Boolean(true) => 1, - Self::Integer(num) => num, - Self::BigInt(_) => { - panic!("TypeError: Cannot mix BigInt and other types, use explicit conversions") - } - } - } - /// Converts the value to a `bool` type. /// /// More information: @@ -652,7 +640,7 @@ impl Value { if obj.borrow().is_array() { if let Ok(num) = string.parse::() { if num > 0 { - let len = i32::from(&self.get_field("length")); + let len = self.get_field("length").as_number().unwrap() as i32; if len < (num + 1) as i32 { self.set_field("length", num + 1); } @@ -930,7 +918,7 @@ impl Value { return Ok(0); } - let integer_index = ctx.to_integer(self)?; + let integer_index = self.to_integer(ctx)?; if integer_index < 0.0 { return Err(ctx.construct_range_error("Integer index must be >= 0")); @@ -948,7 +936,7 @@ impl Value { /// See: https://tc39.es/ecma262/#sec-tolength pub fn to_length(&self, ctx: &mut Interpreter) -> Result { // 1. Let len be ? ToInteger(argument). - let len = ctx.to_integer(self)?; + let len = self.to_integer(ctx)?; // 2. If len ≤ +0, return +0. if len < 0.0 { @@ -958,6 +946,28 @@ impl Value { // 3. Return min(len, 2^53 - 1). Ok(len.min(Number::MAX_SAFE_INTEGER) as usize) } + + /// Converts a value to an integral Number value. + /// + /// See: https://tc39.es/ecma262/#sec-tointeger + pub fn to_integer(&self, ctx: &mut Interpreter) -> Result { + // 1. Let number be ? ToNumber(argument). + let number = ctx.to_number(self)?; + + // 2. If number is +∞ or -∞, return number. + if !number.is_finite() { + // 3. If number is NaN, +0, or -0, return +0. + if number.is_nan() { + return Ok(0.0); + } + return Ok(number); + } + + // 4. Let integer be the Number value that is the same sign as number and whose magnitude is floor(abs(number)). + // 5. If integer is -0, return +0. + // 6. Return integer. + Ok(number.trunc() + 0.0) // We add 0.0 to convert -0.0 to +0.0 + } } impl Default for Value { diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 9960fc97814..2cf52d96590 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -187,29 +187,6 @@ impl Interpreter { } } - /// Converts a value to an integral Number value. - /// - /// See: https://tc39.es/ecma262/#sec-tointeger - #[allow(clippy::wrong_self_convention)] - pub fn to_integer(&mut self, value: &Value) -> Result { - // 1. Let number be ? ToNumber(argument). - let number = self.to_number(value)?; - - // 2. If number is +∞ or -∞, return number. - if !number.is_finite() { - // 3. If number is NaN, +0, or -0, return +0. - if number.is_nan() { - return Ok(0.0); - } - return Ok(number); - } - - // 4. Let integer be the Number value that is the same sign as number and whose magnitude is floor(abs(number)). - // 5. If integer is -0, return +0. - // 6. Return integer. - Ok(number.trunc() + 0.0) // We add 0.0 to convert -0.0 to +0.0 - } - /// Converts a value to a double precision floating point. /// /// See: https://tc39.es/ecma262/#sec-tonumber @@ -253,7 +230,7 @@ impl Interpreter { if let Value::Object(ref x) = value { // Check if object is array if let ObjectData::Array = x.borrow().data { - let length = i32::from(&value.get_field("length")); + let length = value.get_field("length").as_number().unwrap() as i32; let values = (0..length) .map(|idx| value.get_field(idx.to_string())) .collect(); diff --git a/boa/src/exec/tests.rs b/boa/src/exec/tests.rs index 76ae8bf5bb8..50e55ca4fa0 100644 --- a/boa/src/exec/tests.rs +++ b/boa/src/exec/tests.rs @@ -901,32 +901,34 @@ fn to_integer() { let mut engine = Interpreter::new(realm); assert!(Number::equal( - engine.to_integer(&Value::number(f64::NAN)).unwrap(), + Value::number(f64::NAN).to_integer(&mut engine).unwrap(), 0.0 )); assert!(Number::equal( - engine - .to_integer(&Value::number(f64::NEG_INFINITY)) + Value::number(f64::NEG_INFINITY) + .to_integer(&mut engine) .unwrap(), f64::NEG_INFINITY )); assert!(Number::equal( - engine.to_integer(&Value::number(f64::INFINITY)).unwrap(), + Value::number(f64::INFINITY) + .to_integer(&mut engine) + .unwrap(), f64::INFINITY )); assert!(Number::equal( - engine.to_integer(&Value::number(0.0)).unwrap(), + Value::number(0.0).to_integer(&mut engine).unwrap(), 0.0 )); - let number = engine.to_integer(&Value::number(-0.0)).unwrap(); + let number = Value::number(-0.0).to_integer(&mut engine).unwrap(); assert!(!number.is_sign_negative()); assert!(Number::equal(number, 0.0)); assert!(Number::equal( - engine.to_integer(&Value::number(20.9)).unwrap(), + Value::number(20.9).to_integer(&mut engine).unwrap(), 20.0 )); assert!(Number::equal( - engine.to_integer(&Value::number(-20.9)).unwrap(), + Value::number(-20.9).to_integer(&mut engine).unwrap(), -20.0 )); } From ff82661e32c1485157a2b8c2861ee5d1570fbf7b Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 12 Aug 2020 19:36:54 +0200 Subject: [PATCH 13/18] Moved `Interpreter::to_number()` to `Value::to_number()` --- boa/src/builtins/array/mod.rs | 6 +- boa/src/builtins/console/mod.rs | 6 +- boa/src/builtins/date/mod.rs | 30 ++-- boa/src/builtins/json/tests.rs | 20 ++- boa/src/builtins/math/mod.rs | 68 ++++----- boa/src/builtins/math/tests.rs | 196 +++++++++++++------------- boa/src/builtins/number/mod.rs | 8 +- boa/src/builtins/number/tests.rs | 26 ++-- boa/src/builtins/value/conversions.rs | 6 - boa/src/builtins/value/equality.rs | 8 +- boa/src/builtins/value/mod.rs | 58 ++++---- boa/src/builtins/value/tests.rs | 8 +- boa/src/exec/mod.rs | 24 +--- boa/src/exec/operator/mod.rs | 17 ++- 14 files changed, 237 insertions(+), 244 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index a351fda8f4f..33865d8a682 100644 --- a/boa/src/builtins/array/mod.rs +++ b/boa/src/builtins/array/mod.rs @@ -759,16 +759,16 @@ 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: &Value, args: &[Value], _: &mut Interpreter) -> ResultValue { + pub(crate) fn fill(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let len: i32 = this.get_field("length").as_number().unwrap() as i32; let default_value = Value::undefined(); let value = args.get(0).unwrap_or(&default_value); - let relative_start = args.get(1).unwrap_or(&default_value).to_number() as i32; + let relative_start = args.get(1).unwrap_or(&default_value).to_number(ctx)? as i32; let relative_end_val = args.get(2).unwrap_or(&default_value); let relative_end = if relative_end_val.is_undefined() { len } else { - relative_end_val.to_number() as i32 + relative_end_val.to_number(ctx)? as i32 }; let start = if relative_start < 0 { max(len + relative_start, 0) diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 16452ff2d79..980027d8be9 100644 --- a/boa/src/builtins/console/mod.rs +++ b/boa/src/builtins/console/mod.rs @@ -84,7 +84,11 @@ pub fn formatter(data: &[Value], ctx: &mut Interpreter) -> Result } /* float */ 'f' => { - let arg = get_arg_at_index::(data, arg_index).unwrap_or_default(); + let arg = data + .get(arg_index) + .cloned() + .unwrap_or_default() + .to_number(ctx)?; formatted.push_str(&format!("{number:.prec$}", number = arg, prec = 6)); arg_index += 1 } diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index 73def1a674e..4f2ce9c8661 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -312,7 +312,7 @@ impl Date { _ => None, }, tv => { - let tv = ctx.to_number(&tv)?; + let tv = tv.to_number(ctx)?; let secs = (tv / 1_000f64) as i64; let nsecs = ((tv % 1_000f64) * 1_000_000f64) as u32; NaiveDateTime::from_timestamp_opt(secs, nsecs) @@ -340,13 +340,13 @@ impl Date { args: &[Value], ctx: &mut Interpreter, ) -> ResultValue { - let year = ctx.to_number(&args[0])?; - let month = ctx.to_number(&args[1])?; - let day = args.get(2).map_or(Ok(1f64), |value| ctx.to_number(value))?; - let hour = args.get(3).map_or(Ok(0f64), |value| ctx.to_number(value))?; - let min = args.get(4).map_or(Ok(0f64), |value| ctx.to_number(value))?; - let sec = args.get(5).map_or(Ok(0f64), |value| ctx.to_number(value))?; - let milli = args.get(6).map_or(Ok(0f64), |value| ctx.to_number(value))?; + let year = args[0].to_number(ctx)?; + let month = args[1].to_number(ctx)?; + let day = args.get(2).map_or(Ok(1f64), |value| value.to_number(ctx))?; + let hour = args.get(3).map_or(Ok(0f64), |value| value.to_number(ctx))?; + let min = args.get(4).map_or(Ok(0f64), |value| value.to_number(ctx))?; + let sec = args.get(5).map_or(Ok(0f64), |value| value.to_number(ctx))?; + let milli = args.get(6).map_or(Ok(0f64), |value| value.to_number(ctx))?; // If any of the args are infinity or NaN, return an invalid date. if !check_normal_opt!(year, month, day, hour, min, sec, milli) { @@ -1207,13 +1207,13 @@ impl Date { pub(crate) fn utc(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let year = args .get(0) - .map_or(Ok(f64::NAN), |value| ctx.to_number(value))?; - let month = args.get(1).map_or(Ok(1f64), |value| ctx.to_number(value))?; - let day = args.get(2).map_or(Ok(1f64), |value| ctx.to_number(value))?; - let hour = args.get(3).map_or(Ok(0f64), |value| ctx.to_number(value))?; - let min = args.get(4).map_or(Ok(0f64), |value| ctx.to_number(value))?; - let sec = args.get(5).map_or(Ok(0f64), |value| ctx.to_number(value))?; - let milli = args.get(6).map_or(Ok(0f64), |value| ctx.to_number(value))?; + .map_or(Ok(f64::NAN), |value| value.to_number(ctx))?; + let month = args.get(1).map_or(Ok(1f64), |value| value.to_number(ctx))?; + let day = args.get(2).map_or(Ok(1f64), |value| value.to_number(ctx))?; + let hour = args.get(3).map_or(Ok(0f64), |value| value.to_number(ctx))?; + let min = args.get(4).map_or(Ok(0f64), |value| value.to_number(ctx))?; + let sec = args.get(5).map_or(Ok(0f64), |value| value.to_number(ctx))?; + let milli = args.get(6).map_or(Ok(0f64), |value| value.to_number(ctx))?; if !check_normal_opt!(year, month, day, hour, min, sec, milli) { return Ok(Value::number(f64::NAN)); diff --git a/boa/src/builtins/json/tests.rs b/boa/src/builtins/json/tests.rs index cb249f10668..02c661c594b 100644 --- a/boa/src/builtins/json/tests.rs +++ b/boa/src/builtins/json/tests.rs @@ -234,10 +234,22 @@ fn json_parse_array_with_reviver() { }})"#, ) .unwrap(); - assert_eq!(result.get_field("0").to_number() as u8, 2u8); - assert_eq!(result.get_field("1").to_number() as u8, 4u8); - assert_eq!(result.get_field("2").to_number() as u8, 6u8); - assert_eq!(result.get_field("3").to_number() as u8, 8u8); + assert_eq!( + result.get_field("0").to_number(&mut engine).unwrap() as u8, + 2u8 + ); + assert_eq!( + result.get_field("1").to_number(&mut engine).unwrap() as u8, + 4u8 + ); + assert_eq!( + result.get_field("2").to_number(&mut engine).unwrap() as u8, + 6u8 + ); + assert_eq!( + result.get_field("3").to_number(&mut engine).unwrap() as u8, + 8u8 + ); } #[test] diff --git a/boa/src/builtins/math/mod.rs b/boa/src/builtins/math/mod.rs index 44469fa0574..bf7bec64bbb 100644 --- a/boa/src/builtins/math/mod.rs +++ b/boa/src/builtins/math/mod.rs @@ -43,7 +43,7 @@ impl Math { pub(crate) fn abs(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::abs) .into()) @@ -60,7 +60,7 @@ impl Math { pub(crate) fn acos(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::acos) .into()) @@ -77,7 +77,7 @@ impl Math { pub(crate) fn acosh(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::acosh) .into()) @@ -94,7 +94,7 @@ impl Math { pub(crate) fn asin(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::asin) .into()) @@ -111,7 +111,7 @@ impl Math { pub(crate) fn asinh(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::asinh) .into()) @@ -128,7 +128,7 @@ impl Math { pub(crate) fn atan(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::atan) .into()) @@ -145,7 +145,7 @@ impl Math { pub(crate) fn atanh(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::atanh) .into()) @@ -161,8 +161,8 @@ impl Math { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/atan2 pub(crate) fn atan2(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(match ( - args.get(0).map(|x| ctx.to_number(x)).transpose()?, - args.get(1).map(|x| ctx.to_number(x)).transpose()?, + args.get(0).map(|x| x.to_number(ctx)).transpose()?, + args.get(1).map(|x| x.to_number(ctx)).transpose()?, ) { (Some(x), Some(y)) => x.atan2(y), (_, _) => f64::NAN, @@ -181,7 +181,7 @@ impl Math { pub(crate) fn cbrt(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::cbrt) .into()) @@ -198,7 +198,7 @@ impl Math { pub(crate) fn ceil(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::ceil) .into()) @@ -233,7 +233,7 @@ impl Math { pub(crate) fn cos(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::cos) .into()) @@ -250,7 +250,7 @@ impl Math { pub(crate) fn cosh(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::cosh) .into()) @@ -267,7 +267,7 @@ impl Math { pub(crate) fn exp(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::exp) .into()) @@ -286,7 +286,7 @@ impl Math { pub(crate) fn expm1(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::exp_m1) .into()) @@ -303,7 +303,7 @@ impl Math { pub(crate) fn floor(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::floor) .into()) @@ -320,7 +320,7 @@ impl Math { pub(crate) fn fround(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, |x| (x as f32) as f64) .into()) @@ -337,7 +337,7 @@ impl Math { pub(crate) fn hypot(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let mut result = 0f64; for arg in args { - let x = ctx.to_number(arg)?; + let x = arg.to_number(ctx)?; result = result.hypot(x); } Ok(result.into()) @@ -373,7 +373,7 @@ impl Math { pub(crate) fn log(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, |x| if x <= 0.0 { f64::NAN } else { x.ln() }) .into()) @@ -390,7 +390,7 @@ impl Math { pub(crate) fn log1p(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::ln_1p) .into()) @@ -407,7 +407,7 @@ impl Math { pub(crate) fn log10(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, |x| if x <= 0.0 { f64::NAN } else { x.log10() }) .into()) @@ -424,7 +424,7 @@ impl Math { pub(crate) fn log2(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, |x| if x <= 0.0 { f64::NAN } else { x.log2() }) .into()) @@ -441,7 +441,7 @@ impl Math { pub(crate) fn max(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let mut max = f64::NEG_INFINITY; for arg in args { - let num = ctx.to_number(arg)?; + let num = arg.to_number(ctx)?; max = max.max(num); } Ok(max.into()) @@ -458,7 +458,7 @@ impl Math { pub(crate) fn min(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { let mut min = f64::INFINITY; for arg in args { - let num = ctx.to_number(arg)?; + let num = arg.to_number(ctx)?; min = min.min(num); } Ok(min.into()) @@ -474,8 +474,8 @@ impl Math { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/pow pub(crate) fn pow(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(match ( - args.get(0).map(|x| ctx.to_number(x)).transpose()?, - args.get(1).map(|x| ctx.to_number(x)).transpose()?, + args.get(0).map(|x| x.to_number(ctx)).transpose()?, + args.get(1).map(|x| x.to_number(ctx)).transpose()?, ) { (Some(x), Some(y)) => x.powf(y), (_, _) => f64::NAN, @@ -506,7 +506,7 @@ impl Math { pub(crate) fn round(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::round) .into()) @@ -523,7 +523,7 @@ impl Math { pub(crate) fn sign(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or( f64::NAN, @@ -549,7 +549,7 @@ impl Math { pub(crate) fn sin(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::sin) .into()) @@ -566,7 +566,7 @@ impl Math { pub(crate) fn sinh(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::sinh) .into()) @@ -583,7 +583,7 @@ impl Math { pub(crate) fn sqrt(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::sqrt) .into()) @@ -600,7 +600,7 @@ impl Math { pub(crate) fn tan(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::tan) .into()) @@ -617,7 +617,7 @@ impl Math { pub(crate) fn tanh(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::tanh) .into()) @@ -634,7 +634,7 @@ impl Math { pub(crate) fn trunc(_: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { Ok(args .get(0) - .map(|x| ctx.to_number(x)) + .map(|x| x.to_number(ctx)) .transpose()? .map_or(f64::NAN, f64::trunc) .into()) diff --git a/boa/src/builtins/math/tests.rs b/boa/src/builtins/math/tests.rs index 3eb3204ef12..b0848284329 100644 --- a/boa/src/builtins/math/tests.rs +++ b/boa/src/builtins/math/tests.rs @@ -17,8 +17,8 @@ fn abs() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 2.0); - assert_eq!(b.to_number(), 6.655_559_999_999_999_5); + assert_eq!(a.to_number(&mut engine).unwrap(), 2.0); + assert_eq!(b.to_number(&mut engine).unwrap(), 6.655_559_999_999_999_5); } #[test] @@ -39,10 +39,10 @@ fn acos() { let c = forward_val(&mut engine, "c").unwrap(); let d = forward(&mut engine, "d"); - assert_eq!(a.to_number(), 0.643_501_108_793_284_3); - assert_eq!(b, String::from("NaN")); - assert_eq!(c.to_number(), 0_f64); - assert_eq!(d, String::from("NaN")); + assert_eq!(a.to_number(&mut engine).unwrap(), 0.643_501_108_793_284_3); + assert_eq!(b, "NaN"); + assert_eq!(c.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(d, "NaN"); } #[test] @@ -61,9 +61,9 @@ fn acosh() { let b = forward(&mut engine, "b"); let c = forward(&mut engine, "c"); - assert_eq!(a.to_number(), 1.316_957_896_924_816_6); - assert_eq!(b, String::from("NaN")); - assert_eq!(c, String::from("NaN")); + assert_eq!(a.to_number(&mut engine).unwrap(), 1.316_957_896_924_816_6); + assert_eq!(b, "NaN"); + assert_eq!(c, "NaN"); } #[test] @@ -80,7 +80,7 @@ fn asin() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward(&mut engine, "b"); - assert_eq!(a.to_number(), 0.643_501_108_793_284_4); + assert_eq!(a.to_number(&mut engine).unwrap(), 0.643_501_108_793_284_4); assert_eq!(b, String::from("NaN")); } @@ -98,8 +98,8 @@ fn asinh() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 0.881_373_587_019_542_9); - assert_eq!(b.to_number(), 0_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 0.881_373_587_019_542_9); + assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64); } #[test] @@ -118,9 +118,9 @@ fn atan() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), f64::consts::FRAC_PI_4); - assert_eq!(b.to_number(), 0_f64); - assert_eq!(c.to_number(), f64::from(-0)); + assert_eq!(a.to_number(&mut engine).unwrap(), f64::consts::FRAC_PI_4); + assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), f64::from(-0)); } #[test] @@ -137,8 +137,8 @@ fn atan2() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 1.405_647_649_380_269_9); - assert_eq!(b.to_number(), 0.165_148_677_414_626_83); + assert_eq!(a.to_number(&mut engine).unwrap(), 1.405_647_649_380_269_9); + assert_eq!(b.to_number(&mut engine).unwrap(), 0.165_148_677_414_626_83); } #[test] @@ -157,9 +157,9 @@ fn cbrt() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 4_f64); - assert_eq!(b.to_number(), -1_f64); - assert_eq!(c.to_number(), 1_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 4_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), -1_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), 1_f64); } #[test] @@ -178,9 +178,9 @@ fn ceil() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 2_f64); - assert_eq!(b.to_number(), 4_f64); - assert_eq!(c.to_number(), -7_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 2_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 4_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), -7_f64); } #[test] @@ -210,14 +210,14 @@ fn clz32() { let g = forward_val(&mut engine, "g").unwrap(); let h = forward_val(&mut engine, "h").unwrap(); - assert_eq!(a.to_number(), 32_f64); - assert_eq!(b.to_number(), 32_f64); - assert_eq!(c.to_number(), 0_f64); - assert_eq!(d.to_number(), 31_f64); - assert_eq!(e.to_number(), 1_f64); - assert_eq!(f.to_number(), 32_f64); - assert_eq!(g.to_number(), 31_f64); - assert_eq!(h.to_number(), 32_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 32_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 32_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(d.to_number(&mut engine).unwrap(), 31_f64); + assert_eq!(e.to_number(&mut engine).unwrap(), 1_f64); + assert_eq!(f.to_number(&mut engine).unwrap(), 32_f64); + assert_eq!(g.to_number(&mut engine).unwrap(), 31_f64); + assert_eq!(h.to_number(&mut engine).unwrap(), 32_f64); } #[test] @@ -234,8 +234,8 @@ fn cos() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 1_f64); - assert_eq!(b.to_number(), 0.540_302_305_868_139_8); + assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 0.540_302_305_868_139_8); } #[test] @@ -254,9 +254,9 @@ fn cosh() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 1_f64); - assert_eq!(b.to_number(), 1.543_080_634_815_243_7); - assert_eq!(c.to_number(), 1.543_080_634_815_243_7); + assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 1.543_080_634_815_243_7); + assert_eq!(c.to_number(&mut engine).unwrap(), 1.543_080_634_815_243_7); } #[test] @@ -275,9 +275,9 @@ fn exp() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 1_f64); - assert_eq!(b.to_number(), 0.367_879_441_171_442_33); - assert_eq!(c.to_number(), 7.389_056_098_930_65); + assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 0.367_879_441_171_442_33); + assert_eq!(c.to_number(&mut engine).unwrap(), 7.389_056_098_930_65); } #[test] @@ -305,10 +305,10 @@ fn expm1() { assert_eq!(a, String::from("NaN")); assert_eq!(b, String::from("NaN")); - assert_eq!(c.to_number(), 1.718_281_828_459_045); - assert_eq!(d.to_number(), -0.632_120_558_828_557_7); - assert_eq!(e.to_number(), 0_f64); - assert_eq!(f.to_number(), 6.389_056_098_930_65); + assert_eq!(c.to_number(&mut engine).unwrap(), 1.718_281_828_459_045); + assert_eq!(d.to_number(&mut engine).unwrap(), -0.632_120_558_828_557_7); + assert_eq!(e.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(f.to_number(&mut engine).unwrap(), 6.389_056_098_930_65); } #[test] @@ -327,9 +327,9 @@ fn floor() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 1_f64); - assert_eq!(b.to_number(), -4_f64); - assert_eq!(c.to_number(), 3_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), -4_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), 3_f64); } #[test] @@ -359,10 +359,10 @@ fn fround() { assert_eq!(a, String::from("NaN")); assert_eq!(b, String::from("Infinity")); - assert_eq!(c.to_number(), 5f64); - assert_eq!(d.to_number(), 5.5f64); - assert_eq!(e.to_number(), 5.050_000_190_734_863); - assert_eq!(f.to_number(), -5.050_000_190_734_863); + assert_eq!(c.to_number(&mut engine).unwrap(), 5f64); + assert_eq!(d.to_number(&mut engine).unwrap(), 5.5f64); + assert_eq!(e.to_number(&mut engine).unwrap(), 5.050_000_190_734_863); + assert_eq!(f.to_number(&mut engine).unwrap(), -5.050_000_190_734_863); assert_eq!(g, String::from("NaN")); } @@ -391,13 +391,13 @@ fn hypot() { let f = forward_val(&mut engine, "f").unwrap(); let g = forward_val(&mut engine, "g").unwrap(); - assert_eq!(a.to_number(), 0f64); - assert_eq!(b.to_number(), 5f64); - assert_eq!(c.to_number(), 13f64); - assert_eq!(d.to_number(), 7.071_067_811_865_475_5); - assert_eq!(e.to_number(), 8.774964387392123); - assert!(f.to_number().is_infinite()); - assert_eq!(g.to_number(), 12f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 0f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 5f64); + assert_eq!(c.to_number(&mut engine).unwrap(), 13f64); + assert_eq!(d.to_number(&mut engine).unwrap(), 7.071_067_811_865_475_5); + assert_eq!(e.to_number(&mut engine).unwrap(), 8.774964387392123); + assert!(f.to_number(&mut engine).unwrap().is_infinite()); + assert_eq!(g.to_number(&mut engine).unwrap(), 12f64); } #[test] @@ -423,12 +423,12 @@ fn imul() { let e = forward_val(&mut engine, "e").unwrap(); let f = forward_val(&mut engine, "f").unwrap(); - assert_eq!(a.to_number(), 12f64); - assert_eq!(b.to_number(), -60f64); - assert_eq!(c.to_number(), -5f64); - assert_eq!(d.to_number(), -10f64); - assert_eq!(e.to_number(), 0f64); - assert_eq!(f.to_number(), 0f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 12f64); + assert_eq!(b.to_number(&mut engine).unwrap(), -60f64); + assert_eq!(c.to_number(&mut engine).unwrap(), -5f64); + assert_eq!(d.to_number(&mut engine).unwrap(), -10f64); + assert_eq!(e.to_number(&mut engine).unwrap(), 0f64); + assert_eq!(f.to_number(&mut engine).unwrap(), 0f64); } #[test] @@ -447,8 +447,8 @@ fn log() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward(&mut engine, "c"); - assert_eq!(a.to_number(), 0_f64); - assert_eq!(b.to_number(), f64::consts::LN_10); + assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), f64::consts::LN_10); assert_eq!(c, String::from("NaN")); } @@ -477,9 +477,9 @@ fn log1p() { let f = forward(&mut engine, "f"); let g = forward(&mut engine, "g"); - assert_eq!(a.to_number(), f64::consts::LN_2); - assert_eq!(b.to_number(), 0f64); - assert_eq!(c.to_number(), -36.736_800_569_677_1); + assert_eq!(a.to_number(&mut engine).unwrap(), f64::consts::LN_2); + assert_eq!(b.to_number(&mut engine).unwrap(), 0f64); + assert_eq!(c.to_number(&mut engine).unwrap(), -36.736_800_569_677_1); assert_eq!(d, "-Infinity"); assert_eq!(e, String::from("NaN")); assert_eq!(f, String::from("NaN")); @@ -502,8 +502,8 @@ fn log10() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward(&mut engine, "c"); - assert_eq!(a.to_number(), f64::consts::LOG10_2); - assert_eq!(b.to_number(), 0_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), f64::consts::LOG10_2); + assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64); assert_eq!(c, String::from("NaN")); } @@ -523,8 +523,8 @@ fn log2() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward(&mut engine, "c"); - assert_eq!(a.to_number(), 1.584_962_500_721_156); - assert_eq!(b.to_number(), 0_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 1.584_962_500_721_156); + assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64); assert_eq!(c, String::from("NaN")); } @@ -544,9 +544,9 @@ fn max() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 20_f64); - assert_eq!(b.to_number(), -10_f64); - assert_eq!(c.to_number(), 20_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 20_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), -10_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), 20_f64); } #[test] @@ -565,9 +565,9 @@ fn min() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 10_f64); - assert_eq!(b.to_number(), -20_f64); - assert_eq!(c.to_number(), -10_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 10_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), -20_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), -10_f64); } #[test] @@ -588,10 +588,10 @@ fn pow() { let c = forward_val(&mut engine, "c").unwrap(); let d = forward_val(&mut engine, "d").unwrap(); - assert_eq!(a.to_number(), 1_024_f64); - assert_eq!(b.to_number(), 49_f64); - assert_eq!(c.to_number(), 2.0); - assert_eq!(d.to_number(), 0.020_408_163_265_306_12); + assert_eq!(a.to_number(&mut engine).unwrap(), 1_024_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 49_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), 2.0); + assert_eq!(d.to_number(&mut engine).unwrap(), 0.020_408_163_265_306_12); } #[test] @@ -608,8 +608,8 @@ fn round() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 21.0); - assert_eq!(b.to_number(), -20.0); + assert_eq!(a.to_number(&mut engine).unwrap(), 21.0); + assert_eq!(b.to_number(&mut engine).unwrap(), -20.0); } #[test] @@ -628,9 +628,9 @@ fn sign() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 1_f64); - assert_eq!(b.to_number(), -1_f64); - assert_eq!(c.to_number(), 0_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 1_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), -1_f64); + assert_eq!(c.to_number(&mut engine).unwrap(), 0_f64); } #[test] @@ -647,8 +647,8 @@ fn sin() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 0_f64); - assert_eq!(b.to_number(), 0.841_470_984_807_896_5); + assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 0.841_470_984_807_896_5); } #[test] @@ -665,8 +665,8 @@ fn sinh() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 0_f64); - assert_eq!(b.to_number(), 1.175_201_193_643_801_4); + assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 1.175_201_193_643_801_4); } #[test] @@ -685,9 +685,9 @@ fn sqrt() { let b = forward_val(&mut engine, "b").unwrap(); let c = forward_val(&mut engine, "c").unwrap(); - assert_eq!(a.to_number(), 0_f64); - assert_eq!(b.to_number(), f64::consts::SQRT_2); - assert_eq!(c.to_number(), 3_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), f64::consts::SQRT_2); + assert_eq!(c.to_number(&mut engine).unwrap(), 3_f64); } // TODO: Precision is always off between ci and local. We proably need a better way to compare floats anyways @@ -721,8 +721,8 @@ fn tanh() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 0.761_594_155_955_764_9); - assert_eq!(b.to_number(), 0_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 0.761_594_155_955_764_9); + assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64); } #[test] @@ -739,6 +739,6 @@ fn trunc() { let a = forward_val(&mut engine, "a").unwrap(); let b = forward_val(&mut engine, "b").unwrap(); - assert_eq!(a.to_number(), 13_f64); - assert_eq!(b.to_number(), 0_f64); + assert_eq!(a.to_number(&mut engine).unwrap(), 13_f64); + assert_eq!(b.to_number(&mut engine).unwrap(), 0_f64); } diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index a5b799bccce..a8dd52f5404 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -567,8 +567,8 @@ impl Number { args: &[Value], ctx: &mut Interpreter, ) -> ResultValue { - if let Some(val) = args.get(0) { - let number = ctx.to_number(val)?; + if let Some(value) = args.get(0) { + let number = value.to_number(ctx)?; Ok(number.is_finite().into()) } else { Ok(false.into()) @@ -594,8 +594,8 @@ impl Number { args: &[Value], ctx: &mut Interpreter, ) -> ResultValue { - if let Some(val) = args.get(0) { - let number = ctx.to_number(val)?; + if let Some(value) = args.get(0) { + let number = value.to_number(ctx)?; Ok(number.is_nan().into()) } else { Ok(true.into()) diff --git a/boa/src/builtins/number/tests.rs b/boa/src/builtins/number/tests.rs index 5224b83204d..2f95a928e7d 100644 --- a/boa/src/builtins/number/tests.rs +++ b/boa/src/builtins/number/tests.rs @@ -39,14 +39,14 @@ fn call_number() { let invalid_nan = forward_val(&mut engine, "invalid_nan").unwrap(); let from_exp = forward_val(&mut engine, "from_exp").unwrap(); - assert_eq!(default_zero.to_number(), 0_f64); - assert_eq!(int_one.to_number(), 1_f64); - assert_eq!(float_two.to_number(), 2.1); - assert_eq!(str_three.to_number(), 3.2); - assert_eq!(bool_one.to_number(), 1_f64); - assert!(invalid_nan.to_number().is_nan()); - assert_eq!(bool_zero.to_number(), 0_f64); - assert_eq!(from_exp.to_number(), 234_f64); + assert_eq!(default_zero.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(int_one.to_number(&mut engine).unwrap(), 1_f64); + assert_eq!(float_two.to_number(&mut engine).unwrap(), 2.1); + assert_eq!(str_three.to_number(&mut engine).unwrap(), 3.2); + assert_eq!(bool_one.to_number(&mut engine).unwrap(), 1_f64); + assert!(invalid_nan.to_number(&mut engine).unwrap().is_nan()); + assert_eq!(bool_zero.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(from_exp.to_number(&mut engine).unwrap(), 234_f64); } #[test] @@ -396,11 +396,11 @@ fn value_of() { let exp_val = forward_val(&mut engine, "exp_val").unwrap(); let neg_val = forward_val(&mut engine, "neg_val").unwrap(); - assert_eq!(default_val.to_number(), 0_f64); - assert_eq!(int_val.to_number(), 123_f64); - assert_eq!(float_val.to_number(), 1.234); - assert_eq!(exp_val.to_number(), 12_000_f64); - assert_eq!(neg_val.to_number(), -12_000_f64); + assert_eq!(default_val.to_number(&mut engine).unwrap(), 0_f64); + assert_eq!(int_val.to_number(&mut engine).unwrap(), 123_f64); + assert_eq!(float_val.to_number(&mut engine).unwrap(), 1.234); + assert_eq!(exp_val.to_number(&mut engine).unwrap(), 12_000_f64); + assert_eq!(neg_val.to_number(&mut engine).unwrap(), -12_000_f64); } #[test] diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 7db9e91ea15..97b46296151 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -85,12 +85,6 @@ impl From for Value { } } -impl From<&Value> for f64 { - fn from(value: &Value) -> Self { - value.to_number() - } -} - impl From for Value { #[inline] fn from(value: u32) -> Value { diff --git a/boa/src/builtins/value/equality.rs b/boa/src/builtins/value/equality.rs index 73f29e3f5ee..63f9b96f189 100644 --- a/boa/src/builtins/value/equality.rs +++ b/boa/src/builtins/value/equality.rs @@ -1,8 +1,6 @@ use super::*; use crate::{builtins::Number, Interpreter}; -use std::borrow::Borrow; - impl Value { /// Strict equality comparison. /// @@ -61,9 +59,9 @@ impl Value { | (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)) + let x = self.to_number(interpreter)?; + let y = other.to_number(interpreter)?; + Number::equal(x, y) } // 6. If Type(x) is BigInt and Type(y) is String, then diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 0805c040648..62d79f43f98 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -430,30 +430,6 @@ impl Value { } } - /// Converts the value into a 64-bit floating point number - pub fn to_number(&self) -> f64 { - match *self { - Self::Object(_) | Self::Symbol(_) | Self::Undefined => NAN, - Self::String(ref str) => { - if str.is_empty() { - return 0.0; - } - - match FromStr::from_str(str) { - Ok(num) => num, - Err(_) => NAN, - } - } - Self::Boolean(true) => 1.0, - Self::Boolean(false) | Self::Null => 0.0, - Self::Rational(num) => num, - Self::Integer(num) => f64::from(num), - Self::BigInt(_) => { - panic!("TypeError: Cannot mix BigInt and other types, use explicit conversions") - } - } - } - /// Converts the value to a `bool` type. /// /// More information: @@ -881,7 +857,7 @@ impl Value { if primitive.is_bigint() { return Ok(primitive); } - Ok(Value::from(ctx.to_number(self)?)) + Ok(self.to_number(ctx)?.into()) } /// Converts a value to an integral 32 bit unsigned integer. @@ -892,7 +868,7 @@ impl Value { if let Value::Integer(number) = *self { return Ok(number as u32); } - let number = ctx.to_number(self)?; + let number = self.to_number(ctx)?; Ok(f64_to_uint32(number)) } @@ -905,7 +881,7 @@ impl Value { if let Value::Integer(number) = *self { return Ok(number); } - let number = ctx.to_number(self)?; + let number = self.to_number(ctx)?; Ok(f64_to_int32(number)) } @@ -952,7 +928,7 @@ impl Value { /// See: https://tc39.es/ecma262/#sec-tointeger pub fn to_integer(&self, ctx: &mut Interpreter) -> Result { // 1. Let number be ? ToNumber(argument). - let number = ctx.to_number(self)?; + let number = self.to_number(ctx)?; // 2. If number is +∞ or -∞, return number. if !number.is_finite() { @@ -968,6 +944,32 @@ impl Value { // 6. Return integer. Ok(number.trunc() + 0.0) // We add 0.0 to convert -0.0 to +0.0 } + + /// Converts a value to a double precision floating point. + /// + /// See: https://tc39.es/ecma262/#sec-tonumber + pub fn to_number(&self, ctx: &mut Interpreter) -> Result { + match *self { + 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 + Value::String(ref string) => { + if string.trim().is_empty() { + return Ok(0.0); + } + Ok(string.parse().unwrap_or(f64::NAN)) + } + Value::Rational(number) => Ok(number), + Value::Integer(integer) => Ok(f64::from(integer)), + Value::Symbol(_) => Err(ctx.construct_type_error("argument must not be a symbol")), + Value::BigInt(_) => Err(ctx.construct_type_error("argument must not be a bigint")), + Value::Object(_) => { + let primitive = self.to_primitive(ctx, PreferredType::Number)?; + primitive.to_number(ctx) + } + } + } } impl Default for Value { diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 1a667d78a5a..acec8d475ec 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -324,7 +324,7 @@ fn sub_string_and_number_object() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "'Hello' - new Number(999)").unwrap(); - let value = engine.to_number(&value).unwrap(); + let value = value.to_number(&mut engine).unwrap(); assert!(value.is_nan()); } @@ -365,7 +365,7 @@ fn pow_number_and_number() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "3 ** 3").unwrap(); - let value = engine.to_number(&value).unwrap(); + let value = value.to_number(&mut engine).unwrap(); assert_eq!(value, 27.0); } @@ -375,7 +375,7 @@ fn pow_number_and_string() { let mut engine = Interpreter::new(realm); let value = forward_val(&mut engine, "3 ** 'Hello'").unwrap(); - let value = engine.to_number(&value).unwrap(); + let value = value.to_number(&mut engine).unwrap(); assert!(value.is_nan()); } @@ -393,7 +393,7 @@ fn assign_pow_number_and_string() { ", ) .unwrap(); - let value = engine.to_number(&value).unwrap(); + let value = value.to_number(&mut engine).unwrap(); assert!(value.is_nan()); } diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 2cf52d96590..05915294f72 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -187,28 +187,6 @@ impl Interpreter { } } - /// Converts a value to a double precision floating point. - /// - /// See: https://tc39.es/ecma262/#sec-tonumber - #[allow(clippy::wrong_self_convention)] - pub fn to_number(&mut self, value: &Value) -> Result { - 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 - 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 = value.to_primitive(self, PreferredType::Number)?; - self.to_number(&primitive) - } - } - } - /// This is a more specialized version of `to_numeric`. /// /// It returns value converted to a numeric value of type `Number`. @@ -220,7 +198,7 @@ impl Interpreter { if let Some(ref bigint) = primitive.as_bigint() { return Ok(bigint.to_f64()); } - Ok(self.to_number(&primitive)?) + primitive.to_number(self) } /// Converts an array object into a rust vector of values. diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index d2d56678398..9b6c0ff5532 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -173,30 +173,35 @@ impl Executable for UnaryOp { Ok(match self.op() { op::UnaryOp::Minus => x.neg(interpreter)?, - op::UnaryOp::Plus => Value::from(x.to_number()), + op::UnaryOp::Plus => Value::from(x.to_number(interpreter)?), op::UnaryOp::IncrementPost => { let ret = x.clone(); - interpreter.set_value(self.target(), Value::from(x.to_number() + 1.0))?; + let result = x.to_number(interpreter)? + 1.0; + interpreter.set_value(self.target(), result.into())?; ret } op::UnaryOp::IncrementPre => { - interpreter.set_value(self.target(), Value::from(x.to_number() + 1.0))? + let result = x.to_number(interpreter)? + 1.0; + interpreter.set_value(self.target(), result.into())? } op::UnaryOp::DecrementPost => { let ret = x.clone(); - interpreter.set_value(self.target(), Value::from(x.to_number() - 1.0))?; + let result = x.to_number(interpreter)? - 1.0; + interpreter.set_value(self.target(), result.into())?; ret } op::UnaryOp::DecrementPre => { - interpreter.set_value(self.target(), Value::from(x.to_number() - 1.0))? + let result = x.to_number(interpreter)? - 1.0; + interpreter.set_value(self.target(), result.into())? } op::UnaryOp::Not => x.not(interpreter)?, op::UnaryOp::Tilde => { - let num_v_a = x.to_number(); + let num_v_a = x.to_number(interpreter)?; // NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184 Value::from(if num_v_a.is_nan() { -1 } else { + // TODO: this is not spec compliant. !(num_v_a as i32) }) } From 9ae1dc7ce34586fbff34b3e57c9ffb446a3e70dc Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 12 Aug 2020 19:57:17 +0200 Subject: [PATCH 14/18] Moved `Interpreter::to_numeric_number()` to `Value::to_numeric_number()` --- boa/src/builtins/date/mod.rs | 2 +- boa/src/builtins/number/mod.rs | 2 +- boa/src/builtins/value/mod.rs | 13 +++++++++++++ boa/src/builtins/value/operations.rs | 6 +++--- boa/src/exec/mod.rs | 14 -------------- boa/src/exec/operator/mod.rs | 3 +-- 6 files changed, 19 insertions(+), 21 deletions(-) diff --git a/boa/src/builtins/date/mod.rs b/boa/src/builtins/date/mod.rs index 4f2ce9c8661..06d62a8f4fe 100644 --- a/boa/src/builtins/date/mod.rs +++ b/boa/src/builtins/date/mod.rs @@ -62,7 +62,7 @@ macro_rules! setter_method { args .get($e) .and_then(|value| { - ctx.to_numeric_number(value).map_or_else( + value.to_numeric_number(ctx).map_or_else( |_| None, |value| { if value == 0f64 || value.is_normal() { diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index a8dd52f5404..ef5c874e2e6 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -135,7 +135,7 @@ impl Number { /// `[[Call]]` - Creates a number primitive 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)?, + Some(ref value) => value.to_numeric_number(ctx)?, None => 0.0, }; this.set_data(ObjectData::Number(data)); diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 62d79f43f98..788b7e61c3b 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -970,6 +970,19 @@ impl Value { } } } + + /// This is a more specialized version of `to_numeric`. + /// + /// It returns value converted to a numeric value of type `Number`. + /// + /// See: https://tc39.es/ecma262/#sec-tonumeric + pub fn to_numeric_number(&self, ctx: &mut Interpreter) -> Result { + let primitive = self.to_primitive(ctx, PreferredType::Number)?; + if let Some(ref bigint) = primitive.as_bigint() { + return Ok(bigint.to_f64()); + } + primitive.to_number(ctx) + } } impl Default for Value { diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index d9eee3e6db0..d0a00bd0203 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -387,7 +387,7 @@ impl Value { pub fn neg(&self, interpreter: &mut Interpreter) -> ResultValue { Ok(match *self { Self::Symbol(_) | Self::Undefined => Self::rational(NAN), - Self::Object(_) => Self::rational(match interpreter.to_numeric_number(self) { + Self::Object(_) => Self::rational(match self.to_numeric_number(interpreter) { Ok(num) => -num, Err(_) => NAN, }), @@ -404,8 +404,8 @@ impl Value { } #[inline] - pub fn not(&self, _: &mut Interpreter) -> ResultValue { - Ok(Self::boolean(!self.to_boolean())) + pub fn not(&self, _: &mut Interpreter) -> Result { + Ok(!self.to_boolean()) } /// Abstract relational comparison diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index 05915294f72..5f923ea5c15 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -187,20 +187,6 @@ impl Interpreter { } } - /// This is a more specialized version of `to_numeric`. - /// - /// It returns value converted to a numeric value of type `Number`. - /// - /// 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 = value.to_primitive(self, PreferredType::Number)?; - if let Some(ref bigint) = primitive.as_bigint() { - return Ok(bigint.to_f64()); - } - primitive.to_number(self) - } - /// Converts an array object into a rust vector of values. /// /// This is useful for the spread operator, for any other object an `Err` is returned diff --git a/boa/src/exec/operator/mod.rs b/boa/src/exec/operator/mod.rs index 9b6c0ff5532..05efcdfb053 100644 --- a/boa/src/exec/operator/mod.rs +++ b/boa/src/exec/operator/mod.rs @@ -194,10 +194,9 @@ impl Executable for UnaryOp { let result = x.to_number(interpreter)? - 1.0; interpreter.set_value(self.target(), result.into())? } - op::UnaryOp::Not => x.not(interpreter)?, + op::UnaryOp::Not => x.not(interpreter)?.into(), op::UnaryOp::Tilde => { let num_v_a = x.to_number(interpreter)?; - // NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184 Value::from(if num_v_a.is_nan() { -1 } else { From cc903e3e0cc81ce71be9dcbb6e24f6d3203290d3 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Wed, 12 Aug 2020 21:01:37 +0200 Subject: [PATCH 15/18] Added fast path for `Value::to_property_key` --- boa/src/builtins/value/mod.rs | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 788b7e61c3b..8b087110d2a 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -840,13 +840,17 @@ impl Value { /// /// https://tc39.es/ecma262/#sec-topropertykey pub fn to_property_key(&self, ctx: &mut Interpreter) -> Result { - let key = self.to_primitive(ctx, PreferredType::String)?; - if let Value::Symbol(ref symbol) = key { - Ok(PropertyKey::from(symbol.clone())) - } else { - let string = key.to_string(ctx)?; - Ok(PropertyKey::from(string)) - } + Ok(match self { + // Fast path: + Value::String(string) => string.clone().into(), + Value::Symbol(symbol) => symbol.clone().into(), + // Slow path: + _ => match self.to_primitive(ctx, PreferredType::String)? { + Value::String(ref string) => string.clone().into(), + Value::Symbol(ref symbol) => symbol.clone().into(), + primitive => primitive.to_string(ctx)?.into(), + }, + }) } /// It returns value converted to a numeric value of type Number or BigInt. From b5aa974bb97c1901ec179ca5121942f1ba2e1bf2 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 15 Aug 2020 00:10:27 +0200 Subject: [PATCH 16/18] Added `Numeric` enum for `.to_numeric()` --- boa/src/builtins/value/mod.rs | 84 +++++++++++++++++++++++++++- boa/src/builtins/value/operations.rs | 73 ++++++++++++------------ 2 files changed, 117 insertions(+), 40 deletions(-) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 8b087110d2a..63f1894af65 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -856,10 +856,10 @@ impl Value { /// It returns value converted to a numeric value of type Number or BigInt. /// /// See: https://tc39.es/ecma262/#sec-tonumeric - pub fn to_numeric(&self, ctx: &mut Interpreter) -> ResultValue { + pub fn to_numeric(&self, ctx: &mut Interpreter) -> Result { let primitive = self.to_primitive(ctx, PreferredType::Number)?; - if primitive.is_bigint() { - return Ok(primitive); + if let Some(bigint) = primitive.as_bigint() { + return Ok(bigint.clone().into()); } Ok(self.to_number(ctx)?.into()) } @@ -1001,3 +1001,81 @@ pub enum PreferredType { Number, Default, } + +#[derive(Debug, Clone, PartialEq, PartialOrd)] +pub enum Numeric { + Number(f64), + BigInt(RcBigInt), +} + +impl From for Numeric { + #[inline] + fn from(value: f64) -> Self { + Self::Number(value) + } +} + +impl From for Numeric { + #[inline] + fn from(value: i32) -> Self { + Self::Number(value.into()) + } +} + +impl From for Numeric { + #[inline] + fn from(value: i16) -> Self { + Self::Number(value.into()) + } +} + +impl From for Numeric { + #[inline] + fn from(value: i8) -> Self { + Self::Number(value.into()) + } +} + +impl From for Numeric { + #[inline] + fn from(value: u32) -> Self { + Self::Number(value.into()) + } +} + +impl From for Numeric { + #[inline] + fn from(value: u16) -> Self { + Self::Number(value.into()) + } +} + +impl From for Numeric { + #[inline] + fn from(value: u8) -> Self { + Self::Number(value.into()) + } +} + +impl From for Numeric { + #[inline] + fn from(value: BigInt) -> Self { + Self::BigInt(value.into()) + } +} + +impl From for Numeric { + #[inline] + fn from(value: RcBigInt) -> Self { + Self::BigInt(value) + } +} + +impl From for Value { + fn from(value: Numeric) -> Self { + match value { + Numeric::Number(number) => Self::rational(number), + Numeric::BigInt(bigint) => Self::bigint(bigint), + } + } +} diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index d0a00bd0203..c85e651d2df 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -28,8 +28,8 @@ impl Value { (Self::String(ref x), ref y) => Self::string(format!("{}{}", x, y.to_string(ctx)?)), (ref x, Self::String(ref y)) => Self::string(format!("{}{}", x.to_string(ctx)?, y)), (x, y) => match (x.to_numeric(ctx)?, y.to_numeric(ctx)?) { - (Self::Rational(x), Self::Rational(y)) => Self::rational(x + y), - (Self::BigInt(ref n1), Self::BigInt(ref n2)) => { + (Numeric::Number(x), Numeric::Number(y)) => Self::rational(x + y), + (Numeric::BigInt(ref n1), Numeric::BigInt(ref n2)) => { Self::bigint(n1.as_inner().clone() + n2.as_inner().clone()) } (_, _) => { @@ -57,8 +57,8 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a - b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a - b), + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() - b.as_inner().clone()) } (_, _) => { @@ -85,8 +85,8 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a * b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a * b), + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() * b.as_inner().clone()) } (_, _) => { @@ -113,8 +113,8 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a / b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a / b), + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() / b.as_inner().clone()) } (_, _) => { @@ -141,8 +141,8 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a % b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a % b), + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() % b.as_inner().clone()) } (_, _) => { @@ -167,8 +167,8 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a.powf(b)), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::Number(a), Numeric::Number(b)) => Self::rational(a.powf(b)), + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone().pow(b)) } (_, _) => { @@ -197,10 +197,10 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => { + (Numeric::Number(a), Numeric::Number(b)) => { Self::integer(f64_to_int32(a) & f64_to_int32(b)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() & b.as_inner().clone()) } (_, _) => { @@ -229,10 +229,10 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => { + (Numeric::Number(a), Numeric::Number(b)) => { Self::integer(f64_to_int32(a) | f64_to_int32(b)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() | b.as_inner().clone()) } (_, _) => { @@ -261,10 +261,10 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(a), Self::Rational(b)) => { + (Numeric::Number(a), Numeric::Number(b)) => { Self::integer(f64_to_int32(a) ^ f64_to_int32(b)) } - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (Numeric::BigInt(ref a), Numeric::BigInt(ref b)) => { Self::bigint(a.as_inner().clone() ^ b.as_inner().clone()) } (_, _) => { @@ -297,10 +297,10 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(x), Self::Rational(y)) => { + (Numeric::Number(x), Numeric::Number(y)) => { Self::integer(f64_to_int32(x).wrapping_shl(f64_to_uint32(y))) } - (Self::BigInt(ref x), Self::BigInt(ref y)) => { + (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { Self::bigint(x.as_inner().clone() << y.as_inner().clone()) } (_, _) => { @@ -333,10 +333,10 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(x), Self::Rational(y)) => { + (Numeric::Number(x), Numeric::Number(y)) => { Self::integer(f64_to_int32(x).wrapping_shr(f64_to_uint32(y))) } - (Self::BigInt(ref x), Self::BigInt(ref y)) => { + (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => { Self::bigint(x.as_inner().clone() >> y.as_inner().clone()) } (_, _) => { @@ -367,10 +367,10 @@ impl Value { // Slow path: (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { - (Self::Rational(x), Self::Rational(y)) => { + (Numeric::Number(x), Numeric::Number(y)) => { Self::rational(f64_to_uint32(x).wrapping_shr(f64_to_uint32(y))) } - (Self::BigInt(_), Self::BigInt(_)) => { + (Numeric::BigInt(_), Numeric::BigInt(_)) => { return ctx .throw_type_error("BigInts have no unsigned right shift, use >> instead"); } @@ -433,11 +433,11 @@ impl Value { ) -> Result { Ok(match (self, other) { // Fast path (for some common operations): - (Value::Integer(x), Value::Integer(y)) => (x < y).into(), - (Value::Integer(x), Value::Rational(y)) => Number::less_than(f64::from(*x), *y), - (Value::Rational(x), Value::Integer(y)) => Number::less_than(*x, f64::from(*y)), - (Value::Rational(x), Value::Rational(y)) => Number::less_than(*x, *y), - (Value::BigInt(ref x), Value::BigInt(ref y)) => (x < y).into(), + (Self::Integer(x), Self::Integer(y)) => (x < y).into(), + (Self::Integer(x), Self::Rational(y)) => Number::less_than(f64::from(*x), *y), + (Self::Rational(x), Self::Integer(y)) => Number::less_than(*x, f64::from(*y)), + (Self::Rational(x), Self::Rational(y)) => Number::less_than(*x, *y), + (Self::BigInt(ref x), Self::BigInt(ref y)) => (x < y).into(), // Slow path: (_, _) => { @@ -453,7 +453,7 @@ impl Value { }; match (px, py) { - (Value::String(ref x), Value::String(ref y)) => { + (Self::String(ref x), Self::String(ref y)) => { if x.starts_with(y.as_str()) { return Ok(AbstractRelation::False); } @@ -467,14 +467,14 @@ impl Value { } unreachable!() } - (Value::BigInt(ref x), Value::String(ref y)) => { + (Self::BigInt(ref x), Self::String(ref y)) => { if let Some(y) = string_to_bigint(&y) { (*x.as_inner() < y).into() } else { AbstractRelation::Undefined } } - (Value::String(ref x), Value::BigInt(ref y)) => { + (Self::String(ref x), Self::BigInt(ref y)) => { if let Some(x) = string_to_bigint(&x) { (x < *y.as_inner()).into() } else { @@ -482,9 +482,9 @@ impl Value { } } (px, py) => match (px.to_numeric(ctx)?, py.to_numeric(ctx)?) { - (Value::Rational(x), Value::Rational(y)) => Number::less_than(x, y), - (Value::BigInt(ref x), Value::BigInt(ref y)) => (x < y).into(), - (Value::BigInt(ref x), Value::Rational(y)) => { + (Numeric::Number(x), Numeric::Number(y)) => Number::less_than(x, y), + (Numeric::BigInt(ref x), Numeric::BigInt(ref y)) => (x < y).into(), + (Numeric::BigInt(ref x), Numeric::Number(y)) => { if y.is_nan() { return Ok(AbstractRelation::Undefined); } @@ -498,7 +498,7 @@ impl Value { }; (*x.as_inner() < BigInt::try_from(n).unwrap()).into() } - (Value::Rational(x), Value::BigInt(ref y)) => { + (Numeric::Number(x), Numeric::BigInt(ref y)) => { if x.is_nan() { return Ok(AbstractRelation::Undefined); } @@ -512,7 +512,6 @@ impl Value { }; (BigInt::try_from(n).unwrap() < *y.as_inner()).into() } - (_, _) => unreachable!("to_numeric should only retrun number and bigint"), }, } } From 85aa5a13dcfb4fa91c3c079d65cf667a64eddf74 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 15 Aug 2020 01:15:28 +0200 Subject: [PATCH 17/18] Added some documentation --- boa/src/builtins/value/display.rs | 1 + boa/src/builtins/value/mod.rs | 57 ++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 16 deletions(-) diff --git a/boa/src/builtins/value/display.rs b/boa/src/builtins/value/display.rs index d7c07a7d933..01b4bc54454 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -1,5 +1,6 @@ use super::*; +/// This object is used for displaying a `Value`. #[derive(Debug, Clone, Copy)] pub struct ValueDisplay<'value> { pub(super) value: &'value Value, diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 63f1894af65..548f252ae6f 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -699,7 +699,9 @@ impl Value { } } - /// Helper function. + /// Converts the value to a `BigInt`. + /// + /// This function is equivelent to `BigInt(value)` in JavaScript. pub fn to_bigint(&self, ctx: &mut Interpreter) -> Result { match self { Value::Null => Err(ctx.construct_type_error("cannot convert null to a BigInt")), @@ -728,11 +730,25 @@ impl Value { } } + /// Returns an object that implements `Display`. + /// + /// # Examples + /// + /// ``` + /// use boa::builtins::value::Value; + /// + /// let value = Value::number(3); + /// + /// println!("{}", value.display()); + /// ``` + #[inline] pub fn display(&self) -> ValueDisplay<'_> { ValueDisplay { value: self } } - /// Converts a value into a rust heap allocated string. + /// Converts the value to a string. + /// + /// This function is equivelent to `String(value)` in JavaScript. pub fn to_string(&self, ctx: &mut Interpreter) -> Result { match self { Value::Null => Ok("null".into()), @@ -750,8 +766,9 @@ impl Value { } } - /// The abstract operation ToObject converts argument to a value of type Object - /// https://tc39.es/ecma262/#sec-toobject + /// Converts th value to a value of type Object. + /// + /// See: pub fn to_object(&self, ctx: &mut Interpreter) -> ResultValue { match self { Value::Undefined | Value::Null => { @@ -836,9 +853,9 @@ impl Value { } } - /// The abstract operation ToPropertyKey takes argument argument. It converts argument to a value that can be used as a property key. + /// Converts the value to a `PropertyKey`, that can be used as a key for properties. /// - /// https://tc39.es/ecma262/#sec-topropertykey + /// See pub fn to_property_key(&self, ctx: &mut Interpreter) -> Result { Ok(match self { // Fast path: @@ -853,9 +870,9 @@ impl Value { }) } - /// It returns value converted to a numeric value of type Number or BigInt. + /// It returns value converted to a numeric value of type `Number` or `BigInt`. /// - /// See: https://tc39.es/ecma262/#sec-tonumeric + /// See: pub fn to_numeric(&self, ctx: &mut Interpreter) -> Result { let primitive = self.to_primitive(ctx, PreferredType::Number)?; if let Some(bigint) = primitive.as_bigint() { @@ -866,7 +883,9 @@ impl Value { /// Converts a value to an integral 32 bit unsigned integer. /// - /// See: https://tc39.es/ecma262/#sec-toint32 + /// This function is equivelent to `value | 0` in JavaScript + /// + /// See: pub fn to_uint32(&self, ctx: &mut Interpreter) -> Result { // This is the fast path, if the value is Integer we can just return it. if let Value::Integer(number) = *self { @@ -879,7 +898,7 @@ impl Value { /// Converts a value to an integral 32 bit signed integer. /// - /// See: https://tc39.es/ecma262/#sec-toint32 + /// See: pub fn to_int32(&self, ctx: &mut Interpreter) -> Result { // This is the fast path, if the value is Integer we can just return it. if let Value::Integer(number) = *self { @@ -892,7 +911,7 @@ impl Value { /// Converts a value to a non-negative integer if it is a valid integer index value. /// - /// See: https://tc39.es/ecma262/#sec-toindex + /// See: pub fn to_index(&self, ctx: &mut Interpreter) -> Result { if self.is_undefined() { return Ok(0); @@ -913,7 +932,7 @@ impl Value { /// Converts argument to an integer suitable for use as the length of an array-like object. /// - /// See: https://tc39.es/ecma262/#sec-tolength + /// See: pub fn to_length(&self, ctx: &mut Interpreter) -> Result { // 1. Let len be ? ToInteger(argument). let len = self.to_integer(ctx)?; @@ -929,7 +948,7 @@ impl Value { /// Converts a value to an integral Number value. /// - /// See: https://tc39.es/ecma262/#sec-tointeger + /// See: pub fn to_integer(&self, ctx: &mut Interpreter) -> Result { // 1. Let number be ? ToNumber(argument). let number = self.to_number(ctx)?; @@ -951,6 +970,8 @@ impl Value { /// Converts a value to a double precision floating point. /// + /// This function is equivelent to the unary `+` operator (`+value`) in JavaScript + /// /// See: https://tc39.es/ecma262/#sec-tonumber pub fn to_number(&self, ctx: &mut Interpreter) -> Result { match *self { @@ -975,11 +996,11 @@ impl Value { } } - /// This is a more specialized version of `to_numeric`. + /// This is a more specialized version of `to_numeric`, including `BigInt`. /// - /// It returns value converted to a numeric value of type `Number`. + /// This function is equivelent to `Number(value)` in JavaScript /// - /// See: https://tc39.es/ecma262/#sec-tonumeric + /// See: pub fn to_numeric_number(&self, ctx: &mut Interpreter) -> Result { let primitive = self.to_primitive(ctx, PreferredType::Number)?; if let Some(ref bigint) = primitive.as_bigint() { @@ -995,6 +1016,7 @@ impl Default for Value { } } +/// The preffered type to convert an object to a primitive `Value`. #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub enum PreferredType { String, @@ -1002,9 +1024,12 @@ pub enum PreferredType { Default, } +/// Numeric value which can be of two types `Number`, `BigInt`. #[derive(Debug, Clone, PartialEq, PartialOrd)] pub enum Numeric { + /// Double precision floating point number. Number(f64), + /// BigInt an integer of arbitrary size. BigInt(RcBigInt), } From e929bd6e13cecd27c119e3892c1218946789c206 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sat, 15 Aug 2020 01:22:13 +0200 Subject: [PATCH 18/18] Doc fix --- boa/src/builtins/value/mod.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index 548f252ae6f..6f7c6f6b684 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -748,7 +748,7 @@ impl Value { /// Converts the value to a string. /// - /// This function is equivelent to `String(value)` in JavaScript. + /// This function is equivalent to `String(value)` in JavaScript. pub fn to_string(&self, ctx: &mut Interpreter) -> Result { match self { Value::Null => Ok("null".into()), @@ -768,6 +768,8 @@ impl Value { /// Converts th value to a value of type Object. /// + /// This function is equivalent to `Object(value)` in JavaScript + /// /// See: pub fn to_object(&self, ctx: &mut Interpreter) -> ResultValue { match self { @@ -883,7 +885,7 @@ impl Value { /// Converts a value to an integral 32 bit unsigned integer. /// - /// This function is equivelent to `value | 0` in JavaScript + /// This function is equivalent to `value | 0` in JavaScript /// /// See: pub fn to_uint32(&self, ctx: &mut Interpreter) -> Result { @@ -970,7 +972,7 @@ impl Value { /// Converts a value to a double precision floating point. /// - /// This function is equivelent to the unary `+` operator (`+value`) in JavaScript + /// This function is equivalent to the unary `+` operator (`+value`) in JavaScript /// /// See: https://tc39.es/ecma262/#sec-tonumber pub fn to_number(&self, ctx: &mut Interpreter) -> Result { @@ -998,7 +1000,7 @@ impl Value { /// This is a more specialized version of `to_numeric`, including `BigInt`. /// - /// This function is equivelent to `Number(value)` in JavaScript + /// This function is equivalent to `Number(value)` in JavaScript /// /// See: pub fn to_numeric_number(&self, ctx: &mut Interpreter) -> Result {