mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
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:
parent
a574f2852e
commit
1822cfe18d
19 changed files with 62 additions and 86 deletions
3
Cargo.lock
generated
3
Cargo.lock
generated
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
|
|||
|
|
@ -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))]
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
|
|
@ -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({})",
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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() {} }",
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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'() {} }
|
||||
|
|
|
|||
|
|
@ -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 };
|
||||
|
|
|
|||
|
|
@ -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) => {
|
||||
|
|
|
|||
|
|
@ -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" {
|
||||
|
|
|
|||
|
|
@ -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;");
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
Loading…
Reference in a new issue