refactor(ast): fix BigInt memory leak by removing it (#2293)

relates

We'll need to evaluate the value by other means.
This commit is contained in:
Boshen 2024-02-04 16:47:00 +08:00 committed by GitHub
parent a574f2852e
commit 1822cfe18d
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
19 changed files with 62 additions and 86 deletions

3
Cargo.lock generated
View file

@ -1368,7 +1368,6 @@ version = "0.6.0"
dependencies = [
"bitflags 2.4.2",
"num-bigint",
"num-traits",
"oxc_allocator",
"oxc_index",
"oxc_span",
@ -1425,7 +1424,6 @@ name = "oxc_codegen"
version = "0.6.0"
dependencies = [
"bitflags 2.4.2",
"num-bigint",
"oxc_allocator",
"oxc_ast",
"oxc_parser",
@ -1531,7 +1529,6 @@ dependencies = [
"lazy_static",
"memchr",
"mime_guess",
"num-traits",
"once_cell",
"oxc_allocator",
"oxc_ast",

View file

@ -25,7 +25,6 @@ oxc_index = { workspace = true }
bitflags = { workspace = true }
num-bigint = { workspace = true }
num-traits = { workspace = true }
serde = { workspace = true, features = ["derive"], optional = true }
serde_json = { workspace = true, optional = true }

View file

@ -247,13 +247,11 @@ impl<'a> Expression<'a> {
/// Returns literal's value converted to the Boolean type
/// returns `true` when node is truthy, `false` when node is falsy, `None` when it cannot be determined.
pub fn get_boolean_value(&self) -> Option<bool> {
use num_traits::Zero;
match self {
Self::BooleanLiteral(lit) => Some(lit.value),
Self::NullLiteral(_) => Some(false),
Self::NumberLiteral(lit) => Some(lit.value != 0.0),
Self::BigintLiteral(lit) => Some(!lit.value.is_zero()),
Self::BigintLiteral(lit) => Some(!lit.is_zero()),
Self::RegExpLiteral(_) => Some(true),
Self::StringLiteral(lit) => Some(!lit.value.is_empty()),
_ => None,
@ -453,7 +451,7 @@ impl<'a> PropertyKey<'a> {
Expression::StringLiteral(lit) => Some(lit.value.clone()),
Expression::RegExpLiteral(lit) => Some(Atom::from(lit.regex.to_string())),
Expression::NumberLiteral(lit) => Some(Atom::from(lit.value.to_string())),
Expression::BigintLiteral(lit) => Some(Atom::from(lit.value.to_string())),
Expression::BigintLiteral(lit) => Some(lit.raw.clone()),
Expression::NullLiteral(_) => Some("null".into()),
Expression::TemplateLiteral(lit) => {
lit.expressions.is_empty().then(|| lit.quasi()).flatten().cloned()

View file

@ -6,7 +6,6 @@ use std::{
};
use bitflags::bitflags;
use num_bigint::BigInt;
use oxc_span::{Atom, Span};
use oxc_syntax::{BigintBase, NumberBase};
#[cfg(feature = "serde")]
@ -107,18 +106,23 @@ impl<'a> Hash for NumberLiteral<'a> {
}
}
#[derive(Debug, Clone, Hash)]
#[derive(Debug, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]
pub struct BigintLiteral {
#[cfg_attr(feature = "serde", serde(flatten))]
pub span: Span,
#[cfg_attr(feature = "serde", serde(serialize_with = "crate::serialize::serialize_bigint"))]
pub value: BigInt,
pub raw: Atom,
#[cfg_attr(feature = "serde", serde(skip))]
pub base: BigintBase,
}
impl BigintLiteral {
pub fn is_zero(&self) -> bool {
self.raw == "0n"
}
}
#[derive(Debug, Clone, Hash)]
#[cfg_attr(feature = "serde", derive(Serialize), serde(tag = "type"))]
#[cfg_attr(all(feature = "serde", feature = "wasm"), derive(tsify::Tsify))]

View file

@ -5,7 +5,6 @@
clippy::unused_self,
)]
use num_bigint::BigInt;
use std::mem;
use oxc_allocator::{Allocator, Box, String, Vec};
@ -136,8 +135,8 @@ impl<'a> AstBuilder<'a> {
BooleanLiteral { span, value }
}
pub fn bigint_literal(&self, span: Span, value: BigInt, base: BigintBase) -> BigintLiteral {
BigintLiteral { span, value, base }
pub fn bigint_literal(&self, span: Span, raw: Atom, base: BigintBase) -> BigintLiteral {
BigintLiteral { span, raw, base }
}
pub fn template_literal(

View file

@ -539,7 +539,7 @@ impl<'a> AstKind<'a> {
Self::StringLiteral(s) => format!("NumberLiteral({})", s.value).into(),
Self::BooleanLiteral(b) => format!("BooleanLiteral({})", b.value).into(),
Self::NullLiteral(_) => "NullLiteral".into(),
Self::BigintLiteral(b) => format!("BigintLiteral({})", b.value).into(),
Self::BigintLiteral(b) => format!("BigintLiteral({})", b.raw).into(),
Self::RegExpLiteral(r) => format!("RegExpLiteral({})", r.regex).into(),
Self::TemplateLiteral(t) => format!(
"TemplateLiteral({})",

View file

@ -1,5 +1,3 @@
use std::fmt;
use serde::{ser::Serializer, Serialize};
use crate::ast::{Program, RegExpFlags};
@ -28,14 +26,6 @@ impl<'a> Program<'a> {
}
}
pub fn serialize_bigint<T, S>(value: &T, s: S) -> Result<S::Ok, S::Error>
where
T: fmt::Display,
S: serde::Serializer,
{
s.collect_str(&format_args!("{value}n"))
}
impl Serialize for RegExpFlags {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where

View file

@ -24,8 +24,7 @@ oxc_span = { workspace = true }
oxc_allocator = { workspace = true }
oxc_syntax = { workspace = true }
bitflags = { workspace = true }
num-bigint = { workspace = true }
bitflags = { workspace = true }
[dev-dependencies]
oxc_parser = { workspace = true }

View file

@ -1062,13 +1062,11 @@ fn print_non_negative_float<const MINIFY: bool>(value: f64, _p: &Codegen<{ MINIF
impl<const MINIFY: bool> Gen<MINIFY> for BigintLiteral {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) {
use num_bigint::Sign;
if self.value.sign() == Sign::Minus {
p.print_space_before_operator(Operator::Unary(UnaryOperator::UnaryNegation));
if self.raw.contains('_') {
p.print_str(self.raw.replace('_', "").as_bytes());
} else {
p.print_str(self.raw.as_bytes());
}
p.print_str(self.value.to_string().as_bytes());
p.print(b'n');
}
}

View file

@ -38,7 +38,6 @@ serde = { workspace = true }
regex = { workspace = true }
rustc-hash = { workspace = true }
phf = { workspace = true, features = ["macros"] }
num-traits = { workspace = true }
itertools = { workspace = true }
dashmap = { workspace = true }
convert_case = { workspace = true }

View file

@ -58,7 +58,6 @@ impl NoCompareNegZero {
}
fn is_neg_zero(expr: &Expression) -> bool {
use num_traits::Zero;
let Expression::UnaryExpression(unary) = expr.get_inner_expression() else {
return false;
};
@ -67,7 +66,7 @@ fn is_neg_zero(expr: &Expression) -> bool {
}
match &unary.argument {
Expression::NumberLiteral(number) => number.value == 0.0,
Expression::BigintLiteral(bigint) => bigint.value.is_zero(),
Expression::BigintLiteral(bigint) => bigint.is_zero(),
_ => false,
}
}

View file

@ -177,6 +177,8 @@ fn test() {
"class A { get foo() {} get bar() {} get baz() {} }",
"class A { 1() {} 2() {} }",
"class Foo { foo(a: string): string; foo(a: number): number; foo(a: any): any {} }",
// NOTE: This should fail when we get read the big int value
"class A { [123n]() {} 123() {} }",
];
let fail = vec![
@ -201,7 +203,6 @@ fn test() {
"class A { [100]() {} [1e2]() {} }",
"class A { [123.00]() {} [`123`]() {} }",
"class A { static '65'() {} static [0o101]() {} }",
"class A { [123n]() {} 123() {} }",
"class A { [null]() {} 'null'() {} }",
"class A { foo() {} foo() {} foo() {} }",
"class A { static foo() {} static foo() {} }",

View file

@ -81,6 +81,8 @@ fn test() {
// Syntax:error: the '0' prefixed octal literals is not allowed.
// ("var x = { 012: 1, 12: 2 };", None),
("var x = { 1_0: 1, 1: 2 };", None),
// NOTE: This should fail when we get read the big int value
("var x = { 1n: 1, 1: 2 };", None),
];
let fail = vec![
@ -92,7 +94,6 @@ fn test() {
("var x = { 012: 1, 10: 2 };", None),
("var x = { 0b1: 1, 1: 2 };", None),
("var x = { 0o1: 1, 1: 2 };", None),
("var x = { 1n: 1, 1: 2 };", None),
("var x = { 1_0: 1, 10: 2 };", None),
("var x = { \"z\": 1, z: 2 };", None),
("var foo = {\n bar: 1,\n bar: 1,\n}", None),

View file

@ -192,15 +192,6 @@ expression: no_dupe_class_members
╰────
help: The last declaration overwrites previous ones, remove one of them or rename if both should be retained
⚠ eslint(no-dupe-class-members): Duplicate class member: "123"
╭─[no_dupe_class_members.tsx:1:12]
1 │ class A { [123n]() {} 123() {} }
· ──┬─ ─┬─
· │ ╰── "123" is re-declared here
· ╰── "123" is previously declared here
╰────
help: The last declaration overwrites previous ones, remove one of them or rename if both should be retained
⚠ eslint(no-dupe-class-members): Duplicate class member: "null"
╭─[no_dupe_class_members.tsx:1:12]
1 │ class A { [null]() {} 'null'() {} }

View file

@ -59,13 +59,6 @@ expression: no_dupe_keys
╰────
help: Consider removing the duplicated key
⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals
╭─[no_dupe_keys.tsx:1:11]
1 │ var x = { 1n: 1, 1: 2 };
· ── ─
╰────
help: Consider removing the duplicated key
⚠ eslint(no-dupe-keys): Disallow duplicate keys in object literals
╭─[no_dupe_keys.tsx:1:11]
1 │ var x = { 1_0: 1, 10: 2 };

View file

@ -390,7 +390,10 @@ pub fn get_bigint_value(expr: &Expression) -> Option<BigInt> {
None
}
}
Expression::BigintLiteral(bigint_literal) => Some(bigint_literal.value.clone()),
Expression::BigintLiteral(_bigint_literal) => {
// TODO: evaluate the bigint value
None
}
Expression::BooleanLiteral(bool_literal) => {
if bool_literal.value {
Some(BigInt::one())
@ -465,7 +468,7 @@ pub fn get_boolean_value(expr: &Expression) -> Option<bool> {
Expression::NullLiteral(_) => Some(false),
Expression::BooleanLiteral(boolean_literal) => Some(boolean_literal.value),
Expression::NumberLiteral(number_literal) => Some(number_literal.value != 0.0),
Expression::BigintLiteral(big_int_literal) => Some(!big_int_literal.value.is_zero()),
Expression::BigintLiteral(big_int_literal) => Some(!big_int_literal.is_zero()),
Expression::StringLiteral(string_literal) => Some(!string_literal.value.is_empty()),
Expression::TemplateLiteral(template_literal) => {
// only for ``
@ -581,7 +584,7 @@ pub fn get_string_value<'a>(expr: &'a Expression) -> Option<Cow<'a, str>> {
Some(Cow::Owned(number_literal.value.to_string()))
}
Expression::BigintLiteral(big_int_literal) => {
Some(Cow::Owned(format!("{}n", big_int_literal.value)))
Some(Cow::Owned(big_int_literal.raw.to_string()))
}
Expression::NullLiteral(_) => Some(Cow::Borrowed("null")),
Expression::BooleanLiteral(bool_literal) => {

View file

@ -2,7 +2,7 @@
//!
//! <https://github.com/google/closure-compiler/blob/master/src/com/google/javascript/jscomp/PeepholeFoldConstants.java>
use std::{cmp::Ordering, mem, ops::Not};
use std::{cmp::Ordering, mem};
use num_bigint::BigInt;
#[allow(clippy::wildcard_imports)]
@ -674,13 +674,12 @@ impl<'a> Compressor<'a> {
);
return Some(self.ast.literal_number_expression(literal));
}
Expression::BigintLiteral(big_int_literal) => {
use std::ops::Neg;
let value = big_int_literal.value.clone().neg();
let literal =
self.ast.bigint_literal(unary_expr.span, value, big_int_literal.base);
return Some(self.ast.literal_bigint_expression(literal));
Expression::BigintLiteral(_big_int_literal) => {
// let value = big_int_literal.value.clone().neg();
// let literal =
// self.ast.bigint_literal(unary_expr.span, value, big_int_literal.base);
// return Some(self.ast.literal_bigint_expression(literal));
return None;
}
Expression::Identifier(ident) => {
if ident.name == "NaN" {
@ -705,11 +704,12 @@ impl<'a> Compressor<'a> {
return Some(self.ast.literal_number_expression(literal));
}
}
Expression::BigintLiteral(big_int_literal) => {
let value = big_int_literal.value.clone().not();
let leteral =
self.ast.bigint_literal(unary_expr.span, value, big_int_literal.base);
return Some(self.ast.literal_bigint_expression(leteral));
Expression::BigintLiteral(_big_int_literal) => {
// let value = big_int_literal.value.clone().not();
// let leteral =
// self.ast.bigint_literal(unary_expr.span, value, big_int_literal.base);
// return Some(self.ast.literal_bigint_expression(leteral));
return None;
}
Expression::Identifier(ident) => {
if ident.name == "NaN" {

View file

@ -128,8 +128,8 @@ fn test_null_comparison1() {
test_wcb("null == 0", "false;");
test_wcb("null == 1", "false;");
test_wcb("null == 0n", "false;");
test_wcb("null == 1n", "false;");
// test_wcb("null == 0n", "false;");
// test_wcb("null == 1n", "false;");
test_wcb("null == 'hi'", "false;");
test_wcb("null == true", "false;");
test_wcb("null == false", "false;");
@ -148,8 +148,8 @@ fn test_null_comparison1() {
test_wcb("null != 0", "true;");
test_wcb("null != 1", "true;");
test_wcb("null != 0n", "true;");
test_wcb("null != 1n", "true;");
// test_wcb("null != 0n", "true;");
// test_wcb("null != 1n", "true;");
test_wcb("null != 'hi'", "true;");
test_wcb("null != true", "true;");
test_wcb("null != false", "true;");
@ -169,16 +169,16 @@ fn test_null_comparison1() {
test_wcb("0 < null", "false;");
test_wcb("0 > null", "false;");
test_wcb("0 >= null", "true;");
test_wcb("0n < null", "false;");
test_wcb("0n > null", "false;");
test_wcb("0n >= null", "true;");
// test_wcb("0n < null", "false;");
// test_wcb("0n > null", "false;");
// test_wcb("0n >= null", "true;");
test_wcb("true > null", "true;");
test_wcb("'hi' < null", "false;");
test_wcb("'hi' >= null", "false;");
test_wcb("null <= null", "true;");
test_wcb("null < 0", "false;");
test_wcb("null < 0n", "false;");
// test_wcb("null < 0n", "false;");
test_wcb("null > true", "false;");
test_wcb("null < 'hi'", "false;");
test_wcb("null >= 'hi'", "false;");
@ -337,6 +337,7 @@ fn test_string_number_comparison() {
}
#[test]
#[ignore]
fn test_bigint_number_comparison() {
test_wcb("1n < 2", "true;");
test_wcb("1n > 2", "false;");
@ -381,6 +382,7 @@ fn test_bigint_number_comparison() {
}
#[test]
#[ignore]
fn test_bigint_string_comparison() {
test_wcb("1n < '2'", "true;");
test_wcb("2n > '1'", "true;");
@ -393,6 +395,7 @@ fn test_bigint_string_comparison() {
}
#[test]
#[ignore]
fn test_string_bigint_comparison() {
test_wcb("'1' < 2n", "true;");
test_wcb("'2' > 1n", "true;");
@ -410,10 +413,10 @@ fn test_nan_comparison() {
test_wcb("NaN <= 1", "false;");
test_wcb("NaN > 1", "false;");
test_wcb("NaN >= 1", "false;");
test_wcb("NaN < 1n", "false;");
test_wcb("NaN <= 1n", "false;");
test_wcb("NaN > 1n", "false;");
test_wcb("NaN >= 1n", "false;");
// test_wcb("NaN < 1n", "false;");
// test_wcb("NaN <= 1n", "false;");
// test_wcb("NaN > 1n", "false;");
// test_wcb("NaN >= 1n", "false;");
test_wcb("NaN < NaN", "false;");
test_wcb("NaN >= NaN", "false;");
@ -500,6 +503,7 @@ fn unary_ops() {
}
#[test]
#[ignore]
fn unary_with_big_int() {
test("-(1n)", "-1n;");
test("- -1n", "1n;");

View file

@ -309,11 +309,12 @@ impl<'a> Parser<'a> {
_ => return Err(self.unexpected()),
};
let token = self.cur_token();
let src = self.cur_src().strip_suffix('n').unwrap();
let value = parse_big_int(src, token.kind)
let raw = self.cur_src();
let src = raw.strip_suffix('n').unwrap();
let _value = parse_big_int(src, token.kind)
.map_err(|err| diagnostics::InvalidNumber(err, token.span()))?;
self.bump_any();
Ok(self.ast.bigint_literal(self.end_span(span), value, base))
Ok(self.ast.bigint_literal(self.end_span(span), Atom::from(raw), base))
}
pub(crate) fn parse_literal_regexp(&mut self) -> RegExpLiteral {