From dd14653521a7395ee2f5f701b86c77488f4a6799 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Tue, 12 May 2020 06:32:13 +0200 Subject: [PATCH 1/6] Added BigInt --- Cargo.lock | 23 ++ boa/Cargo.toml | 1 + boa/src/builtins/bigint/mod.rs | 99 +++++++ boa/src/builtins/bigint/tests.rs | 147 +++++++++++ boa/src/builtins/mod.rs | 2 + boa/src/builtins/number/mod.rs | 1 + boa/src/builtins/number/tests.rs | 11 + boa/src/builtins/object/mod.rs | 19 ++ boa/src/builtins/value/conversions.rs | 26 ++ boa/src/builtins/value/mod.rs | 51 +++- boa/src/builtins/value/operations.rs | 237 +++++++++++++---- boa/src/builtins/value/tests.rs | 8 +- boa/src/exec/mod.rs | 21 +- boa/src/syntax/ast/bigint.rs | 243 ++++++++++++++++++ boa/src/syntax/ast/constant.rs | 10 + boa/src/syntax/ast/mod.rs | 1 + boa/src/syntax/ast/token.rs | 15 +- boa/src/syntax/lexer/mod.rs | 9 +- .../syntax/parser/expression/primary/mod.rs | 3 + 19 files changed, 858 insertions(+), 69 deletions(-) create mode 100644 boa/src/builtins/bigint/mod.rs create mode 100644 boa/src/builtins/bigint/tests.rs create mode 100644 boa/src/syntax/ast/bigint.rs diff --git a/Cargo.lock b/Cargo.lock index b205cea5d19..6c249439ef2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -7,6 +7,7 @@ dependencies = [ "criterion", "gc", "jemallocator", + "num-bigint", "num-traits", "rand", "regex", @@ -387,6 +388,28 @@ dependencies = [ "autocfg", ] +[[package]] +name = "num-bigint" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "090c7f9998ee0ff65aa5b723e4009f7b217707f1fb5ea551329cc4d6231fb304" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", + "serde", +] + +[[package]] +name = "num-integer" +version = "0.1.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f6ea62e9d81a77cd3ee9a2a5b9b609447857f3d358704331e4ef39eb247fcba" +dependencies = [ + "autocfg", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.11" diff --git a/boa/Cargo.toml b/boa/Cargo.toml index dfd749beb7f..2bc5f1350fe 100644 --- a/boa/Cargo.toml +++ b/boa/Cargo.toml @@ -17,6 +17,7 @@ rand = "0.7.3" num-traits = "0.2.11" regex = "1.3.7" rustc-hash = "1.1.0" +num-bigint = {version = "0.2.6", features = ["serde"]} # Optional Dependencies serde = { version = "1.0.106", features = ["derive"], optional = true } diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs new file mode 100644 index 00000000000..ce2a1d27a31 --- /dev/null +++ b/boa/src/builtins/bigint/mod.rs @@ -0,0 +1,99 @@ +//! This module implements the global `BigInt` object. +//! +//! `BigInt` is a built-in object that provides a way to represent whole numbers larger +//! than the largest number JavaScript can reliably represent with the Number primitive +//! and represented by the `Number.MAX_SAFE_INTEGER` constant. +//! `BigInt` can be used for arbitrarily large integers. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-bigint-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt + +use crate::{ + builtins::{ + function::make_constructor_fn, + value::{ResultValue, Value}, + }, + exec::Interpreter, + syntax::ast::bigint::BigInt, +}; + +#[cfg(test)] +mod tests; + +/// `BigInt()` function. +/// +/// More Information https://tc39.es/ecma262/#sec-number-constructor-number-value +pub fn make_bigint(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + let data = match args.get(0) { + Some(ref value) => { + if let Some(bigint) = value.to_bigint() { + Value::from(bigint) + } else { + panic!("RangeError: The value cannot be converted to a BigInt because it is not an integer"); + } + } + None => Value::from(BigInt::from(0)), + }; + Ok(data) +} + +/// `BigInt.prototype.toString( [radix] )` +/// +/// The `toString()` method returns a string representing the specified BigInt object. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.tostring +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/toString +pub fn to_string(this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + let radix = if !args.is_empty() { + args[0].to_integer() + } else { + 10 + }; + if radix < 2 && radix > 36 { + panic!("RangeError: toString() radix argument must be between 2 and 36"); + } + Ok(Value::from( + this.to_bigint().unwrap().to_str_radix(radix as u32), + )) +} + +// /// `BigInt.prototype.valueOf()` +// /// +// /// The `valueOf()` method returns the wrapped primitive value of a Number object. +// /// +// /// More information: +// /// - [ECMAScript reference][spec] +// /// - [MDN documentation][mdn] +// /// +/// [spec]: https://tc39.es/ecma262/#sec-bigint.prototype.valueof +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/valueOf +pub fn value_of(this: &mut Value, _args: &[Value], _ctx: &mut Interpreter) -> ResultValue { + Ok(Value::from( + this.to_bigint().expect("BigInt.prototype.valueOf"), + )) +} + +/// Create a new `Number` object +pub fn create(global: &Value) -> Value { + let prototype = Value::new_object(Some(global)); + prototype.set_internal_slot("BigIntData", Value::from(BigInt::from(0))); + + make_builtin_fn!(to_string, named "toString", with length 1, of prototype); + make_builtin_fn!(value_of, named "valueOf", of prototype); + + make_constructor_fn(make_bigint, global, prototype) +} + +/// Initialise the `BigInt` object on the global object. +#[inline] +pub fn init(global: &Value) { + global.set_field_slice("BigInt", create(global)); +} diff --git a/boa/src/builtins/bigint/tests.rs b/boa/src/builtins/bigint/tests.rs new file mode 100644 index 00000000000..8bcfa7df52d --- /dev/null +++ b/boa/src/builtins/bigint/tests.rs @@ -0,0 +1,147 @@ +use crate::{forward, Executor, Realm}; + +#[test] +fn equality() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "0n == 0n"), "true"); + assert_eq!(forward(&mut engine, "1n == 0n"), "false"); + assert_eq!( + forward( + &mut engine, + "1000000000000000000000000000000000n == 1000000000000000000000000000000000n" + ), + "true" + ); + + assert_eq!(forward(&mut engine, "0n == ''"), "true"); + assert_eq!(forward(&mut engine, "100n == '100'"), "true"); + assert_eq!(forward(&mut engine, "100n == '100.5'"), "false"); + assert_eq!( + forward(&mut engine, "10000000000000000n == '10000000000000000'"), + "true" + ); + + assert_eq!(forward(&mut engine, "'' == 0n"), "true"); + assert_eq!(forward(&mut engine, "'100' == 100n"), "true"); + assert_eq!(forward(&mut engine, "'100.5' == 100n"), "false"); + assert_eq!( + forward(&mut engine, "'10000000000000000' == 10000000000000000n"), + "true" + ); + + assert_eq!(forward(&mut engine, "0n == 0"), "true"); + assert_eq!(forward(&mut engine, "0n == 0.0"), "true"); + assert_eq!(forward(&mut engine, "100n == 100"), "true"); + assert_eq!(forward(&mut engine, "100n == 100.0"), "true"); + assert_eq!(forward(&mut engine, "100n == '100.5'"), "false"); + assert_eq!(forward(&mut engine, "100n == '1005'"), "false"); + assert_eq!( + forward(&mut engine, "10000000000000000n == 10000000000000000"), + "true" + ); + + assert_eq!(forward(&mut engine, "0 == 0n"), "true"); + assert_eq!(forward(&mut engine, "0.0 == 0n"), "true"); + assert_eq!(forward(&mut engine, "100 == 100n"), "true"); + assert_eq!(forward(&mut engine, "100.0 == 100n"), "true"); + assert_eq!(forward(&mut engine, "100.5 == 100n"), "false"); + assert_eq!(forward(&mut engine, "1005 == 100n"), "false"); + assert_eq!( + forward(&mut engine, "10000000000000000 == 10000000000000000n"), + "true" + ); +} + +#[test] +fn bigint_function_conversion() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "BigInt(1000)"), "1000n"); + assert_eq!( + forward(&mut engine, "BigInt(20000000000000000)"), + "20000000000000000n" + ); + assert_eq!( + forward(&mut engine, "BigInt(1000000000000000000000000000000000)"), + "999999999999999945575230987042816n" + ); +} + +#[test] +fn add() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "10000n + 1000n"), "11000n"); +} + +#[test] +fn sub() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "10000n - 1000n"), "9000n"); +} + +#[test] +fn mul() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!( + forward(&mut engine, "123456789n * 102030n"), + "12596296181670n" + ); +} + +#[test] +fn div() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "15000n / 50n"), "300n"); +} + +#[test] +fn div_with_truncation() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "15001n / 50n"), "300n"); +} + +#[test] +fn r#mod() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "15007n % 10n"), "7n"); +} + +#[test] +fn pow() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!( + forward(&mut engine, "100n ** 10n"), + "100000000000000000000n" + ); +} + +#[test] +fn to_string() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(forward(&mut engine, "1000n.toString()"), "1000"); + + assert_eq!(forward(&mut engine, "1000n.toString(2)"), "1111101000"); + + assert_eq!(forward(&mut engine, "255n.toString(16)"), "ff"); + + assert_eq!(forward(&mut engine, "1000n.toString(36)"), "rs"); +} diff --git a/boa/src/builtins/mod.rs b/boa/src/builtins/mod.rs index 532b74c8b87..912c5000e7c 100644 --- a/boa/src/builtins/mod.rs +++ b/boa/src/builtins/mod.rs @@ -22,6 +22,7 @@ macro_rules! make_builtin_fn { } pub mod array; +pub mod bigint; pub mod boolean; pub mod console; pub mod error; @@ -42,6 +43,7 @@ use value::Value; #[inline] pub fn init(global: &Value) { array::init(global); + bigint::init(global); boolean::init(global); json::init(global); math::init(global); diff --git a/boa/src/builtins/number/mod.rs b/boa/src/builtins/number/mod.rs index e990c5ef1ed..0da8860f5ac 100644 --- a/boa/src/builtins/number/mod.rs +++ b/boa/src/builtins/number/mod.rs @@ -42,6 +42,7 @@ fn to_number(value: &Value) -> Value { ValueData::Object(ref o) => (o).deref().borrow().get_internal_slot("NumberData"), ValueData::Null => Value::from(0), ValueData::Rational(n) => Value::from(n), + ValueData::BigInt(ref bigint) => Value::from(bigint.to_f64()), ValueData::String(ref s) => match s.parse::() { Ok(n) => Value::from(n), Err(_) => Value::from(f64::NAN), diff --git a/boa/src/builtins/number/tests.rs b/boa/src/builtins/number/tests.rs index 1b5a1b24b85..e99ff550799 100644 --- a/boa/src/builtins/number/tests.rs +++ b/boa/src/builtins/number/tests.rs @@ -426,3 +426,14 @@ fn same_value_zero() { assert_eq!(super::same_value_zero(0.0, f64::NAN), false); assert_eq!(super::equals(1.0, 1.0), true); } + +#[test] +fn from_bigint() { + let realm = Realm::create(); + let mut engine = Executor::new(realm); + + assert_eq!(&forward(&mut engine, "Number(0n)"), "0",); + assert_eq!(&forward(&mut engine, "Number(100000n)"), "100000",); + assert_eq!(&forward(&mut engine, "Number(100000n)"), "100000",); + assert_eq!(&forward(&mut engine, "Number(1n << 1240n)"), "Infinity",); +} diff --git a/boa/src/builtins/object/mod.rs b/boa/src/builtins/object/mod.rs index 6731a857bb8..88afd80c24b 100644 --- a/boa/src/builtins/object/mod.rs +++ b/boa/src/builtins/object/mod.rs @@ -431,6 +431,22 @@ impl Object { obj } + /// Return a new `BigInt` object whose `[[BigIntData]]` internal slot is set to argument. + fn from_bigint(argument: &Value) -> Self { + let mut obj = Self { + kind: ObjectKind::BigInt, + internal_slots: FxHashMap::default(), + properties: FxHashMap::default(), + sym_properties: FxHashMap::default(), + state: None, + func: None, + }; + + obj.internal_slots + .insert("BigIntData".to_string(), argument.clone()); + obj + } + /// Converts the `Value` to an `Object` type. /// /// More information: @@ -442,6 +458,7 @@ impl Object { ValueData::Boolean(_) => Ok(Self::from_boolean(value)), ValueData::Rational(_) => Ok(Self::from_number(value)), ValueData::String(_) => Ok(Self::from_string(value)), + ValueData::BigInt(_) => Ok(Self::from_bigint(value)), ValueData::Object(ref obj) => Ok((*obj).deref().borrow().clone()), _ => Err(()), } @@ -479,6 +496,7 @@ pub enum ObjectKind { Ordinary, Boolean, Number, + BigInt, } impl Display for ObjectKind { @@ -495,6 +513,7 @@ impl Display for ObjectKind { Self::Ordinary => "Ordinary", Self::Boolean => "Boolean", Self::Number => "Number", + Self::BigInt => "BigInt", } ) } diff --git a/boa/src/builtins/value/conversions.rs b/boa/src/builtins/value/conversions.rs index 72ab5da5c63..b86094a6e5a 100644 --- a/boa/src/builtins/value/conversions.rs +++ b/boa/src/builtins/value/conversions.rs @@ -76,6 +76,32 @@ impl From<&Value> for i32 { } } +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)] +pub struct TryFromBigIntError; + +impl Display for TryFromBigIntError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not convert value to a BigInt type") + } +} + +impl TryFrom<&Value> for BigInt { + type Error = TryFromBigIntError; + + fn try_from(value: &Value) -> Result { + match value.data() { + ValueData::BigInt(ref bigint) => Ok(bigint.clone()), + _ => Err(TryFromBigIntError), + } + } +} + +impl From for Value { + fn from(value: BigInt) -> Self { + Value::bigint(value) + } +} + impl From for Value { fn from(value: usize) -> Value { Value::integer(value as i32) diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index b87af5a27cb..cd7f47494c5 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -13,14 +13,16 @@ use crate::builtins::{ }, property::Property, }; +use crate::syntax::ast::bigint::BigInt; use gc::{Finalize, Gc, GcCell, GcCellRef, Trace}; use serde_json::{map::Map, Number as JSONNumber, Value as JSONValue}; use std::{ any::Any, collections::HashSet, + convert::TryFrom, f64::NAN, fmt::{self, Display}, - ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Not, Rem, Shl, Shr, Sub}, + ops::{Add, BitAnd, BitOr, BitXor, Deref, DerefMut, Div, Mul, Neg, Not, Rem, Shl, Shr, Sub}, str::FromStr, }; @@ -86,6 +88,12 @@ impl Value { Self::rational(value.into()) } + /// Creates a new bigint value. + #[inline] + pub fn bigint(value: BigInt) -> Self { + Self(Gc::new(ValueData::BigInt(value))) + } + /// Creates a new boolean value. #[inline] pub fn boolean(value: bool) -> Self { @@ -106,7 +114,10 @@ impl Value { /// Helper function to convert the `Value` to a number and compute its power. pub fn as_num_to_power(&self, other: Self) -> Self { - Self::rational(self.to_number().powf(other.to_number())) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => Self::bigint(a.clone().pow(b)), + (a, b) => Self::rational(a.to_number().powf(b.to_number())), + } } /// Returns a new empty object @@ -163,6 +174,8 @@ pub enum ValueData { Rational(f64), /// `Number` - A 32-bit integer, such as `42` Integer(i32), + /// --- + BigInt(BigInt), /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values Object(Box>), /// `Symbol` - A Symbol Type - Internally Symbols are similar to objects, except there are no properties, only internal slots @@ -290,6 +303,7 @@ impl ValueData { Self::Rational(n) if n != 0.0 && !n.is_nan() => true, Self::Integer(n) if n != 0 => true, Self::Boolean(v) => v, + Self::BigInt(ref n) if *n != 0 => true, _ => false, } } @@ -312,6 +326,9 @@ impl ValueData { 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") + } } } @@ -330,6 +347,30 @@ impl ValueData { 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") + } + } + } + + /// Helper function. + pub fn to_bigint(&self) -> Option { + match self { + Self::String(ref string) => string_to_bigint(string), + Self::Boolean(true) => Some(BigInt::from(1)), + Self::Boolean(false) | Self::Null => Some(BigInt::from(0)), + Self::Rational(num) => BigInt::try_from(*num).ok(), + Self::Integer(num) => Some(BigInt::from(*num)), + ValueData::BigInt(b) => Some(b.clone()), + ValueData::Object(ref o) => { + let object = (o).deref().borrow(); + if object.kind == ObjectKind::BigInt { + object.get_internal_slot("BigIntData").to_bigint() + } else { + None + } + } + _ => None, } } @@ -698,6 +739,10 @@ impl ValueData { JSONNumber::from_f64(num).expect("Could not convert to JSONNumber"), ), Self::Integer(val) => JSONValue::Number(JSONNumber::from(val)), + Self::BigInt(_) => { + // TODO: throw TypeError + panic!("TypeError: \"BigInt value can't be serialized in JSON\""); + } } } @@ -719,6 +764,7 @@ impl ValueData { "object" } } + Self::BigInt(_) => "bigint", } } } @@ -938,6 +984,7 @@ impl Display for ValueData { ), Self::Object(_) => write!(f, "{}", log_string_from(self, true)), Self::Integer(v) => write!(f, "{}", v), + Self::BigInt(ref num) => write!(f, "{}n", num), } } } diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index c42e3831bc4..a85ce840f7c 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -3,6 +3,7 @@ use crate::builtins::number; use crate::Interpreter; use std::borrow::Borrow; +use std::convert::TryFrom; impl Value { /// Strict equality comparison. @@ -25,14 +26,22 @@ impl Value { /// /// This method is executed when doing abstract equality comparisons with the `==` operator. /// For more information, check + #[allow(clippy::float_cmp)] pub fn equals(&mut self, other: &mut Self, interpreter: &mut Interpreter) -> bool { + // 1. If Type(x) is the same as Type(y), then + // a. Return the result of performing Strict Equality Comparison x === y. if self.get_type() == other.get_type() { return self.strict_equals(other); } match (self.data(), other.data()) { + // 2. If x is null and y is undefined, return true. + // 3. If x is undefined and y is null, return true. _ if self.is_null_or_undefined() && other.is_null_or_undefined() => true, + // 3. If Type(x) is Number and Type(y) is String, return the result of the comparison x == ! ToNumber(y). + // 4. If Type(x) is String and Type(y) is Number, return the result of the comparison ! ToNumber(x) == y. + // // https://github.com/rust-lang/rust/issues/54883 (ValueData::Integer(_), ValueData::String(_)) | (ValueData::Rational(_), ValueData::String(_)) @@ -44,25 +53,121 @@ impl Value { let b: &Value = other.borrow(); number::equals(f64::from(a), f64::from(b)) } + + // 6. If Type(x) is BigInt and Type(y) is String, then + // a. Let n be ! StringToBigInt(y). + // b. If n is NaN, return false. + // c. Return the result of the comparison x == n. + (ValueData::BigInt(ref a), ValueData::String(ref b)) => match string_to_bigint(b) { + Some(ref b) => a == b, + None => false, + }, + + // 7. If Type(x) is String and Type(y) is BigInt, return the result of the comparison y == x. + (ValueData::String(ref a), ValueData::BigInt(ref b)) => match string_to_bigint(a) { + Some(ref a) => a == b, + None => false, + }, + + // 8. If Type(x) is Boolean, return the result of the comparison ! ToNumber(x) == y. (ValueData::Boolean(_), _) => { other.equals(&mut Value::from(self.to_integer()), interpreter) } + + // 9. If Type(y) is Boolean, return the result of the comparison x == ! ToNumber(y). (_, ValueData::Boolean(_)) => { self.equals(&mut Value::from(other.to_integer()), interpreter) } + + // 10. If Type(x) is either String, Number, BigInt, or Symbol and Type(y) is Object, return the result + // of the comparison x == ? ToPrimitive(y). (ValueData::Object(_), _) => { let mut primitive = interpreter.to_primitive(self, None); primitive.equals(other, interpreter) } + + // 11. If Type(x) is Object and Type(y) is either String, Number, BigInt, or Symbol, return the result + // of the comparison ? ToPrimitive(x) == y. (_, ValueData::Object(_)) => { let mut primitive = interpreter.to_primitive(other, None); primitive.equals(self, interpreter) } + + // 12. If Type(x) is BigInt and Type(y) is Number, or if Type(x) is Number and Type(y) is BigInt, then + // a. If x or y are any of NaN, +∞, or -∞, return false. + // b. If the mathematical value of x is equal to the mathematical value of y, return true; otherwise return false. + (ValueData::BigInt(ref a), ValueData::Rational(ref b)) => a == b, + (ValueData::Rational(ref a), ValueData::BigInt(ref b)) => a == b, + (ValueData::BigInt(ref a), ValueData::Integer(ref b)) => a == b, + (ValueData::Integer(ref a), ValueData::BigInt(ref b)) => a == b, + + // 13. Return false. _ => false, } } } +/// This function takes a string and conversts it to BigInt type. +/// +/// If the result is NaN than None is returned. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// +/// [spec]: https://tc39.es/ecma262/#sec-stringtobigint +pub fn string_to_bigint(string: &str) -> Option { + if string.is_empty() { + return Some(BigInt::from(0)); + } + + BigInt::from_str(string).ok() +} + +/// The internal comparison abstract operation SameValue(x, y), +/// where x and y are ECMAScript language values, produces true or false. +/// Such a comparison is performed as follows: +/// +/// https://tc39.es/ecma262/#sec-samevalue +/// strict mode currently compares the pointers +pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { + if strict { + // Do both Values point to the same underlying valueData? + let x_ptr = Gc::into_raw(x.0.clone()); + let y_ptr = Gc::into_raw(y.0.clone()); + return x_ptr == y_ptr; + } + + if x.get_type() != y.get_type() { + return false; + } + + // TODO: check BigInt + // https://github.com/jasonwilliams/boa/pull/358 + if x.is_number() { + return number::same_value(f64::from(x), f64::from(y)); + } + + same_value_non_number(x, y) +} + +pub fn same_value_non_number(x: &Value, y: &Value) -> bool { + debug_assert!(x.get_type() == y.get_type()); + match x.get_type() { + "undefined" => true, + "null" => true, + "string" => { + if x.to_string() == y.to_string() { + return true; + } + false + } + "bigint" => BigInt::try_from(x).unwrap() == BigInt::try_from(y).unwrap(), + "boolean" => bool::from(x) == bool::from(y), + "object" => std::ptr::eq(x, y), + _ => false, + } +} + impl Add for Value { type Output = Self; fn add(self, other: Self) -> Self { @@ -70,6 +175,9 @@ impl Add for Value { (ValueData::String(ref s), ref o) => { Self::string(format!("{}{}", s.clone(), &o.to_string())) } + (ValueData::BigInt(ref n1), ValueData::BigInt(ref n2)) => { + Self::bigint(n1.clone() + n2.clone()) + } (ref s, ValueData::String(ref o)) => Self::string(format!("{}{}", s.to_string(), o)), (ref s, ref o) => Self::rational(s.to_number() + o.to_number()), } @@ -78,55 +186,101 @@ impl Add for Value { impl Sub for Value { type Output = Self; fn sub(self, other: Self) -> Self { - Self::rational(self.to_number() - other.to_number()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() - b.clone()) + } + (a, b) => Self::rational(a.to_number() - b.to_number()), + } } } impl Mul for Value { type Output = Self; fn mul(self, other: Self) -> Self { - Self::rational(self.to_number() * other.to_number()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() * b.clone()) + } + (a, b) => Self::rational(a.to_number() * b.to_number()), + } } } impl Div for Value { type Output = Self; fn div(self, other: Self) -> Self { - Self::rational(self.to_number() / other.to_number()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() / b.clone()) + } + (a, b) => Self::rational(a.to_number() / b.to_number()), + } } } impl Rem for Value { type Output = Self; fn rem(self, other: Self) -> Self { - Self::rational(self.to_number() % other.to_number()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() % b.clone()) + } + (a, b) => Self::rational(a.to_number() % b.to_number()), + } } } impl BitAnd for Value { type Output = Self; fn bitand(self, other: Self) -> Self { - Self::integer(self.to_integer() & other.to_integer()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() & b.clone()) + } + (a, b) => Self::integer(a.to_integer() & b.to_integer()), + } } } impl BitOr for Value { type Output = Self; fn bitor(self, other: Self) -> Self { - Self::integer(self.to_integer() | other.to_integer()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() | b.clone()) + } + (a, b) => Self::integer(a.to_integer() | b.to_integer()), + } } } impl BitXor for Value { type Output = Self; fn bitxor(self, other: Self) -> Self { - Self::integer(self.to_integer() ^ other.to_integer()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() ^ b.clone()) + } + (a, b) => Self::integer(a.to_integer() ^ b.to_integer()), + } } } + impl Shl for Value { type Output = Self; fn shl(self, other: Self) -> Self { - Self::integer(self.to_integer() << other.to_integer()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() << b.clone()) + } + (a, b) => Self::integer(a.to_integer() << b.to_integer()), + } } } impl Shr for Value { type Output = Self; fn shr(self, other: Self) -> Self { - Self::integer(self.to_integer() >> other.to_integer()) + match (self.data(), other.data()) { + (ValueData::BigInt(ref a), ValueData::BigInt(ref b)) => { + Self::bigint(a.clone() >> b.clone()) + } + (a, b) => Self::integer(a.to_integer() >> b.to_integer()), + } } } impl Not for Value { @@ -136,6 +290,27 @@ impl Not for Value { } } +impl Neg for Value { + type Output = Self; + + fn neg(self) -> Self::Output { + match self.data() { + ValueData::Object(_) | ValueData::Symbol(_) | ValueData::Undefined => { + Self::rational(NAN) + } + ValueData::String(ref str) => Self::rational(match f64::from_str(str) { + Ok(num) => -num, + Err(_) => NAN, + }), + ValueData::Rational(num) => Self::rational(-num), + ValueData::Integer(num) => Self::rational(-f64::from(*num)), + ValueData::Boolean(true) => Self::integer(1), + ValueData::Boolean(false) | ValueData::Null => Self::integer(0), + ValueData::BigInt(ref num) => Self::bigint(-num.clone()), + } + } +} + /// The internal comparison abstract operation SameValueZero(x, y), /// where x and y are ECMAScript language values, produces true or false. /// SameValueZero differs from SameValue only in its treatment of +0 and -0. @@ -154,47 +329,3 @@ pub fn same_value_zero(x: &Value, y: &Value) -> bool { same_value_non_number(x, y) } - -/// The internal comparison abstract operation SameValue(x, y), -/// where x and y are ECMAScript language values, produces true or false. -/// Such a comparison is performed as follows: -/// -/// https://tc39.es/ecma262/#sec-samevalue -/// strict mode currently compares the pointers -pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { - if strict { - // Do both Values point to the same underlying valueData? - let x_ptr = Gc::into_raw(x.0.clone()); - let y_ptr = Gc::into_raw(y.0.clone()); - return x_ptr == y_ptr; - } - - if x.get_type() != y.get_type() { - return false; - } - - // TODO: check BigInt - // https://github.com/jasonwilliams/boa/pull/358 - if x.is_number() { - return number::same_value(f64::from(x), f64::from(y)); - } - - same_value_non_number(x, y) -} - -pub fn same_value_non_number(x: &Value, y: &Value) -> bool { - debug_assert!(x.get_type() == y.get_type()); - match x.get_type() { - "undefined" => true, - "null" => true, - "string" => { - if x.to_string() == y.to_string() { - return true; - } - false - } - "boolean" => bool::from(x) == bool::from(y), - "object" => std::ptr::eq(x, y), - _ => false, - } -} diff --git a/boa/src/builtins/value/tests.rs b/boa/src/builtins/value/tests.rs index bfccf0fe438..de8aa425841 100644 --- a/boa/src/builtins/value/tests.rs +++ b/boa/src/builtins/value/tests.rs @@ -83,14 +83,12 @@ fn abstract_equality_comparison() { ); assert_eq!(forward(&mut engine, "0 == null"), "false"); - // https://github.com/jasonwilliams/boa/issues/357 assert_eq!(forward(&mut engine, "0 == '-0'"), "true"); assert_eq!(forward(&mut engine, "0 == '+0'"), "true"); assert_eq!(forward(&mut engine, "'+0' == 0"), "true"); assert_eq!(forward(&mut engine, "'-0' == 0"), "true"); - // https://github.com/jasonwilliams/boa/issues/393 - // assert_eq!(forward(&mut engine, "0 == NaN"), "false"); - // assert_eq!(forward(&mut engine, "'foo' == NaN"), "false"); - // assert_eq!(forward(&mut engine, "NaN == NaN"), "false"); + assert_eq!(forward(&mut engine, "0 == NaN"), "false"); + assert_eq!(forward(&mut engine, "'foo' == NaN"), "false"); + assert_eq!(forward(&mut engine, "NaN == NaN"), "false"); } diff --git a/boa/src/exec/mod.rs b/boa/src/exec/mod.rs index edc324c39c2..f61e55a062a 100644 --- a/boa/src/exec/mod.rs +++ b/boa/src/exec/mod.rs @@ -74,6 +74,7 @@ impl Executor for Interpreter { Node::Const(Const::Undefined) => Ok(Value::undefined()), Node::Const(Const::Num(num)) => Ok(Value::rational(num)), Node::Const(Const::Int(num)) => Ok(Value::integer(num)), + Node::Const(Const::BigInt(ref num)) => Ok(Value::from(num.clone())), // we can't move String from Const into value, because const is a garbage collected value // Which means Drop() get's called on Const, but str will be gone at that point. // Do Const values need to be garbage collected? We no longer need them once we've generated Values @@ -316,7 +317,7 @@ impl Executor for Interpreter { Node::UnaryOp(ref op, ref a) => { let v_a = self.run(a)?; Ok(match *op { - UnaryOp::Minus => Value::from(-v_a.to_number()), + UnaryOp::Minus => -v_a, UnaryOp::Plus => Value::from(v_a.to_number()), UnaryOp::IncrementPost => { let ret = v_a.clone(); @@ -564,6 +565,7 @@ impl Executor for Interpreter { "object" } } + ValueData::BigInt(_) => "bigint", })) } Node::StatementList(ref list) => { @@ -716,7 +718,9 @@ impl Interpreter { _ => input.clone(), } } - /// to_string() converts a value into a String + + /// Converts a value into a `String`. + /// /// https://tc39.es/ecma262/#sec-tostring #[allow(clippy::wrong_self_convention)] pub fn to_string(&mut self, value: &Value) -> Value { @@ -727,6 +731,7 @@ impl Interpreter { ValueData::Rational(ref num) => Value::from(num.to_string()), ValueData::Integer(ref num) => Value::from(num.to_string()), ValueData::String(ref string) => Value::from(string.clone()), + ValueData::BigInt(ref bigint) => Value::from(bigint.to_string()), ValueData::Object(_) => { let prim_value = self.to_primitive(&mut (value.clone()), Some("string")); self.to_string(&prim_value) @@ -800,6 +805,16 @@ impl Interpreter { Ok(string_obj) } ValueData::Object(_) | ValueData::Symbol(_) => Ok(value.clone()), + ValueData::BigInt(_) => { + let proto = self + .realm + .environment + .get_binding_value("BigInt") + .get_field_slice(PROTOTYPE); + let bigint_obj = Value::new_object_from_prototype(proto, ObjectKind::BigInt); + bigint_obj.set_internal_slot("BigIntData", value.clone()); + Ok(bigint_obj) + } } } @@ -811,6 +826,7 @@ impl Interpreter { ValueData::Rational(ref num) => num.to_string(), ValueData::Integer(ref num) => num.to_string(), ValueData::String(ref string) => string.clone(), + ValueData::BigInt(ref bigint) => bigint.to_string(), ValueData::Object(_) => { let prim_value = self.to_primitive(&mut (value.clone()), Some("string")); self.to_string(&prim_value).to_string() @@ -832,6 +848,7 @@ impl Interpreter { ValueData::Rational(num) => num, ValueData::Integer(num) => f64::from(num), ValueData::String(ref string) => string.parse::().unwrap(), + ValueData::BigInt(ref bigint) => bigint.to_f64(), ValueData::Object(_) => { let prim_value = self.to_primitive(&mut (value.clone()), Some("number")); self.to_string(&prim_value) diff --git a/boa/src/syntax/ast/bigint.rs b/boa/src/syntax/ast/bigint.rs new file mode 100644 index 00000000000..f88e0d0a1cb --- /dev/null +++ b/boa/src/syntax/ast/bigint.rs @@ -0,0 +1,243 @@ +//! This module implements the `BigInt` structure, which represents the BigInt values in JavaScript. +//! +//! More information: +//! - [ECMAScript reference][spec] +//! - [MDN documentation][mdn] +//! +//! [spec]: https://tc39.es/ecma262/#sec-bigint-objects +//! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt + +use gc::{unsafe_empty_trace, Finalize, Trace}; +use num_traits::cast::{FromPrimitive, ToPrimitive}; +use num_traits::pow::Pow; + +use std::convert::TryFrom; +use std::str::FromStr; + +#[cfg(feature = "serde")] +use serde::{Deserialize, Serialize}; + +#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] +#[derive(Clone, Hash, PartialEq, Eq, PartialOrd, Ord, Default)] +pub struct BigInt(num_bigint::BigInt); + +impl BigInt { + /// Returns the inner bigint structure. + #[inline] + pub fn into_inner(self) -> num_bigint::BigInt { + self.0 + } + + /// Converts a string to a BigInt with the specified radix. + #[inline] + pub fn from_str_radix(buf: &str, radix: u32) -> Option { + num_bigint::BigInt::parse_bytes(buf.as_bytes(), radix).map(Self) + } + + #[inline] + pub fn pow(self, other: &Self) -> Self { + Self( + self.0.pow( + other + .0 + .to_biguint() + .expect("RangeError: \"BigInt negative exponent\""), + ), + ) + } + + /// Converts the BigInt to a f64 type. + /// + /// Returns `std::f64::INFINITY` if the BigInt is too big. + #[inline] + pub fn to_f64(&self) -> f64 { + self.0.to_f64().unwrap_or(std::f64::INFINITY) + } +} + +impl From for BigInt { + fn from(n: i64) -> BigInt { + BigInt(num_bigint::BigInt::from(n)) + } +} + +impl From for BigInt { + fn from(n: i32) -> BigInt { + BigInt(num_bigint::BigInt::from(n)) + } +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct TryFromF64Error; + +impl std::fmt::Display for TryFromF64Error { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not convert f64 value to a BigInt type") + } +} + +impl TryFrom for BigInt { + type Error = TryFromF64Error; + + fn try_from(n: f64) -> Result { + match num_bigint::BigInt::from_f64(n) { + Some(bigint) => Ok(BigInt(bigint)), + None => Err(TryFromF64Error), + } + } +} + +#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, PartialOrd, Ord)] +pub struct ParseBigIntError; + +impl std::fmt::Display for ParseBigIntError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "Could not parse to BigInt type") + } +} + +impl FromStr for BigInt { + type Err = ParseBigIntError; + + fn from_str(string: &str) -> Result { + match num_bigint::BigInt::from_str(string) { + Ok(bigint) => Ok(BigInt(bigint)), + Err(_) => Err(ParseBigIntError), + } + } +} + +macro_rules! impl_bigint_operator { + ($op:ident, $op_method:ident, $assign_op:ident, $assign_op_method:ident) => { + impl std::ops::$op for BigInt { + type Output = Self; + + fn $op_method(mut self, other: Self) -> Self { + std::ops::$assign_op::$assign_op_method(&mut self.0, other.0); + self + } + } + }; +} + +impl_bigint_operator!(Add, add, AddAssign, add_assign); +impl_bigint_operator!(Sub, sub, SubAssign, sub_assign); +impl_bigint_operator!(Mul, mul, MulAssign, mul_assign); +impl_bigint_operator!(Div, div, DivAssign, div_assign); +impl_bigint_operator!(Rem, rem, RemAssign, rem_assign); +impl_bigint_operator!(BitAnd, bitand, BitAndAssign, bitand_assign); +impl_bigint_operator!(BitOr, bitor, BitOrAssign, bitor_assign); +impl_bigint_operator!(BitXor, bitxor, BitXorAssign, bitxor_assign); + +impl std::ops::Shr for BigInt { + type Output = Self; + + fn shr(mut self, other: Self) -> Self::Output { + use std::ops::ShlAssign; + use std::ops::ShrAssign; + + if let Some(n) = other.0.to_i32() { + if n > 0 { + self.0.shr_assign(n as usize) + } else { + self.0.shl_assign(n.abs() as usize) + } + + return self; + } + + panic!("RangeError: Maximum BigInt size exceeded"); + } +} + +impl std::ops::Shl for BigInt { + type Output = Self; + + fn shl(mut self, other: Self) -> Self::Output { + use std::ops::ShlAssign; + use std::ops::ShrAssign; + + if let Some(n) = other.0.to_i32() { + if n > 0 { + self.0.shl_assign(n as usize) + } else { + self.0.shr_assign(n.abs() as usize) + } + + return self; + } + + panic!("RangeError: Maximum BigInt size exceeded"); + } +} + +impl std::ops::Neg for BigInt { + type Output = Self; + + fn neg(self) -> Self::Output { + Self(-self.0) + } +} + +impl PartialEq for BigInt { + fn eq(&self, other: &i32) -> bool { + self.0 == num_bigint::BigInt::from(*other) + } +} + +impl PartialEq for i32 { + fn eq(&self, other: &BigInt) -> bool { + num_bigint::BigInt::from(*self) == other.0 + } +} + +impl PartialEq for BigInt { + fn eq(&self, other: &f64) -> bool { + if other.fract() != 0.0 { + return false; + } + + self.0 == num_bigint::BigInt::from(*other as i64) + } +} + +impl PartialEq for f64 { + fn eq(&self, other: &BigInt) -> bool { + if self.fract() != 0.0 { + return false; + } + + num_bigint::BigInt::from(*self as i64) == other.0 + } +} + +impl std::fmt::Debug for BigInt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::fmt::Display for BigInt { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.0) + } +} + +impl std::ops::Deref for BigInt { + type Target = num_bigint::BigInt; + + fn deref(&self) -> &Self::Target { + &self.0 + } +} + +impl std::ops::DerefMut for BigInt { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.0 + } +} + +impl Finalize for BigInt {} +unsafe impl Trace for BigInt { + unsafe_empty_trace!(); +} diff --git a/boa/src/syntax/ast/constant.rs b/boa/src/syntax/ast/constant.rs index 02805233def..3dd6213643d 100644 --- a/boa/src/syntax/ast/constant.rs +++ b/boa/src/syntax/ast/constant.rs @@ -7,6 +7,7 @@ //! [spec]: https://tc39.es/ecma262/#sec-primary-expression-literals //! [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Literals +use crate::syntax::ast::bigint::BigInt; use gc::{Finalize, Trace}; use std::fmt::{Display, Formatter, Result}; @@ -64,6 +65,8 @@ pub enum Const { /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals Int(i32), + BigInt(BigInt), + /// The Boolean type has two literal values: `true` and `false`. /// /// The Boolean object is a wrapper around the primitive Boolean data type. @@ -131,6 +134,12 @@ impl From for Const { } } +impl From for Const { + fn from(i: BigInt) -> Self { + Self::BigInt(i) + } +} + impl From for Const { fn from(b: bool) -> Self { Self::Bool(b) @@ -143,6 +152,7 @@ impl Display for Const { Self::String(ref st) => write!(f, "\"{}\"", st), Self::Num(num) => write!(f, "{}", num), Self::Int(num) => write!(f, "{}", num), + Self::BigInt(ref num) => write!(f, "{}", num), Self::Bool(v) => write!(f, "{}", v), Self::Null => write!(f, "null"), Self::Undefined => write!(f, "undefined"), diff --git a/boa/src/syntax/ast/mod.rs b/boa/src/syntax/ast/mod.rs index 7d1d1c7fd17..21669bcdbbe 100644 --- a/boa/src/syntax/ast/mod.rs +++ b/boa/src/syntax/ast/mod.rs @@ -1,5 +1,6 @@ //! The Javascript Abstract Syntax Tree. +pub mod bigint; pub mod constant; pub mod keyword; pub mod node; diff --git a/boa/src/syntax/ast/token.rs b/boa/src/syntax/ast/token.rs index 0df76ac7738..d87380bb45c 100644 --- a/boa/src/syntax/ast/token.rs +++ b/boa/src/syntax/ast/token.rs @@ -5,7 +5,7 @@ //! //! [spec]: https://tc39.es/ecma262/#sec-tokens -use crate::syntax::ast::{keyword::Keyword, pos::Position, punc::Punctuator}; +use crate::syntax::ast::{bigint::BigInt, keyword::Keyword, pos::Position, punc::Punctuator}; use std::fmt::{Debug, Display, Formatter, Result}; #[cfg(feature = "serde")] @@ -58,14 +58,16 @@ impl Debug for VecToken { /// Represents the type differenct types of numeric literals. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] -#[derive(Clone, Copy, PartialEq, Debug)] +#[derive(Clone, PartialEq, Debug)] pub enum NumericLiteral { /// A floating point number Rational(f64), /// An integer Integer(i32), - // TODO: Add BigInt + + // A BigInt + BigInt(BigInt), } impl From for NumericLiteral { @@ -80,6 +82,12 @@ impl From for NumericLiteral { } } +impl From for NumericLiteral { + fn from(n: BigInt) -> Self { + Self::BigInt(n) + } +} + /// Represents the type of Token and the data it has inside. #[cfg_attr(feature = "serde", derive(Serialize, Deserialize))] #[derive(Clone, PartialEq, Debug)] @@ -207,6 +215,7 @@ impl Display for TokenKind { Self::NullLiteral => write!(f, "null"), Self::NumericLiteral(NumericLiteral::Rational(num)) => write!(f, "{}", num), Self::NumericLiteral(NumericLiteral::Integer(num)) => write!(f, "{}", num), + Self::NumericLiteral(NumericLiteral::BigInt(ref num)) => write!(f, "{}n", num), Self::Punctuator(ref punc) => write!(f, "{}", punc), Self::StringLiteral(ref lit) => write!(f, "{}", lit), Self::RegularExpressionLiteral(ref body, ref flags) => write!(f, "/{}/{}", body, flags), diff --git a/boa/src/syntax/lexer/mod.rs b/boa/src/syntax/lexer/mod.rs index c0696df90d9..4296e8c9d1c 100644 --- a/boa/src/syntax/lexer/mod.rs +++ b/boa/src/syntax/lexer/mod.rs @@ -6,6 +6,7 @@ #[cfg(test)] mod tests; +use crate::syntax::ast::bigint::BigInt; use crate::syntax::ast::{ punc::Punctuator, token::{NumericLiteral, Token, TokenKind}, @@ -395,10 +396,10 @@ impl<'a> Lexer<'a> { }; let num = match kind { - NumericKind::BigInt(_) => { - // TODO: Implement bigint. - // NOTE: implementation goes here. - unimplemented!("BigInt"); + NumericKind::BigInt(base) => { + NumericLiteral::BigInt( + BigInt::from_str_radix(&buf, base).expect("Could not conver to BigInt") + ) } NumericKind::Rational /* base: 10 */ => { NumericLiteral::Rational( diff --git a/boa/src/syntax/parser/expression/primary/mod.rs b/boa/src/syntax/parser/expression/primary/mod.rs index 0022ddacc6c..34c1b8f635a 100644 --- a/boa/src/syntax/parser/expression/primary/mod.rs +++ b/boa/src/syntax/parser/expression/primary/mod.rs @@ -85,6 +85,9 @@ impl TokenParser for PrimaryExpression { TokenKind::StringLiteral(s) => Ok(Node::const_node(s)), TokenKind::NumericLiteral(NumericLiteral::Integer(num)) => Ok(Node::const_node(*num)), TokenKind::NumericLiteral(NumericLiteral::Rational(num)) => Ok(Node::const_node(*num)), + TokenKind::NumericLiteral(NumericLiteral::BigInt(num)) => { + Ok(Node::const_node(num.clone())) + } TokenKind::RegularExpressionLiteral(body, flags) => Ok(Node::new(Node::call( Node::local("RegExp"), vec![Node::const_node(body), Node::const_node(flags)], From cbbc0088218a2eb0ad7772957e37e4d330dd3b7e Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Sun, 17 May 2020 17:37:49 +0200 Subject: [PATCH 2/6] Fixed some things --- boa/src/builtins/bigint/mod.rs | 11 +++++++++-- boa/src/builtins/value/mod.rs | 16 ++++++++-------- boa/src/builtins/value/operations.rs | 4 +--- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/boa/src/builtins/bigint/mod.rs b/boa/src/builtins/bigint/mod.rs index ce2a1d27a31..403aae28d9b 100644 --- a/boa/src/builtins/bigint/mod.rs +++ b/boa/src/builtins/bigint/mod.rs @@ -24,9 +24,16 @@ use crate::{ #[cfg(test)] mod tests; -/// `BigInt()` function. +/// `BigInt()` /// -/// More Information https://tc39.es/ecma262/#sec-number-constructor-number-value +/// The `BigInt()` constructor is used to create BigInt objects. +/// +/// More information: +/// - [ECMAScript reference][spec] +/// - [MDN documentation][mdn] +/// +/// [spec]: https://tc39.es/ecma262/#sec-bigint-objects +/// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/BigInt/BigInt pub fn make_bigint(_this: &mut Value, args: &[Value], _ctx: &mut Interpreter) -> ResultValue { let data = match args.get(0) { Some(ref value) => { diff --git a/boa/src/builtins/value/mod.rs b/boa/src/builtins/value/mod.rs index cd7f47494c5..5fe5ee74ed5 100644 --- a/boa/src/builtins/value/mod.rs +++ b/boa/src/builtins/value/mod.rs @@ -162,23 +162,23 @@ impl Display for Value { /// A Javascript value #[derive(Trace, Finalize, Debug, Clone)] pub enum ValueData { - /// `null` - A null value, for when a value doesn't exist + /// `null` - A null value, for when a value doesn't exist. Null, - /// `undefined` - An undefined value, for when a field or index doesn't exist + /// `undefined` - An undefined value, for when a field or index doesn't exist. Undefined, - /// `boolean` - A `true` / `false` value, for if a certain criteria is met + /// `boolean` - A `true` / `false` value, for if a certain criteria is met. Boolean(bool), - /// `String` - A UTF-8 string, such as `"Hello, world"` + /// `String` - A UTF-8 string, such as `"Hello, world"`. String(String), /// `Number` - A 64-bit floating point number, such as `3.1415` Rational(f64), - /// `Number` - A 32-bit integer, such as `42` + /// `Number` - A 32-bit integer, such as `42`. Integer(i32), - /// --- + /// `BigInt` - holds any arbitrary large signed integer. BigInt(BigInt), - /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values + /// `Object` - An object, such as `Math`, represented by a binary tree of string keys to Javascript values. Object(Box>), - /// `Symbol` - A Symbol Type - Internally Symbols are similar to objects, except there are no properties, only internal slots + /// `Symbol` - A Symbol Type - Internally Symbols are similar to objects, except there are no properties, only internal slots. Symbol(Box>), } diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index a85ce840f7c..91b9917ce05 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -132,9 +132,7 @@ pub fn string_to_bigint(string: &str) -> Option { pub fn same_value(x: &Value, y: &Value, strict: bool) -> bool { if strict { // Do both Values point to the same underlying valueData? - let x_ptr = Gc::into_raw(x.0.clone()); - let y_ptr = Gc::into_raw(y.0.clone()); - return x_ptr == y_ptr; + return std::ptr::eq(x.data(), y.data()); } if x.get_type() != y.get_type() { From 8ad01737581fb4a36b0b57f6f0b01d6a083f7ec3 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 18 May 2020 16:36:45 +0200 Subject: [PATCH 3/6] Updated comment Co-authored-by: Iban Eguia --- boa/src/builtins/value/operations.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/builtins/value/operations.rs b/boa/src/builtins/value/operations.rs index 91b9917ce05..10eefde1bb4 100644 --- a/boa/src/builtins/value/operations.rs +++ b/boa/src/builtins/value/operations.rs @@ -109,7 +109,7 @@ impl Value { /// This function takes a string and conversts it to BigInt type. /// -/// If the result is NaN than None is returned. +/// If the result is `NaN` than `None` is returned. /// /// More information: /// - [ECMAScript reference][spec] From 9d010c4a6ef860458196ca932aded0b5212c53a8 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 18 May 2020 16:39:05 +0200 Subject: [PATCH 4/6] Commented some things --- boa/src/syntax/ast/bigint.rs | 3 +++ boa/src/syntax/ast/constant.rs | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/boa/src/syntax/ast/bigint.rs b/boa/src/syntax/ast/bigint.rs index f88e0d0a1cb..e7d2a3b371d 100644 --- a/boa/src/syntax/ast/bigint.rs +++ b/boa/src/syntax/ast/bigint.rs @@ -239,5 +239,8 @@ impl std::ops::DerefMut for BigInt { impl Finalize for BigInt {} unsafe impl Trace for BigInt { + // BigInt type implements an empty trace becasue the inner structure + // `num_bigint::BigInt` does not implement `Trace` trait. + // If it did this would be unsound. unsafe_empty_trace!(); } diff --git a/boa/src/syntax/ast/constant.rs b/boa/src/syntax/ast/constant.rs index 3dd6213643d..cb415fbf7b2 100644 --- a/boa/src/syntax/ast/constant.rs +++ b/boa/src/syntax/ast/constant.rs @@ -61,10 +61,19 @@ pub enum Const { /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals Int(i32), + /// BigInt provides a way to represent whole numbers larger than the largest number JavaScript + /// can reliably represent with the `Number` primitive. + /// + /// More information: + /// - [ECMAScript reference][spec] + /// - [MDN documentation][mdn] + /// + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-boolean-value + /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals BigInt(BigInt), /// The Boolean type has two literal values: `true` and `false`. From c484766632ab3304262e5fb72ed7b55a99a7f231 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 18 May 2020 16:40:21 +0200 Subject: [PATCH 5/6] fixed rustfmt issues --- boa/src/syntax/ast/bigint.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/boa/src/syntax/ast/bigint.rs b/boa/src/syntax/ast/bigint.rs index e7d2a3b371d..c432ca01f52 100644 --- a/boa/src/syntax/ast/bigint.rs +++ b/boa/src/syntax/ast/bigint.rs @@ -239,7 +239,7 @@ impl std::ops::DerefMut for BigInt { impl Finalize for BigInt {} unsafe impl Trace for BigInt { - // BigInt type implements an empty trace becasue the inner structure + // BigInt type implements an empty trace becasue the inner structure // `num_bigint::BigInt` does not implement `Trace` trait. // If it did this would be unsound. unsafe_empty_trace!(); From 9036958d183d37e1f28113ba7126acf6962e3793 Mon Sep 17 00:00:00 2001 From: HalidOdat Date: Mon, 18 May 2020 16:43:45 +0200 Subject: [PATCH 6/6] fixed spec link --- boa/src/syntax/ast/constant.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/boa/src/syntax/ast/constant.rs b/boa/src/syntax/ast/constant.rs index cb415fbf7b2..9f117825d4a 100644 --- a/boa/src/syntax/ast/constant.rs +++ b/boa/src/syntax/ast/constant.rs @@ -61,7 +61,7 @@ pub enum Const { /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-number-value /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals Int(i32), @@ -72,7 +72,7 @@ pub enum Const { /// - [ECMAScript reference][spec] /// - [MDN documentation][mdn] /// - /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-boolean-value + /// [spec]: https://tc39.es/ecma262/#sec-terms-and-definitions-bigint-value /// [mdn]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Grammar_and_types#Numeric_literals BigInt(BigInt),