From c5b708b2ef0df5b98f2ebd945596ac3a555707ae Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sun, 16 Aug 2020 00:46:46 +0200 Subject: [PATCH] Moved value operations from `Interpreter` to `Value` (#625) --- boa/src/builtins/array/mod.rs | 86 +++-- boa/src/builtins/bigint/mod.rs | 8 +- boa/src/builtins/console/mod.rs | 39 +- boa/src/builtins/date/mod.rs | 40 +-- boa/src/builtins/date/tests.rs | 22 +- boa/src/builtins/error/mod.rs | 8 +- boa/src/builtins/error/range.rs | 6 +- boa/src/builtins/error/reference.rs | 6 +- boa/src/builtins/error/syntax.rs | 5 +- boa/src/builtins/error/type.rs | 6 +- boa/src/builtins/function/mod.rs | 2 +- boa/src/builtins/json/mod.rs | 9 +- boa/src/builtins/json/tests.rs | 20 +- boa/src/builtins/map/mod.rs | 4 +- boa/src/builtins/math/mod.rs | 74 ++-- boa/src/builtins/math/tests.rs | 196 +++++------ boa/src/builtins/number/mod.rs | 24 +- boa/src/builtins/number/tests.rs | 26 +- boa/src/builtins/object/mod.rs | 15 +- boa/src/builtins/regexp/mod.rs | 14 +- boa/src/builtins/string/mod.rs | 177 +++++----- boa/src/builtins/symbol/mod.rs | 2 +- boa/src/builtins/symbol/tests.rs | 2 +- boa/src/builtins/value/conversions.rs | 19 +- boa/src/builtins/value/display.rs | 74 ++-- boa/src/builtins/value/equality.rs | 22 +- boa/src/builtins/value/mod.rs | 489 +++++++++++++++++++++++--- boa/src/builtins/value/operations.rs | 131 +++---- boa/src/builtins/value/tests.rs | 47 +-- boa/src/exec/call/mod.rs | 9 +- boa/src/exec/field/mod.rs | 10 +- boa/src/exec/mod.rs | 341 +----------------- boa/src/exec/operator/mod.rs | 26 +- boa/src/exec/tests.rs | 81 +++-- boa/src/lib.rs | 6 +- boa_cli/src/main.rs | 10 +- boa_wasm/src/lib.rs | 4 +- 37 files changed, 1099 insertions(+), 961 deletions(-) diff --git a/boa/src/builtins/array/mod.rs b/boa/src/builtins/array/mod.rs index 6976a17b9d..33865d8a68 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()); @@ -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")); + let length = this.get_field("length").as_number().unwrap() as i32; 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); } @@ -367,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 { @@ -405,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); @@ -447,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 { @@ -507,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()); @@ -516,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)) @@ -543,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)?; @@ -587,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 @@ -640,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) @@ -688,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()]; @@ -727,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()); @@ -754,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 { - let len: i32 = i32::from(&this.get_field("length")); + 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) @@ -796,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(); @@ -829,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, }; @@ -886,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)?; @@ -939,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()); @@ -949,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)) @@ -971,13 +979,13 @@ 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"), }; 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"); @@ -1015,7 +1023,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; } @@ -1038,13 +1046,13 @@ 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"), }; 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( @@ -1092,7 +1100,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/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index 45a82a1a71..d2cdffc853 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)) @@ -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 }; @@ -184,10 +184,10 @@ 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 = ctx.to_bigint(bigint_arg)?; + let bigint = bigint_arg.to_bigint(ctx)?; Ok(( bigint diff --git a/boa/src/builtins/console/mod.rs b/boa/src/builtins/console/mod.rs index 5493571958..980027d8be 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()), @@ -74,26 +74,37 @@ 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; } /* 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 } /* 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 */ '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 +122,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) @@ -153,7 +164,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); } @@ -289,7 +300,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 +324,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 +358,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 +387,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(), }; @@ -384,7 +395,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 { @@ -409,7 +420,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 08591516f6..06d62a8f4f 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}; @@ -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() { @@ -306,13 +306,13 @@ 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, }, 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) { @@ -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)), } @@ -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/date/tests.rs b/boa/src/builtins/date/tests.rs index 45c2b16abc..fb005c3f0d 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 { @@ -14,19 +16,15 @@ 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") - }; - - let date_time = 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/error/mod.rs b/boa/src/builtins/error/mod.rs index 51024a8cc0..7fea89045f 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 @@ -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/range.rs b/boa/src/builtins/error/range.rs index 87c6dc2740..0318bf4424 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 baa79d3137..d9ae09ef57 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 508015770f..2d7ab27bf1 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,7 +59,8 @@ 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()) + // FIXME: This should not use `.display()` + Ok(format!("{}: {}", name.display(), message.display()).into()) } /// Initialise the global object with the `SyntaxError` object. diff --git a/boa/src/builtins/error/type.rs b/boa/src/builtins/error/type.rs index 5e61e91a59..7d13075cd5 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/function/mod.rs b/boa/src/builtins/function/mod.rs index fb701dd6b1..ad34e9dabc 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/json/mod.rs b/boa/src/builtins/json/mod.rs index 1393b48385..31d0e9627f 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/json/tests.rs b/boa/src/builtins/json/tests.rs index cb249f1066..02c661c594 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/map/mod.rs b/boa/src/builtins/map/mod.rs index a6eaaf6033..9e7a72d276 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/math/mod.rs b/boa/src/builtins/math/mod.rs index fa8af32ee1..bf7bec64bb 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()) @@ -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) @@ -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()) @@ -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, @@ -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 3eb3204ef1..b084828432 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 14409c44cf..6a47eb29d3 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)); @@ -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()? + .map_or(10, |radix| radix as u8); // 4. If radixNumber < 2 or radixNumber > 36, throw a RangeError exception. if radix < 2 || radix > 36 { @@ -563,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()) @@ -590,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 5224b83204..2f95a928e7 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/object/mod.rs b/boa/src/builtins/object/mod.rs index 89d6af5094..39d9656a0c 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() )), } } @@ -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()) @@ -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 )` @@ -545,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() @@ -565,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 own_property = ctx.to_object(this).map(|obj| { + 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/regexp/mod.rs b/boa/src/builtins/regexp/mod.rs index 579ec60930..7ce8eb4850 100644 --- a/boa/src/builtins/regexp/mod.rs +++ b/boa/src/builtins/regexp/mod.rs @@ -289,8 +289,11 @@ 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 mut last_index = usize::from(&this.get_field("lastIndex")); + let arg_str = args + .get(0) + .expect("could not get argument") + .to_string(ctx)?; + 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) { @@ -325,8 +328,11 @@ 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 mut last_index = usize::from(&this.get_field("lastIndex")); + let arg_str = args + .get(0) + .expect("could not get argument") + .to_string(ctx)?; + 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 80bc7909d7..c88f44b637 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,11 +111,11 @@ 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 pos = i32::from( - args.get(0) - .expect("failed to get argument for String method"), - ); + let primitive_val = this.to_string(ctx)?; + 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 @@ -153,15 +153,15 @@ 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. 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)); @@ -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,10 +214,10 @@ 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)?; + let n = arg.to_integer(ctx)?; if n < 0.0 { return ctx.throw_range_error("repeat count cannot be a negative number"); } @@ -249,14 +249,17 @@ 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) - .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. @@ -296,13 +299,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; @@ -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); @@ -339,13 +342,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; @@ -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); @@ -383,13 +388,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; @@ -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); @@ -442,7 +449,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 +522,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,15 +552,18 @@ 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 .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); @@ -589,15 +599,18 @@ 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 .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); @@ -627,7 +640,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,16 +690,16 @@ 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")); } - 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| 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,16 +717,16 @@ 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")); } - 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| 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 +765,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 +785,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 +805,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 +825,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 +847,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,22 +866,23 @@ 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 } 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 @@ -901,15 +915,14 @@ 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 } 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. @@ -919,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 { @@ -977,7 +992,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 +1012,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 853aa90d86..4d47b1a0d1 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/symbol/tests.rs b/boa/src/builtins/symbol/tests.rs index 518632a4ef..a85b351aba 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 3a0a04872f..97b4629615 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) @@ -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 { @@ -108,12 +102,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 +119,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 8b0f1e3a2c..01b4bc5445 100644 --- a/boa/src/builtins/value/display.rs +++ b/boa/src/builtins/value/display.rs @@ -1,5 +1,11 @@ use super::*; +/// This object is used for displaying a `Value`. +#[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 +40,7 @@ macro_rules! print_obj_value { vec![format!( "{:>width$}: {}", "__proto__", - object.prototype(), + object.prototype().display(), width = $indent, )] } @@ -86,15 +92,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 { @@ -126,15 +133,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)"); } @@ -158,7 +166,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 +187,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 +227,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/equality.rs b/boa/src/builtins/value/equality.rs index 9ea75e6000..63f9b96f18 100644 --- a/boa/src/builtins/value/equality.rs +++ b/boa/src/builtins/value/equality.rs @@ -1,7 +1,5 @@ use super::*; -use crate::{builtins::Number, exec::PreferredType, Interpreter}; - -use std::borrow::Borrow; +use crate::{builtins::Number, Interpreter}; 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 @@ -82,26 +80,22 @@ 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). (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 b2016f3149..6f7c6f6b68 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -5,11 +5,12 @@ #[cfg(test)] mod tests; +use super::number::{f64_to_int32, f64_to_uint32}; 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; @@ -36,6 +37,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::*; @@ -383,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 { @@ -419,51 +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 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: @@ -650,7 +616,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); } @@ -704,6 +670,346 @@ 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()) + } + } + + /// 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")), + 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")), + } + } + + /// 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 the value to a string. + /// + /// 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()), + 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) + } + } + } + + /// 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 { + 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()), + } + } + + /// Converts the value to a `PropertyKey`, that can be used as a key for properties. + /// + /// See + pub fn to_property_key(&self, ctx: &mut Interpreter) -> Result { + 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`. + /// + /// 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() { + return Ok(bigint.clone().into()); + } + Ok(self.to_number(ctx)?.into()) + } + + /// Converts a value to an integral 32 bit unsigned integer. + /// + /// This function is equivalent 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 { + return Ok(number as u32); + } + let number = self.to_number(ctx)?; + + Ok(f64_to_uint32(number)) + } + + /// Converts a value to an integral 32 bit signed integer. + /// + /// 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 { + return Ok(number); + } + let number = self.to_number(ctx)?; + + Ok(f64_to_int32(number)) + } + + /// Converts a value to a non-negative integer if it is a valid integer index value. + /// + /// See: + pub fn to_index(&self, ctx: &mut Interpreter) -> Result { + if self.is_undefined() { + return Ok(0); + } + + let integer_index = self.to_integer(ctx)?; + + 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) + } + + /// Converts argument to an integer suitable for use as the length of an array-like object. + /// + /// See: + pub fn to_length(&self, ctx: &mut Interpreter) -> Result { + // 1. Let len be ? ToInteger(argument). + let len = self.to_integer(ctx)?; + + // 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 an integral Number value. + /// + /// See: + pub fn to_integer(&self, ctx: &mut Interpreter) -> Result { + // 1. Let number be ? ToNumber(argument). + let number = self.to_number(ctx)?; + + // 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. + /// + /// 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 { + 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) + } + } + } + + /// This is a more specialized version of `to_numeric`, including `BigInt`. + /// + /// This function is equivalent to `Number(value)` in JavaScript + /// + /// 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() { + return Ok(bigint.to_f64()); + } + primitive.to_number(ctx) + } } impl Default for Value { @@ -711,3 +1017,92 @@ impl Default for Value { Self::Undefined } } + +/// The preffered type to convert an object to a primitive `Value`. +#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum PreferredType { + String, + Number, + 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), +} + +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 c8ad4c39b5..c85e651d2d 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] @@ -12,22 +14,22 @@ 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()) } // 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)), - (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)) => { + (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)?) { + (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()) } (_, _) => { @@ -54,9 +56,9 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a - b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -82,9 +84,9 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a * b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -110,9 +112,9 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a / b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -138,9 +140,9 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a % b), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -164,9 +166,9 @@ 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)?) { - (Self::Rational(a), Self::Rational(b)) => Self::rational(a.powf(b)), - (Self::BigInt(ref a), Self::BigInt(ref b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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)) } (_, _) => { @@ -194,11 +196,11 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(a), Self::Rational(b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -226,11 +228,11 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(a), Self::Rational(b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -258,11 +260,11 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(a), Self::Rational(b)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -294,11 +296,11 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(x), Self::Rational(y)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -330,11 +332,11 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(x), Self::Rational(y)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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()) } (_, _) => { @@ -364,11 +366,11 @@ impl Value { } // Slow path: - (_, _) => match (ctx.to_numeric(self)?, ctx.to_numeric(other)?) { - (Self::Rational(x), Self::Rational(y)) => { + (_, _) => match (self.to_numeric(ctx)?, other.to_numeric(ctx)?) { + (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"); } @@ -385,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, }), @@ -402,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 @@ -431,27 +433,27 @@ 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: (_, _) => { 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) }; 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); } @@ -465,24 +467,24 @@ 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 { AbstractRelation::Undefined } } - (px, py) => match (ctx.to_numeric(&px)?, ctx.to_numeric(&py)?) { - (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)) => { + (px, py) => match (px.to_numeric(ctx)?, py.to_numeric(ctx)?) { + (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); } @@ -496,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); } @@ -510,7 +512,6 @@ impl Value { }; (BigInt::try_from(n).unwrap() < *y.as_inner()).into() } - (_, _) => unreachable!("to_numeric should only retrun number and bigint"), }, } } diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index 6401b72cbe..acec8d475e 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"); @@ -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); } @@ -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"); } @@ -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); } @@ -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"); } @@ -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); } @@ -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()); } @@ -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,17 +354,18 @@ 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); } #[test] +#[allow(clippy::float_cmp)] fn pow_number_and_number() { let realm = Realm::create(); 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); } @@ -374,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()); } @@ -392,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()); } @@ -400,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] @@ -409,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] @@ -421,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] @@ -433,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] @@ -445,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] @@ -459,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 363c165327..46a2eaa156 100644 --- a/boa/src/exec/call/mod.rs +++ b/boa/src/exec/call/mod.rs @@ -11,17 +11,16 @@ 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())) } 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/exec/field/mod.rs b/boa/src/exec/field/mod.rs index 45e0a2ab42..bba610bbbf 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,11 +18,11 @@ 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)?; - 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 c33117cb74..5f923ea5c1 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -26,11 +26,10 @@ use crate::{ builtins, builtins::{ function::{Function as FunctionObject, FunctionBody, ThisMode}, - number::{f64_to_int32, f64_to_uint32}, object::{Object, ObjectData, PROTOTYPE}, property::PropertyKey, - value::{RcBigInt, RcString, ResultValue, Type, Value}, - BigInt, Console, Number, + value::{PreferredType, ResultValue, Type, Value}, + Console, }, realm::Realm, syntax::ast::{ @@ -39,9 +38,6 @@ use crate::{ }, BoaProfiler, }; -use std::borrow::Borrow; -use std::convert::TryFrom; -use std::ops::Deref; pub trait Executable { /// Runs this executable in the given executor. @@ -54,12 +50,6 @@ pub(crate) enum InterpreterState { Return, Break(Option), } -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum PreferredType { - String, - Number, - Default, -} /// A Javascript intepreter #[derive(Debug)] @@ -197,210 +187,22 @@ 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 = self.to_primitive(value, PreferredType::String)?; - self.to_string(&primitive) - } - } - } - - /// 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 = self.to_primitive(value, 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 - #[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 - #[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 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 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 - #[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 - #[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 = self.to_primitive(value, PreferredType::Number)?; - self.to_number(&primitive) - } - } - } - - /// 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 = self.to_primitive(value, 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`. - /// - /// 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)?; - if let Some(ref bigint) = primitive.as_bigint() { - return Ok(bigint.to_f64()); - } - Ok(self.to_number(&primitive)?) - } - /// 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 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 { - let length = i32::from(&value.get_field("length")); + if let ObjectData::Array = x.borrow().data { + let length = value.get_field("length").as_number().unwrap() as i32; let values = (0..length) .map(|idx| value.get_field(idx.to_string())) .collect(); 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 @@ -417,7 +219,6 @@ impl Interpreter { .environment .get_binding_value("Array") .expect("Array was not initialized") - .borrow() .get_field(PROTOTYPE), ); array.set_field("0", key); @@ -476,49 +277,6 @@ 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)?; - if let Value::Symbol(ref symbol) = key { - Ok(PropertyKey::from(symbol.clone())) - } else { - let string = self.to_string(&key)?; - 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() { @@ -528,93 +286,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) => { @@ -629,7 +300,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 0b5a8c9671..05efcdfb05 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) } })) @@ -173,30 +173,34 @@ 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::Not => x.not(interpreter)?.into(), op::UnaryOp::Tilde => { - let num_v_a = x.to_number(); - // NOTE: possible UB: https://github.com/rust-lang/rust/issues/10184 + let num_v_a = x.to_number(interpreter)?; Value::from(if num_v_a.is_nan() { -1 } else { + // TODO: this is not spec compliant. !(num_v_a as i32) }) } @@ -211,7 +215,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 0a4b089e91..50e55ca4fa 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] @@ -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] @@ -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 )); } @@ -936,25 +938,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 ); } @@ -966,7 +972,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); }; }; @@ -1078,11 +1084,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] @@ -1105,11 +1117,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] diff --git a/boa/src/lib.rs b/boa/src/lib.rs index 4314d77f79..55a6006d3f 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 ddb2def637..80ff176650 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 97eab2e5c6..6c58529e8b 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()) }