From ef2dcfd2296cc7936f4b19af52e670468bc27cd5 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Sun, 27 Oct 2019 11:49:12 -0700 Subject: [PATCH 1/5] Feature: Skeleton code for Number refs #34 --- src/lib/builtins/mod.rs | 2 + src/lib/js/.string.rs.swp | Bin 0 -> 16384 bytes src/lib/js/number.rs | 97 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 99 insertions(+) create mode 100644 src/lib/js/.string.rs.swp create mode 100644 src/lib/js/number.rs diff --git a/src/lib/builtins/mod.rs b/src/lib/builtins/mod.rs index 1c59657f314..3d549fa4455 100644 --- a/src/lib/builtins/mod.rs +++ b/src/lib/builtins/mod.rs @@ -12,6 +12,8 @@ pub mod function; pub mod json; /// The global `Math` object pub mod math; +/// The global `Number` object +pub mod number; /// The global `Object` object pub mod object; /// The global 'RegExp' object diff --git a/src/lib/js/.string.rs.swp b/src/lib/js/.string.rs.swp new file mode 100644 index 0000000000000000000000000000000000000000..10aacd640fbf159a7b7a71feb0c0a81393dd9a2a GIT binary patch literal 16384 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 literal 0 HcmV?d00001 diff --git a/src/lib/js/number.rs b/src/lib/js/number.rs new file mode 100644 index 00000000000..a27a5625829 --- /dev/null +++ b/src/lib/js/number.rs @@ -0,0 +1,97 @@ +use crate::{ + exec::Interpreter, + js::value::{ResultValue, Value}, +}; + +/// Create a new number [[Construct]] +pub fn make_number(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// https://tc39.es/ecma262/#sec-number-constructor-number-value +pub fn call_number(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// https://tc39.es/ecma262/#sec-number.prototype.toexponential +pub fn to_expotential(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// https://tc39.es/ecma262/#sec-number.prototype.tofixed +pub fn to_fixed(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// https://tc39.es/ecma262/#sec-number.prototype.tolocalestring +pub fn to_locale_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// https://tc39.es/ecma262/#sec-number.prototype.toprecision +pub fn to_precision(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// https://tc39.es/ecma262/#sec-number.prototype.tostring +pub fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// https://tc39.es/ecma262/#sec-number.prototype.valueof +pub fn value_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { + unimplemented!() +} + +/// Create a new `Number` object +pub fn create_constructor(global: &Value) -> Value { + unimplemented!() +} + +/// Iniitalize the `Number` object on the global object +pub fn init(global: &Value) { + unimplemented!() +} + +#[cfg(test)] +mod tests { + #[test] + fn make_number() { + unimplemented!() + } + + #[test] + pub fn call_number() { + unimplemented!() + } + + #[test] + pub fn to_expotential() { + unimplemented!() + } + + #[test] + pub fn to_fixed() { + unimplemented!() + } + + #[test] + pub fn to_locale_string() { + unimplemented!() + } + + #[test] + pub fn to_precision() { + unimplemented!() + } + + #[test] + pub fn to_string() { + unimplemented!() + } + + #[test] + pub fn value_of() { + unimplemented!() + } +} From 9406aa85b06221f100ef8267302a946eaed1dd1a Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Sun, 27 Oct 2019 11:48:26 -0700 Subject: [PATCH 2/5] Feature(Number): Tests for the Test Driven Development! refs #34 --- .gitignore | 4 +- src/lib/builtins/value.rs | 5 + src/lib/js/.string.rs.swp | Bin 16384 -> 0 bytes src/lib/js/number.rs | 231 +++++++++++++++++++++++++++++++++++--- 4 files changed, 221 insertions(+), 19 deletions(-) delete mode 100644 src/lib/js/.string.rs.swp diff --git a/.gitignore b/.gitignore index 6241b9fa41d..379a81f1405 100644 --- a/.gitignore +++ b/.gitignore @@ -3,8 +3,8 @@ *.iml # Vim -.*.swp -.*.swo +*.*.swp +*.*.swo # Build target diff --git a/src/lib/builtins/value.rs b/src/lib/builtins/value.rs index 72219763726..1aeb5bbb1b4 100644 --- a/src/lib/builtins/value.rs +++ b/src/lib/builtins/value.rs @@ -131,6 +131,11 @@ impl ValueData { } } + /// Returns true if the value is a number + pub fn is_num(&self) -> bool { + self.is_double() + } + /// Returns true if the value is a string pub fn is_string(&self) -> bool { match *self { diff --git a/src/lib/js/.string.rs.swp b/src/lib/js/.string.rs.swp deleted file mode 100644 index 10aacd640fbf159a7b7a71feb0c0a81393dd9a2a..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 16384 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 diff --git a/src/lib/js/number.rs b/src/lib/js/number.rs index a27a5625829..7656932aec8 100644 --- a/src/lib/js/number.rs +++ b/src/lib/js/number.rs @@ -4,57 +4,68 @@ use crate::{ }; /// Create a new number [[Construct]] -pub fn make_number(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn make_number(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// https://tc39.es/ecma262/#sec-number-constructor-number-value -pub fn call_number(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn call_number(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// https://tc39.es/ecma262/#sec-number.prototype.toexponential -pub fn to_expotential(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn to_expotential(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// https://tc39.es/ecma262/#sec-number.prototype.tofixed -pub fn to_fixed(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn to_fixed(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// https://tc39.es/ecma262/#sec-number.prototype.tolocalestring -pub fn to_locale_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn to_locale_string(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// https://tc39.es/ecma262/#sec-number.prototype.toprecision -pub fn to_precision(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn to_precision(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// https://tc39.es/ecma262/#sec-number.prototype.tostring -pub fn to_string(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn to_string(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// https://tc39.es/ecma262/#sec-number.prototype.valueof -pub fn value_of(this: &Value, args: &[Value], ctx: &mut Interpreter) -> ResultValue { +pub fn value_of(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { unimplemented!() } /// Create a new `Number` object -pub fn create_constructor(global: &Value) -> Value { +pub fn create_constructor(_global: &Value) -> Value { unimplemented!() } /// Iniitalize the `Number` object on the global object -pub fn init(global: &Value) { +pub fn init(_global: &Value) { unimplemented!() } #[cfg(test)] mod tests { + use super::*; + use crate::{exec::Executor, forward, forward_val, js::value::ValueData, realm::Realm}; + use std::f64::NAN; + + #[test] + fn check_number_constructor_is_function() { + let global = ValueData::new_obj(None); + let number_constructor = create_constructor(&global); + assert_eq!(number_constructor.is_function(), true); + } + #[test] fn make_number() { unimplemented!() @@ -62,36 +73,222 @@ mod tests { #[test] pub fn call_number() { - unimplemented!() + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + const default_zero = new Number(); + const int_one = new Number(1); + const float_two = new Number(2.1); + const str_three = new Number('3.2'); + const bool_one = new Number(true); + const bool_zero = new Number(true); + const invalid_nan = new Number("I am not a number"); + const from_exp = new Number("2.34e+2"); + "#; + + forward(&mut engine, init); + let default_zero = forward_val(&mut engine, "default_zero").unwrap(); + let int_one = forward_val(&mut engine, "int_one").unwrap(); + let float_two = forward_val(&mut engine, "float_two").unwrap(); + let str_three = forward_val(&mut engine, "str_three").unwrap(); + let bool_one = forward_val(&mut engine, "bool_one").unwrap(); + let bool_zero = forward_val(&mut engine, "bool_zero").unwrap(); + 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_num(), f64::from(0)); + assert_eq!(int_one.to_num(), f64::from(1)); + assert_eq!(float_two.to_num(), f64::from(2.1)); + assert_eq!(str_three.to_num(), f64::from(3.2)); + assert_eq!(bool_one.to_num(), f64::from(1)); + assert_eq!(bool_zero.to_num(), f64::from(0)); + assert_eq!(invalid_nan.to_num(), NAN); + assert_eq!(from_exp.to_num(), f64::from(234)); + + for v in vec![ + &default_zero, + &int_one, + &float_two, + &str_three, + &bool_one, + &invalid_nan, + &from_exp, + ] + .iter() + { + assert_eq!(v.is_object(), true); + assert_eq!(v.is_num(), true); + } } #[test] pub fn to_expotential() { - unimplemented!() + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + const default_exp = Number().toExponential(); + const int_exp = Number(5).toExponential(); + const float_exp = Number(1.234).toExponential(); + cont big_exp = Number(1234).toExponential(); + cont nan_exp = Number("I am also not a number").toExponential(); + cont noop_exp = Number("1.23e+2").toExponential(); + "#; + + forward(&mut engine, init); + let default_exp = forward(&mut engine, "default_exp"); + let int_exp = forward(&mut engine, "int_exp"); + let float_exp = forward(&mut engine, "float_exp"); + let big_exp = forward(&mut engine, "big_exp"); + let nan_exp = forward(&mut engine, "nan_exp"); + let noop_exp = forward(&mut engine, "noop_exp"); + + assert_eq!(default_exp, String::from("0e+0")); + assert_eq!(int_exp, String::from("5")); + assert_eq!(float_exp, String::from("1.234e+0")); + assert_eq!(big_exp, String::from("5e+0")); + assert_eq!(nan_exp, String::from("1.234e+3")); + assert_eq!(default_exp, String::from("Nan")); + assert_eq!(noop_exp, String::from("1.23e+2")); } #[test] pub fn to_fixed() { - unimplemented!() + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + const default_fixed = Number().toFixed(); + const pos_fixed = Number("3.456e+4").toFixed(); + const neg_fixed = Number("3.456e-4").toFixed(); + const noop_fixed = Number(5).toFixed(); + const nan_fixed = Number("I am not a number").toFixed(); + "#; + + forward(&mut engine, init); + let default_fixed = forward(&mut engine, "default_fixed"); + let pos_fixed = forward(&mut engine, "pos_fixed"); + let neg_fixed = forward(&mut engine, "neg_fixed"); + let noop_fixed = forward(&mut engine, "noop_fixed"); + let nan_fixed = forward(&mut engine, "nan_fixed"); + + assert_eq!(default_fixed, String::from("0")); + assert_eq!(pos_fixed, String::from("34560")); + assert_eq!(neg_fixed, String::from("0")); + assert_eq!(noop_fixed, String::from("5")); + assert_eq!(nan_fixed, String::from("NaN")); } #[test] pub fn to_locale_string() { - unimplemented!() + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + const default_locale = Number().toLocaleString(); + const small_locale = Number(5).toLocaleString(); + const big_locale = Number("345600").toLocaleString(); + const neg_locale = Number(-25).toLocaleString(); + "#; + + // TODO: We don't actually do any locale checking here + // To honor the spec we should print numbers according to user locale. + + forward(&mut engine, init); + let default_locale = forward(&mut engine, "default_locale"); + let small_locale = forward(&mut engine, "small_locale"); + let big_locale = forward(&mut engine, "big_locale"); + let neg_locale = forward(&mut engine, "neg_locale"); + + assert_eq!(default_locale, String::from("0")); + assert_eq!(small_locale, String::from("5")); + assert_eq!(big_locale, String::from("345600")); + assert_eq!(neg_locale, String::from("345600")); } #[test] pub fn to_precision() { - unimplemented!() + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + const default_precision = Number().toPrecision(); + const low_precision = Number(123456789).toPrecision(1); + const more_precision = Number(123456789).toPrecision(4); + const exact_precision = Number(123456789).toPrecision(9); + const over_precision = Number(123456789).toPrecision(50); + const neg_precision = Number(-123456789).toPrecision(4); + "#; + + forward(&mut engine, init); + let default_precision = forward(&mut engine, "default_precision"); + let low_precision = forward(&mut engine, "low_precision"); + let more_precision = forward(&mut engine, "more_precision"); + let exact_precision = forward(&mut engine, "exact_precision"); + let over_precision = forward(&mut engine, "over_precision"); + let neg_precision = forward(&mut engine, "neg_precision"); + + assert_eq!(default_precision, String::from("0")); + assert_eq!(low_precision, String::from("1e+8")); + assert_eq!(more_precision, String::from("1.235e+8")); + assert_eq!(exact_precision, String::from("123456789")); + assert_eq!( + over_precision, + String::from("123456789.00000000000000000000000000000000000000000") + ); + assert_eq!(neg_precision, String::from("-1.235e+8")); } #[test] pub fn to_string() { - unimplemented!() + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + const default_string = Number().toString(); + const int_string = Number(123).toString(); + const float_string = Number(1.234).toString(); + const exp_string = Number(1.2e+4).toString(); + const neg_string = Number(-1.2).toString(); + "#; + + forward(&mut engine, init); + let default_string = forward(&mut engine, "default_string"); + let int_string = forward(&mut engine, "int_string"); + let float_string = forward(&mut engine, "float_string"); + let exp_string = forward(&mut engine, "exp_string"); + let neg_string = forward(&mut engine, "neg_string"); + + assert_eq!(default_string, String::from("0")); + assert_eq!(int_string, String::from("123")); + assert_eq!(float_string, String::from("1.234")); + assert_eq!(exp_string, String::from("1200")); + assert_eq!(neg_string, String::from("-1.2")); } #[test] pub fn value_of() { - unimplemented!() + let realm = Realm::create(); + let mut engine = Executor::new(realm); + let init = r#" + const default_val = Number().valueOf(); + const int_val = Number("123").valueOf(); + const float_val = Number(1.234).valueOf(); + const exp_val = Number(1.2e+4).valueOf() + const neg_val = Number(-1.2e+4).valueOf() + "#; + + forward(&mut engine, init); + let default_val = forward_val(&mut engine, "default_val").unwrap(); + let int_val = forward_val(&mut engine, "int_val").unwrap(); + let float_val = forward_val(&mut engine, "float_val").unwrap(); + let exp_val = forward_val(&mut engine, "exp_val").unwrap(); + let neg_val = forward_val(&mut engine, "neg_val").unwrap(); + + for v in vec![&default_val, &int_val, &float_val, &exp_val, &neg_val].iter() { + assert_eq!(v.is_object(), true); + assert_eq!(v.is_num(), true); + } + + assert_eq!(default_val.to_num(), f64::from(0)); + assert_eq!(int_val.to_num(), f64::from(123)); + assert_eq!(float_val.to_num(), f64::from(1.234)); + assert_eq!(exp_val.to_num(), f64::from(1200)); + assert_eq!(neg_val.to_num(), f64::from(-1200)); } } From 6b61642ad2adbeb8fff291b59cc1949d4f64b943 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Sun, 27 Oct 2019 11:48:39 -0700 Subject: [PATCH 3/5] Feature: Most of Number() is complete Includes: - make_number() - call_number() - Number().toExponential() - Number().toFixed() - Number().toLocaleString() (ish) - Number().toString() - Number().valueOf() - create_constructor() - init() Missing: - Number().toPrecision() refs #34 --- .gitignore | 2 +- src/lib/builtins/string.rs | 1 + src/lib/exec.rs | 29 +++++ src/lib/js/number.rs | 238 +++++++++++++++++++++++++------------ src/lib/realm.rs | 7 +- tests/js/test.js | 8 -- 6 files changed, 198 insertions(+), 87 deletions(-) diff --git a/.gitignore b/.gitignore index 379a81f1405..42c96f2e8c5 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,4 @@ yarn-error.log .vscode/settings.json # tests/js/test.js is used for testing changes locally -test/js/test.js +tests/js/test.js diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index d543072c095..ff6e6cbb22e 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -546,6 +546,7 @@ fn is_trimmable_whitespace(c: char) -> bool { pub fn trim(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str: String = ctx.value_to_rust_string(this); + println!("{:?}", this_str); Ok(to_value(this_str.trim_matches(is_trimmable_whitespace))) } diff --git a/src/lib/exec.rs b/src/lib/exec.rs index 7cca7d0d9cc..9e15ce6e115 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -549,13 +549,16 @@ impl Interpreter { /// https://tc39.es/ecma262/#sec-ordinarytoprimitive fn ordinary_to_primitive(&mut self, o: &Value, hint: &str) -> Value { + println!("{:?}", o.get_type()); debug_assert!(o.get_type() == "object"); + println!("{:?}", hint); debug_assert!(hint == "string" || hint == "number"); let method_names: Vec<&str> = if hint == "string" { vec!["toString", "valueOf"] } else { vec!["valueOf", "toString"] }; + println!("{:?}", method_names); for name in method_names.iter() { let method: Value = o.get_field_slice(name); if method.is_function() { @@ -682,6 +685,32 @@ impl Interpreter { _ => String::from("undefined"), } } + + pub fn value_to_rust_number(&mut self, value: &Value) -> f64 { + match *value.deref().borrow() { + ValueData::Null => f64::from(0), + ValueData::Boolean(boolean) => match boolean { + false => f64::from(0), + _ => f64::from(1), + }, + ValueData::Number(num) => num, + ValueData::Integer(num) => f64::from(num), + ValueData::String(ref string) => string.parse::().unwrap(), + ValueData::Object(_) => { + println!("It's a object!"); + let prim_value = self.to_primitive(value, Some("number")); + println!("{:?}", prim_value); + self.to_string(&prim_value) + .to_string() + .parse::() + .unwrap() + } + _ => { + // TODO: Make undefined? + f64::from(0) + } + } + } } #[cfg(test)] diff --git a/src/lib/js/number.rs b/src/lib/js/number.rs index 7656932aec8..bbd08f54988 100644 --- a/src/lib/js/number.rs +++ b/src/lib/js/number.rs @@ -1,63 +1,175 @@ use crate::{ exec::Interpreter, - js::value::{ResultValue, Value}, + js::{ + function::NativeFunctionData, + object::{Object, ObjectKind, PROTOTYPE}, + value::{to_value, ResultValue, Value, ValueData}, + }, }; +use std::{borrow::Borrow, f64, ops::Deref}; + +/// Helper function: to_number(value: &Value) -> Value +/// +/// Converts a Value to a Number. +fn to_number(value: &Value) -> Value { + match *value.deref().borrow() { + ValueData::Boolean(b) => match b { + true => to_value(1), + false => to_value(0), + }, + ValueData::Function(_) | ValueData::Undefined => to_value(f64::NAN), + ValueData::Integer(i) => to_value(f64::from(i)), + ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"), + ValueData::Null => to_value(0), + ValueData::Number(n) => to_value(f64::from(n)), + ValueData::String(ref s) => match s.parse::() { + Ok(n) => to_value(f64::from(n)), + Err(_) => to_value(f64::NAN), + }, + } +} + +/// Helper function: num_to_exponential(n: f64) -> String +/// +/// Formats a float as a ES6-style exponential number string. +fn num_to_exponential(n: f64) -> String { + match n.abs() { + x if x > 1.0 => format!("{:e}", n).replace("e", "e+"), + x if x == 0.0 => format!("{:e}", n).replace("e", "e+"), + _ => format!("{:e}", n), + } +} +/// Number(arg) +/// /// Create a new number [[Construct]] -pub fn make_number(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + let data = match args.get(0) { + Some(n) => n.clone(), + None => to_value(0), + }; + this.set_internal_slot("NumberData", data); + Ok(this.clone()) } +/// Number() +/// /// https://tc39.es/ecma262/#sec-number-constructor-number-value -pub fn call_number(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +pub fn call_number(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + match args.get(0) { + Some(ref value) => Ok(to_number(value)), + None => Ok(to_number(&to_value(0))), + } } +/// Number().toExponential() +/// /// https://tc39.es/ecma262/#sec-number.prototype.toexponential -pub fn to_expotential(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +pub fn to_exponential(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + let this_num = to_number(this).to_num(); + let this_str_num = num_to_exponential(this_num); + Ok(to_value(this_str_num)) } /// https://tc39.es/ecma262/#sec-number.prototype.tofixed -pub fn to_fixed(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +pub fn to_fixed(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + let this_num = to_number(this).to_num(); + let precision = match args.get(0) { + Some(n) => match n.to_int() { + x if x > 0 => n.to_int() as usize, + _ => 0, + }, + None => 0, + }; + let this_fixed_num = format!("{:.*}", precision, this_num); + Ok(to_value(this_fixed_num)) } +/// Number().toLocaleString() +/// /// https://tc39.es/ecma262/#sec-number.prototype.tolocalestring -pub fn to_locale_string(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +/// +/// Note that while this technically conforms to the Ecma standard, it does no actual +/// internationalization logic. +pub fn to_locale_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + let this_num = to_number(this).to_num(); + let this_str_num = format!("{}", this_num); + Ok(to_value(this_str_num)) } +/// Number().toPrecision(p) +/// /// https://tc39.es/ecma262/#sec-number.prototype.toprecision -pub fn to_precision(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +pub fn to_precision(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + println!("Number::to_precision()"); + let this_num = to_number(this); + let _num_str_len = format!("{}", this_num.to_num()).len(); + let _precision = match args.get(0) { + Some(n) => match n.to_int() { + x if x > 0 => n.to_int() as usize, + _ => 0, + }, + None => 0, + }; + // TODO: Implement toPrecision + unimplemented!(); } +/// Number().toString() +/// /// https://tc39.es/ecma262/#sec-number.prototype.tostring -pub fn to_string(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +pub fn to_string(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + Ok(to_value(format!("{}", to_number(this).to_num()))) } +/// Number().valueOf() +/// /// https://tc39.es/ecma262/#sec-number.prototype.valueof -pub fn value_of(_this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - unimplemented!() +pub fn value_of(this: &Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + Ok(to_number(this)) } /// Create a new `Number` object -pub fn create_constructor(_global: &Value) -> Value { - unimplemented!() +pub fn create_constructor(global: &Value) -> Value { + let mut number_constructor = Object::default(); + number_constructor.kind = ObjectKind::Function; + + number_constructor.set_internal_method("construct", make_number); + number_constructor.set_internal_method("call", call_number); + + let number_prototype = ValueData::new_obj(Some(global)); + + number_prototype.set_internal_slot("NumberData", to_value(0)); + + number_prototype.set_field_slice( + "toExponential", + to_value(to_exponential as NativeFunctionData), + ); + number_prototype.set_field_slice("toFixed", to_value(to_fixed as NativeFunctionData)); + number_prototype.set_field_slice( + "toLocaleString", + to_value(to_locale_string as NativeFunctionData), + ); + number_prototype.set_field_slice("toPrecision", to_value(to_precision as NativeFunctionData)); + number_prototype.set_field_slice("toString", to_value(to_string as NativeFunctionData)); + number_prototype.set_field_slice("valueOf", to_value(value_of as NativeFunctionData)); + + let number = to_value(number_constructor); + number_prototype.set_field_slice("constructor", number.clone()); + number.set_field_slice(PROTOTYPE, number_prototype); + number } -/// Iniitalize the `Number` object on the global object -pub fn init(_global: &Value) { - unimplemented!() +/// Initalize the `Number` object on the global object +pub fn init(global: &Value) { + global.set_field_slice("Number", create_constructor(global)); } #[cfg(test)] mod tests { use super::*; use crate::{exec::Executor, forward, forward_val, js::value::ValueData, realm::Realm}; - use std::f64::NAN; + use std::f64; #[test] fn check_number_constructor_is_function() { @@ -67,23 +179,18 @@ mod tests { } #[test] - fn make_number() { - unimplemented!() - } - - #[test] - pub fn call_number() { + fn call_number() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" - const default_zero = new Number(); - const int_one = new Number(1); - const float_two = new Number(2.1); - const str_three = new Number('3.2'); - const bool_one = new Number(true); - const bool_zero = new Number(true); - const invalid_nan = new Number("I am not a number"); - const from_exp = new Number("2.34e+2"); + const default_zero = Number(); + const int_one = Number(1); + const float_two = Number(2.1); + const str_three = Number('3.2'); + const bool_one = Number(true); + const bool_zero = Number(false); + const invalid_nan = Number("I am not a number"); + const from_exp = Number("2.34e+2"); "#; forward(&mut engine, init); @@ -102,27 +209,12 @@ mod tests { assert_eq!(str_three.to_num(), f64::from(3.2)); assert_eq!(bool_one.to_num(), f64::from(1)); assert_eq!(bool_zero.to_num(), f64::from(0)); - assert_eq!(invalid_nan.to_num(), NAN); + assert!(invalid_nan.to_num().is_nan()); assert_eq!(from_exp.to_num(), f64::from(234)); - - for v in vec![ - &default_zero, - &int_one, - &float_two, - &str_three, - &bool_one, - &invalid_nan, - &from_exp, - ] - .iter() - { - assert_eq!(v.is_object(), true); - assert_eq!(v.is_num(), true); - } } #[test] - pub fn to_expotential() { + fn to_exponential() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -143,16 +235,15 @@ mod tests { let noop_exp = forward(&mut engine, "noop_exp"); assert_eq!(default_exp, String::from("0e+0")); - assert_eq!(int_exp, String::from("5")); + assert_eq!(int_exp, String::from("5e+0")); assert_eq!(float_exp, String::from("1.234e+0")); - assert_eq!(big_exp, String::from("5e+0")); - assert_eq!(nan_exp, String::from("1.234e+3")); - assert_eq!(default_exp, String::from("Nan")); + assert_eq!(big_exp, String::from("1.234e+3")); + assert_eq!(nan_exp, String::from("NaN")); assert_eq!(noop_exp, String::from("1.23e+2")); } #[test] - pub fn to_fixed() { + fn to_fixed() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -178,7 +269,7 @@ mod tests { } #[test] - pub fn to_locale_string() { + fn to_locale_string() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -200,11 +291,11 @@ mod tests { assert_eq!(default_locale, String::from("0")); assert_eq!(small_locale, String::from("5")); assert_eq!(big_locale, String::from("345600")); - assert_eq!(neg_locale, String::from("345600")); + assert_eq!(neg_locale, String::from("-25")); } #[test] - pub fn to_precision() { + fn to_precision() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" @@ -236,14 +327,14 @@ mod tests { } #[test] - pub fn to_string() { + fn to_string() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" const default_string = Number().toString(); const int_string = Number(123).toString(); const float_string = Number(1.234).toString(); - const exp_string = Number(1.2e+4).toString(); + const exp_string = Number("1.2e+4").toString(); const neg_string = Number(-1.2).toString(); "#; @@ -257,20 +348,22 @@ mod tests { assert_eq!(default_string, String::from("0")); assert_eq!(int_string, String::from("123")); assert_eq!(float_string, String::from("1.234")); - assert_eq!(exp_string, String::from("1200")); + assert_eq!(exp_string, String::from("12000")); assert_eq!(neg_string, String::from("-1.2")); } #[test] - pub fn value_of() { + fn value_of() { let realm = Realm::create(); let mut engine = Executor::new(realm); + // TODO: In addition to parsing numbers from strings, parse them bare As of October 2019 + // the parser does not understand scientific e.g., Xe+Y or -Xe-Y notation. let init = r#" const default_val = Number().valueOf(); const int_val = Number("123").valueOf(); const float_val = Number(1.234).valueOf(); - const exp_val = Number(1.2e+4).valueOf() - const neg_val = Number(-1.2e+4).valueOf() + const exp_val = Number("1.2e+4").valueOf() + const neg_val = Number("-1.2e+4").valueOf() "#; forward(&mut engine, init); @@ -280,15 +373,10 @@ mod tests { let exp_val = forward_val(&mut engine, "exp_val").unwrap(); let neg_val = forward_val(&mut engine, "neg_val").unwrap(); - for v in vec![&default_val, &int_val, &float_val, &exp_val, &neg_val].iter() { - assert_eq!(v.is_object(), true); - assert_eq!(v.is_num(), true); - } - assert_eq!(default_val.to_num(), f64::from(0)); assert_eq!(int_val.to_num(), f64::from(123)); assert_eq!(float_val.to_num(), f64::from(1.234)); - assert_eq!(exp_val.to_num(), f64::from(1200)); - assert_eq!(neg_val.to_num(), f64::from(-1200)); + assert_eq!(exp_val.to_num(), f64::from(12000)); + assert_eq!(neg_val.to_num(), f64::from(-12000)); } } diff --git a/src/lib/realm.rs b/src/lib/realm.rs index b42c4d035fa..44ba8ffd8d0 100644 --- a/src/lib/realm.rs +++ b/src/lib/realm.rs @@ -54,14 +54,15 @@ impl Realm { // Create intrinsics, add global objects here function::init(global); - global.set_field_slice("String", string::create_constructor(global)); - global.set_field_slice("RegExp", regexp::create_constructor(global)); global.set_field_slice("Array", array::create_constructor(global)); global.set_field_slice("Boolean", boolean::create_constructor(global)); global.set_field_slice("JSON", json::create_constructor(global)); global.set_field_slice("Math", math::create_constructor(global)); - global.set_field_slice("console", console::create_constructor(global)); + global.set_field_slice("Number", number::create_constructor(global)); global.set_field_slice("Object", object::create_constructor(global)); + global.set_field_slice("RegExp", regexp::create_constructor(global)); + global.set_field_slice("String", string::create_constructor(global)); + global.set_field_slice("console", console::create_constructor(global)); } } diff --git a/tests/js/test.js b/tests/js/test.js index ac0f9d87ba1..e69de29bb2d 100644 --- a/tests/js/test.js +++ b/tests/js/test.js @@ -1,8 +0,0 @@ -let a = { - a: true, - b() { - return 2; - } -}; - -console.log(a["b"]()); From 340291df65b4bab79c80480414d083f2ec933255 Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Sun, 27 Oct 2019 11:48:42 -0700 Subject: [PATCH 4/5] Feature(Number): test compatability with const -> var I don't have all the context on _why_ but all of the tests for `Number` started failing. Upon investigation it was becuase `const` stopped acting the way I expected. Switching my test cases from using `const` to using `var` variable declarations made everything work again. I don't know enough about JS to know if this is a bug or expected behavior. Based on changes to other tests, it is know behavior. Refs #34 --- src/lib/{js => builtins}/number.rs | 106 ++++++++++++++--------------- src/lib/realm.rs | 2 +- 2 files changed, 53 insertions(+), 55 deletions(-) rename src/lib/{js => builtins}/number.rs (82%) diff --git a/src/lib/js/number.rs b/src/lib/builtins/number.rs similarity index 82% rename from src/lib/js/number.rs rename to src/lib/builtins/number.rs index bbd08f54988..3d968e96407 100644 --- a/src/lib/js/number.rs +++ b/src/lib/builtins/number.rs @@ -1,10 +1,10 @@ use crate::{ - exec::Interpreter, - js::{ + builtins::{ function::NativeFunctionData, object::{Object, ObjectKind, PROTOTYPE}, value::{to_value, ResultValue, Value, ValueData}, }, + exec::Interpreter, }; use std::{borrow::Borrow, f64, ops::Deref}; @@ -45,8 +45,8 @@ fn num_to_exponential(n: f64) -> String { /// Create a new number [[Construct]] pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let data = match args.get(0) { - Some(n) => n.clone(), - None => to_value(0), + Some(ref value) => to_number(value), + None => to_number(&to_value(0)), }; this.set_internal_slot("NumberData", data); Ok(this.clone()) @@ -56,10 +56,11 @@ pub fn make_number(this: &Value, args: &[Value], _ctx: &mut Interpreter) -> Resu /// /// https://tc39.es/ecma262/#sec-number-constructor-number-value pub fn call_number(_this: &Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { - match args.get(0) { - Some(ref value) => Ok(to_number(value)), - None => Ok(to_number(&to_value(0))), - } + let data = match args.get(0) { + Some(ref value) => to_number(value), + None => to_number(&to_value(0)), + }; + Ok(data) } /// Number().toExponential() @@ -160,15 +161,10 @@ pub fn create_constructor(global: &Value) -> Value { number } -/// Initalize the `Number` object on the global object -pub fn init(global: &Value) { - global.set_field_slice("Number", create_constructor(global)); -} - #[cfg(test)] mod tests { use super::*; - use crate::{exec::Executor, forward, forward_val, js::value::ValueData, realm::Realm}; + use crate::{builtins::value::ValueData, exec::Executor, forward, forward_val, realm::Realm}; use std::f64; #[test] @@ -183,18 +179,19 @@ mod tests { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" - const default_zero = Number(); - const int_one = Number(1); - const float_two = Number(2.1); - const str_three = Number('3.2'); - const bool_one = Number(true); - const bool_zero = Number(false); - const invalid_nan = Number("I am not a number"); - const from_exp = Number("2.34e+2"); + var default_zero = Number(); + var int_one = Number(1); + var float_two = Number(2.1); + var str_three = Number('3.2'); + var bool_one = Number(true); + var bool_zero = Number(false); + var invalid_nan = Number("I am not a number"); + var from_exp = Number("2.34e+2"); "#; forward(&mut engine, init); let default_zero = forward_val(&mut engine, "default_zero").unwrap(); + println!("{:?}", default_zero); let int_one = forward_val(&mut engine, "int_one").unwrap(); let float_two = forward_val(&mut engine, "float_two").unwrap(); let str_three = forward_val(&mut engine, "str_three").unwrap(); @@ -208,8 +205,8 @@ mod tests { assert_eq!(float_two.to_num(), f64::from(2.1)); assert_eq!(str_three.to_num(), f64::from(3.2)); assert_eq!(bool_one.to_num(), f64::from(1)); - assert_eq!(bool_zero.to_num(), f64::from(0)); assert!(invalid_nan.to_num().is_nan()); + assert_eq!(bool_zero.to_num(), f64::from(0)); assert_eq!(from_exp.to_num(), f64::from(234)); } @@ -218,12 +215,12 @@ mod tests { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" - const default_exp = Number().toExponential(); - const int_exp = Number(5).toExponential(); - const float_exp = Number(1.234).toExponential(); - cont big_exp = Number(1234).toExponential(); - cont nan_exp = Number("I am also not a number").toExponential(); - cont noop_exp = Number("1.23e+2").toExponential(); + var default_exp = Number().toExponential(); + var int_exp = Number(5).toExponential(); + var float_exp = Number(1.234).toExponential(); + var big_exp = Number(1234).toExponential(); + var nan_exp = Number("I am also not a number").toExponential(); + var noop_exp = Number("1.23e+2").toExponential(); "#; forward(&mut engine, init); @@ -247,11 +244,11 @@ mod tests { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" - const default_fixed = Number().toFixed(); - const pos_fixed = Number("3.456e+4").toFixed(); - const neg_fixed = Number("3.456e-4").toFixed(); - const noop_fixed = Number(5).toFixed(); - const nan_fixed = Number("I am not a number").toFixed(); + var default_fixed = Number().toFixed(); + var pos_fixed = Number("3.456e+4").toFixed(); + var neg_fixed = Number("3.456e-4").toFixed(); + var noop_fixed = Number(5).toFixed(); + var nan_fixed = Number("I am not a number").toFixed(); "#; forward(&mut engine, init); @@ -273,10 +270,10 @@ mod tests { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" - const default_locale = Number().toLocaleString(); - const small_locale = Number(5).toLocaleString(); - const big_locale = Number("345600").toLocaleString(); - const neg_locale = Number(-25).toLocaleString(); + var default_locale = Number().toLocaleString(); + var small_locale = Number(5).toLocaleString(); + var big_locale = Number("345600").toLocaleString(); + var neg_locale = Number(-25).toLocaleString(); "#; // TODO: We don't actually do any locale checking here @@ -295,16 +292,17 @@ mod tests { } #[test] + #[ignore] fn to_precision() { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" - const default_precision = Number().toPrecision(); - const low_precision = Number(123456789).toPrecision(1); - const more_precision = Number(123456789).toPrecision(4); - const exact_precision = Number(123456789).toPrecision(9); - const over_precision = Number(123456789).toPrecision(50); - const neg_precision = Number(-123456789).toPrecision(4); + var default_precision = Number().toPrecision(); + var low_precision = Number(123456789).toPrecision(1); + var more_precision = Number(123456789).toPrecision(4); + var exact_precision = Number(123456789).toPrecision(9); + var over_precision = Number(123456789).toPrecision(50); + var neg_precision = Number(-123456789).toPrecision(4); "#; forward(&mut engine, init); @@ -331,11 +329,11 @@ mod tests { let realm = Realm::create(); let mut engine = Executor::new(realm); let init = r#" - const default_string = Number().toString(); - const int_string = Number(123).toString(); - const float_string = Number(1.234).toString(); - const exp_string = Number("1.2e+4").toString(); - const neg_string = Number(-1.2).toString(); + var default_string = Number().toString(); + var int_string = Number(123).toString(); + var float_string = Number(1.234).toString(); + var exp_string = Number("1.2e+4").toString(); + var neg_string = Number(-1.2).toString(); "#; forward(&mut engine, init); @@ -359,11 +357,11 @@ mod tests { // TODO: In addition to parsing numbers from strings, parse them bare As of October 2019 // the parser does not understand scientific e.g., Xe+Y or -Xe-Y notation. let init = r#" - const default_val = Number().valueOf(); - const int_val = Number("123").valueOf(); - const float_val = Number(1.234).valueOf(); - const exp_val = Number("1.2e+4").valueOf() - const neg_val = Number("-1.2e+4").valueOf() + var default_val = Number().valueOf(); + var int_val = Number("123").valueOf(); + var float_val = Number(1.234).valueOf(); + var exp_val = Number("1.2e+4").valueOf() + var neg_val = Number("-1.2e+4").valueOf() "#; forward(&mut engine, init); diff --git a/src/lib/realm.rs b/src/lib/realm.rs index 44ba8ffd8d0..372c7eac13d 100644 --- a/src/lib/realm.rs +++ b/src/lib/realm.rs @@ -5,7 +5,7 @@ //!A realm is represented in this implementation as a Realm struct with the fields specified from the spec use crate::{ builtins::{ - array, boolean, console, function, json, math, object, regexp, string, + array, boolean, console, function, json, math, number, object, regexp, string, value::{Value, ValueData}, }, environment::{ From 179614612615b18e25999285d94ffa61d847a82f Mon Sep 17 00:00:00 2001 From: Elijah Voigt Date: Sun, 27 Oct 2019 11:53:32 -0700 Subject: [PATCH 5/5] Changelog: Number() object. Includes some clippy fixes. Fixes #34 --- CHANGELOG.md | 2 ++ src/lib/builtins/number.rs | 16 +++++++++------- src/lib/builtins/string.rs | 1 - src/lib/exec.rs | 16 +++++++--------- 4 files changed, 18 insertions(+), 17 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af1c6a33ebe..769a7450341 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -46,6 +46,8 @@ Feature enhancements: Implement Array.prototype.fill() (@bojan88) - Array tests: Tests implemented for shift, unshift and reverse, pop and push (@muskuloes) - Demo page has been improved, new font plus change on input. Thanks @WofWca +- [FEATURE #182](https://github.com/jasonwilliams/boa/pull/182): + Implement some Number prototype methods (incl tests) (@pop) Bug fixes: diff --git a/src/lib/builtins/number.rs b/src/lib/builtins/number.rs index 3d968e96407..a0762d9aa06 100644 --- a/src/lib/builtins/number.rs +++ b/src/lib/builtins/number.rs @@ -13,17 +13,20 @@ use std::{borrow::Borrow, f64, ops::Deref}; /// Converts a Value to a Number. fn to_number(value: &Value) -> Value { match *value.deref().borrow() { - ValueData::Boolean(b) => match b { - true => to_value(1), - false => to_value(0), - }, + ValueData::Boolean(b) => { + if b { + to_value(1) + } else { + to_value(0) + } + } ValueData::Function(_) | ValueData::Undefined => to_value(f64::NAN), ValueData::Integer(i) => to_value(f64::from(i)), ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"), ValueData::Null => to_value(0), - ValueData::Number(n) => to_value(f64::from(n)), + ValueData::Number(n) => to_value(n), ValueData::String(ref s) => match s.parse::() { - Ok(n) => to_value(f64::from(n)), + Ok(n) => to_value(n), Err(_) => to_value(f64::NAN), }, } @@ -191,7 +194,6 @@ mod tests { forward(&mut engine, init); let default_zero = forward_val(&mut engine, "default_zero").unwrap(); - println!("{:?}", default_zero); let int_one = forward_val(&mut engine, "int_one").unwrap(); let float_two = forward_val(&mut engine, "float_two").unwrap(); let str_three = forward_val(&mut engine, "str_three").unwrap(); diff --git a/src/lib/builtins/string.rs b/src/lib/builtins/string.rs index ff6e6cbb22e..d543072c095 100644 --- a/src/lib/builtins/string.rs +++ b/src/lib/builtins/string.rs @@ -546,7 +546,6 @@ fn is_trimmable_whitespace(c: char) -> bool { pub fn trim(this: &Value, _: &[Value], ctx: &mut Interpreter) -> ResultValue { let this_str: String = ctx.value_to_rust_string(this); - println!("{:?}", this_str); Ok(to_value(this_str.trim_matches(is_trimmable_whitespace))) } diff --git a/src/lib/exec.rs b/src/lib/exec.rs index 9e15ce6e115..d00a04097ef 100644 --- a/src/lib/exec.rs +++ b/src/lib/exec.rs @@ -549,16 +549,13 @@ impl Interpreter { /// https://tc39.es/ecma262/#sec-ordinarytoprimitive fn ordinary_to_primitive(&mut self, o: &Value, hint: &str) -> Value { - println!("{:?}", o.get_type()); debug_assert!(o.get_type() == "object"); - println!("{:?}", hint); debug_assert!(hint == "string" || hint == "number"); let method_names: Vec<&str> = if hint == "string" { vec!["toString", "valueOf"] } else { vec!["valueOf", "toString"] }; - println!("{:?}", method_names); for name in method_names.iter() { let method: Value = o.get_field_slice(name); if method.is_function() { @@ -689,17 +686,18 @@ impl Interpreter { pub fn value_to_rust_number(&mut self, value: &Value) -> f64 { match *value.deref().borrow() { ValueData::Null => f64::from(0), - ValueData::Boolean(boolean) => match boolean { - false => f64::from(0), - _ => f64::from(1), - }, + ValueData::Boolean(boolean) => { + if boolean { + f64::from(1) + } else { + f64::from(0) + } + } ValueData::Number(num) => num, ValueData::Integer(num) => f64::from(num), ValueData::String(ref string) => string.parse::().unwrap(), ValueData::Object(_) => { - println!("It's a object!"); let prim_value = self.to_primitive(value, Some("number")); - println!("{:?}", prim_value); self.to_string(&prim_value) .to_string() .parse::()