From 07271c2ed9735c7ff88ec94a7df167c27385bd07 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 02:57:08 +0330 Subject: [PATCH 01/16] feat(transformer): numeric separator plugin. --- crates/oxc_transformer/src/es2021/mod.rs | 2 ++ .../src/es2021/numeric_separator.rs | 29 +++++++++++++++++++ crates/oxc_transformer/src/lib.rs | 16 +++++----- crates/oxc_transformer/src/options.rs | 1 + tasks/transform_conformance/src/test_case.rs | 1 + 5 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 crates/oxc_transformer/src/es2021/numeric_separator.rs diff --git a/crates/oxc_transformer/src/es2021/mod.rs b/crates/oxc_transformer/src/es2021/mod.rs index 4818faba8cdf..9601b563fc94 100644 --- a/crates/oxc_transformer/src/es2021/mod.rs +++ b/crates/oxc_transformer/src/es2021/mod.rs @@ -1,3 +1,5 @@ mod logical_assignment_operators; +mod numeric_separator; pub use logical_assignment_operators::LogicalAssignmentOperators; +pub use numeric_separator::NumericSeparator; diff --git a/crates/oxc_transformer/src/es2021/numeric_separator.rs b/crates/oxc_transformer/src/es2021/numeric_separator.rs new file mode 100644 index 000000000000..3d37db9fa253 --- /dev/null +++ b/crates/oxc_transformer/src/es2021/numeric_separator.rs @@ -0,0 +1,29 @@ +use std::marker::PhantomData; + +use oxc_ast::ast::*; + +use crate::{ + context::TransformerCtx, + options::{TransformOptions, TransformTarget}, +}; + +/// ES2021: Numeric Separator +/// +/// References: +/// * +pub struct NumericSeparator<'a>(PhantomData<&'a ()>); + +impl<'a> NumericSeparator<'a> { + pub fn new(_: TransformerCtx<'a>, options: &TransformOptions) -> Option { + (options.target < TransformTarget::ES2021 || options.numeric_separator) + .then_some(Self(PhantomData {})) + } + + #[allow(clippy::unused_self)] + pub fn transform_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { + if !lit.raw.is_empty() { + // set literal raw string to empty so codegen have to use the value. + lit.raw = ""; + } + } +} diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 82e3d204fba4..a4854a049ddd 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -40,7 +40,7 @@ use crate::{ es2016::ExponentiationOperator, es2019::{JsonStrings, OptionalCatchBinding}, es2020::NullishCoalescingOperator, - es2021::LogicalAssignmentOperators, + es2021::{LogicalAssignmentOperators, NumericSeparator}, es2022::ClassStaticBlock, es3::PropertyLiteral, react_jsx::ReactJsx, @@ -69,6 +69,7 @@ pub struct Transformer<'a> { es2022_class_static_block: Option>, // es2021 es2021_logical_assignment_operators: Option>, + es2021_numeric_separator: Option>, // es2020 es2020_nullish_coalescing_operators: Option>, // es2019 @@ -111,6 +112,7 @@ impl<'a> Transformer<'a> { es2022_class_static_block: es2022::ClassStaticBlock::new(Rc::clone(&ast), &options), // es2021 es2021_logical_assignment_operators: LogicalAssignmentOperators::new(Rc::clone(&ast), ctx.clone(), &options), + es2021_numeric_separator: NumericSeparator::new(ctx.clone(), &options), // es2020 es2020_nullish_coalescing_operators: NullishCoalescingOperator::new(Rc::clone(&ast), ctx.clone(), &options), // es2019 @@ -298,15 +300,15 @@ impl<'a> VisitMut<'a> for Transformer<'a> { } fn visit_directive(&mut self, directive: &mut Directive<'a>) { - self.es2019_json_strings - .as_mut() - .map(|t: &mut JsonStrings| t.transform_directive(directive)); + self.es2019_json_strings.as_mut().map(|t| t.transform_directive(directive)); + } + + fn visit_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { + self.es2021_numeric_separator.as_mut().map(|t| t.transform_number_literal(lit)); } fn visit_string_literal(&mut self, lit: &mut StringLiteral) { - self.es2019_json_strings - .as_mut() - .map(|t: &mut JsonStrings| t.transform_string_literal(lit)); + self.es2019_json_strings.as_mut().map(|t| t.transform_string_literal(lit)); } fn visit_method_definition(&mut self, def: &mut MethodDefinition<'a>) { diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index e658cefe3a74..7d844a3aae01 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -18,6 +18,7 @@ pub struct TransformOptions { pub class_static_block: bool, // es2021 pub logical_assignment_operators: bool, + pub numeric_separator: bool, // es2020 pub nullish_coalescing_operator: Option, // es2019 diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 081073e7d49e..0db582b0f1cd 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -108,6 +108,7 @@ pub trait TestCase { logical_assignment_operators: options .get_plugin("transform-logical-assignment-operators") .is_some(), + numeric_separator: options.get_plugin("transform-numeric-separator").is_some(), nullish_coalescing_operator: options .get_plugin("transform-nullish-coalescing-operator") .map(get_options::), From eb54d89dc47f5843ac3505be6b90c6149fafc705 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 03:24:42 +0330 Subject: [PATCH 02/16] refactor(ast): use CompactStr instead of Atom for BigIntLiteral's value. --- crates/oxc_ast/src/ast/js.rs | 2 +- crates/oxc_ast/src/ast/literal.rs | 6 ++++-- crates/oxc_ast/src/ast_builder.rs | 8 ++++---- crates/oxc_parser/src/js/expression.rs | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index 679190744119..a336a7b0607b 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -501,7 +501,7 @@ impl<'a> PropertyKey<'a> { Expression::StringLiteral(lit) => Some(lit.value.to_compact_str()), Expression::RegExpLiteral(lit) => Some(lit.regex.to_string().into()), Expression::NumericLiteral(lit) => Some(lit.value.to_string().into()), - Expression::BigintLiteral(lit) => Some(lit.raw.to_compact_str()), + Expression::BigintLiteral(lit) => Some(lit.raw.clone()), Expression::NullLiteral(_) => Some("null".into()), Expression::TemplateLiteral(lit) => lit .expressions diff --git a/crates/oxc_ast/src/ast/literal.rs b/crates/oxc_ast/src/ast/literal.rs index 8edf72289421..f2556d78a658 100644 --- a/crates/oxc_ast/src/ast/literal.rs +++ b/crates/oxc_ast/src/ast/literal.rs @@ -6,10 +6,11 @@ use std::{ fmt, hash::{Hash, Hasher}, + marker::PhantomData, }; use bitflags::bitflags; -use oxc_span::{Atom, Span}; +use oxc_span::{Atom, CompactStr, Span}; use oxc_syntax::{BigintBase, NumberBase}; #[cfg(feature = "serialize")] use serde::Serialize; @@ -116,9 +117,10 @@ impl<'a> Hash for NumericLiteral<'a> { pub struct BigIntLiteral<'a> { #[cfg_attr(feature = "serialize", serde(flatten))] pub span: Span, - pub raw: Atom<'a>, + pub raw: CompactStr, #[cfg_attr(feature = "serialize", serde(skip))] pub base: BigintBase, + pub(crate) _marker: PhantomData<&'a ()>, } impl<'a> BigIntLiteral<'a> { diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index a0a4600bca19..b2ae36d8507b 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -5,10 +5,10 @@ clippy::unused_self, )] -use std::mem; +use std::{marker::PhantomData, mem}; use oxc_allocator::{Allocator, Box, String, Vec}; -use oxc_span::{Atom, GetSpan, SourceType, Span, SPAN}; +use oxc_span::{Atom, CompactStr, GetSpan, SourceType, Span, SPAN}; use oxc_syntax::{ operator::{ AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator, @@ -140,8 +140,8 @@ impl<'a> AstBuilder<'a> { BooleanLiteral { span, value } } - pub fn bigint_literal(&self, span: Span, raw: Atom<'a>, base: BigintBase) -> BigIntLiteral<'a> { - BigIntLiteral { span, raw, base } + pub fn bigint_literal(&self, span: Span, raw: &'a str, base: BigintBase) -> BigIntLiteral<'a> { + BigIntLiteral { span, raw: CompactStr::new(raw), base, _marker: PhantomData {} } } pub fn template_literal( diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index bf8169244877..0a0a0186b6c9 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -325,7 +325,7 @@ impl<'a> ParserImpl<'a> { 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), Atom::from(raw), base)) + Ok(self.ast.bigint_literal(self.end_span(span), raw, base)) } pub(crate) fn parse_literal_regexp(&mut self) -> RegExpLiteral<'a> { From 6111184e3b64ebb7f8e056aab8f691ed524d9036 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 03:25:13 +0330 Subject: [PATCH 03/16] feat(trans): add support for big int numeric seperators. --- crates/oxc_transformer/src/es2021/numeric_separator.rs | 10 +++++++++- crates/oxc_transformer/src/lib.rs | 4 ++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/oxc_transformer/src/es2021/numeric_separator.rs b/crates/oxc_transformer/src/es2021/numeric_separator.rs index 3d37db9fa253..aabacdb30946 100644 --- a/crates/oxc_transformer/src/es2021/numeric_separator.rs +++ b/crates/oxc_transformer/src/es2021/numeric_separator.rs @@ -14,16 +14,24 @@ use crate::{ pub struct NumericSeparator<'a>(PhantomData<&'a ()>); impl<'a> NumericSeparator<'a> { + #![allow(clippy::unused_self)] + pub fn new(_: TransformerCtx<'a>, options: &TransformOptions) -> Option { (options.target < TransformTarget::ES2021 || options.numeric_separator) .then_some(Self(PhantomData {})) } - #[allow(clippy::unused_self)] pub fn transform_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { if !lit.raw.is_empty() { // set literal raw string to empty so codegen have to use the value. lit.raw = ""; } } + + pub fn transform_bigint_literal(&mut self, lit: &mut BigIntLiteral<'a>) { + let raw = &lit.raw; + if !raw.is_empty() && raw.contains('_') { + lit.raw = raw.replace('_', "").into(); + } + } } diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index a4854a049ddd..d76d2502f745 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -307,6 +307,10 @@ impl<'a> VisitMut<'a> for Transformer<'a> { self.es2021_numeric_separator.as_mut().map(|t| t.transform_number_literal(lit)); } + fn visit_bigint_literal(&mut self, lit: &mut BigIntLiteral<'a>) { + self.es2021_numeric_separator.as_mut().map(|t| t.transform_bigint_literal(lit)); + } + fn visit_string_literal(&mut self, lit: &mut StringLiteral) { self.es2019_json_strings.as_mut().map(|t| t.transform_string_literal(lit)); } From 326244615653bbae380275afc008b90592f51c80 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 03:29:05 +0330 Subject: [PATCH 04/16] fix: remove the code left behind by mistake. --- crates/oxc_codegen/src/gen.rs | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 49039a1d8e48..092d068c3025 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1167,11 +1167,7 @@ fn print_non_negative_float(value: f64, _p: &Codegen<{ MINIF impl<'a, const MINIFY: bool> Gen for BigIntLiteral<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, _ctx: Context) { p.add_source_mapping(self.span.start); - if self.raw.contains('_') { - p.print_str(self.raw.replace('_', "").as_bytes()); - } else { - p.print_str(self.raw.as_bytes()); - } + p.print_str(self.raw.as_bytes()); } } From 6c39138aa1026b41184f0f6f17c06cd205e700f3 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 04:35:46 +0330 Subject: [PATCH 05/16] refactor(codegen): change how numeric literals are generated. --- crates/oxc_codegen/src/gen.rs | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 092d068c3025..eb217729f93e 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1081,18 +1081,19 @@ impl<'a, const MINIFY: bool> Gen for NumericLiteral<'a> { let value = abs_value as u64; // If integers less than 1000, we know that exponential notation will always be longer than // the integer representation. This is not the case for 1000 which is "1e3". - if value < 1000 { - format!("{value}") - } else if (1_000_000_000_000..=0xFFFF_FFFF_FFFF_F800).contains(&value) { - let hex = format!("{value:#x}"); - let result = print_non_negative_float(abs_value, p); - if hex.len() < result.len() { - hex - } else { - result + match value { + _ if !MINIFY => print_non_negative_float(abs_value, p), + value @ ..=999 => format!("{value}"), + 1_000_000_000_000..=0xFFFF_FFFF_FFFF_F800 => { + let hex = format!("{value:#x}"); + let result = print_non_negative_float(abs_value, p); + if hex.len() < result.len() { + hex + } else { + result + } } - } else { - print_non_negative_float(abs_value, p) + _ => print_non_negative_float(abs_value, p), } }; let bytes = result.as_bytes(); From 9c04b72a882969c08597e8c34f88593799fd9ec1 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 05:16:07 +0330 Subject: [PATCH 06/16] refactor(trans): use ast allocator to modify numeric raw values. --- crates/oxc_codegen/src/gen.rs | 23 +++++++++---------- .../src/es2021/numeric_separator.rs | 15 ++++++------ 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index eb217729f93e..092d068c3025 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1081,19 +1081,18 @@ impl<'a, const MINIFY: bool> Gen for NumericLiteral<'a> { let value = abs_value as u64; // If integers less than 1000, we know that exponential notation will always be longer than // the integer representation. This is not the case for 1000 which is "1e3". - match value { - _ if !MINIFY => print_non_negative_float(abs_value, p), - value @ ..=999 => format!("{value}"), - 1_000_000_000_000..=0xFFFF_FFFF_FFFF_F800 => { - let hex = format!("{value:#x}"); - let result = print_non_negative_float(abs_value, p); - if hex.len() < result.len() { - hex - } else { - result - } + if value < 1000 { + format!("{value}") + } else if (1_000_000_000_000..=0xFFFF_FFFF_FFFF_F800).contains(&value) { + let hex = format!("{value:#x}"); + let result = print_non_negative_float(abs_value, p); + if hex.len() < result.len() { + hex + } else { + result } - _ => print_non_negative_float(abs_value, p), + } else { + print_non_negative_float(abs_value, p) } }; let bytes = result.as_bytes(); diff --git a/crates/oxc_transformer/src/es2021/numeric_separator.rs b/crates/oxc_transformer/src/es2021/numeric_separator.rs index aabacdb30946..cf55fc076bb9 100644 --- a/crates/oxc_transformer/src/es2021/numeric_separator.rs +++ b/crates/oxc_transformer/src/es2021/numeric_separator.rs @@ -1,5 +1,3 @@ -use std::marker::PhantomData; - use oxc_ast::ast::*; use crate::{ @@ -11,26 +9,27 @@ use crate::{ /// /// References: /// * -pub struct NumericSeparator<'a>(PhantomData<&'a ()>); +pub struct NumericSeparator<'a> { + ctx: TransformerCtx<'a>, +} impl<'a> NumericSeparator<'a> { #![allow(clippy::unused_self)] - pub fn new(_: TransformerCtx<'a>, options: &TransformOptions) -> Option { + pub fn new(ctx: TransformerCtx<'a>, options: &TransformOptions) -> Option { (options.target < TransformTarget::ES2021 || options.numeric_separator) - .then_some(Self(PhantomData {})) + .then_some(Self { ctx }) } pub fn transform_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { if !lit.raw.is_empty() { - // set literal raw string to empty so codegen have to use the value. - lit.raw = ""; + lit.raw = self.ctx.ast.new_str(lit.raw.replace('_', "").as_str()); } } pub fn transform_bigint_literal(&mut self, lit: &mut BigIntLiteral<'a>) { let raw = &lit.raw; - if !raw.is_empty() && raw.contains('_') { + if !raw.is_empty() { lit.raw = raw.replace('_', "").into(); } } From d3615a6f2bc0c91e9ed0717c030c44ce9626eada Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 19:08:10 +0330 Subject: [PATCH 07/16] feat(transformer): add transform literal for numeric literals. --- crates/oxc_transformer/src/es2015/literals.rs | 44 +++++++++++++++++++ crates/oxc_transformer/src/es2015/mod.rs | 2 + crates/oxc_transformer/src/lib.rs | 9 +++- crates/oxc_transformer/src/options.rs | 1 + tasks/transform_conformance/src/test_case.rs | 1 + 5 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 crates/oxc_transformer/src/es2015/literals.rs diff --git a/crates/oxc_transformer/src/es2015/literals.rs b/crates/oxc_transformer/src/es2015/literals.rs new file mode 100644 index 000000000000..9791418daee4 --- /dev/null +++ b/crates/oxc_transformer/src/es2015/literals.rs @@ -0,0 +1,44 @@ +use std::rc::Rc; + +use oxc_ast::{ast::*, AstBuilder}; + +use crate::{ + context::TransformerCtx, + options::{TransformOptions, TransformTarget}, +}; + +/// ES2015: Shorthand Properties +/// +/// References: +/// * +/// * +pub struct Literals<'a> { + _ast: Rc>, +} + +impl<'a> Literals<'a> { + pub fn new(ctx: TransformerCtx<'a>, options: &TransformOptions) -> Option { + (options.target < TransformTarget::ES2015 || options.literals) + .then_some(Self { _ast: ctx.ast }) + } + + pub fn transform_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { + // early return if number's raw value is empty or shorter than 2 characters, + // both `0bxxx` and `0oxxx` need at least 3 characters be defined. + if lit.raw.len() <= 2 { + return; + } + + match lit.raw[0..3].as_bytes() { + // Set binary and octal raw values to empty, It would force the codegen, + // go generate them from their value. + [b'0', b'b' | b'B' | b'o' | b'O'] => lit.raw = "", + _ => {} + } + } + + pub fn transform_string_literal(&mut self, _: &mut StringLiteral<'a>) { + // TODO: As of today oxc_lexer takes care of this, We have to rework it + // so it can be controlled via the transformer. + } +} diff --git a/crates/oxc_transformer/src/es2015/mod.rs b/crates/oxc_transformer/src/es2015/mod.rs index ce30b75b279b..94618af441cb 100644 --- a/crates/oxc_transformer/src/es2015/mod.rs +++ b/crates/oxc_transformer/src/es2015/mod.rs @@ -2,6 +2,7 @@ mod arrow_functions; mod duplicate_keys; mod function_name; mod instanceof; +mod literals; mod new_target; mod shorthand_properties; mod template_literals; @@ -10,6 +11,7 @@ pub use arrow_functions::{ArrowFunctions, ArrowFunctionsOptions}; pub use duplicate_keys::DuplicateKeys; pub use function_name::FunctionName; pub use instanceof::Instanceof; +pub use literals::Literals; pub use new_target::NewTarget; pub use shorthand_properties::ShorthandProperties; pub use template_literals::TemplateLiterals; diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 82e3d204fba4..f0574da2b020 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -83,6 +83,7 @@ pub struct Transformer<'a> { es2015_template_literals: Option>, es2015_duplicate_keys: Option>, es2015_instanceof: Option>, + es2015_literals: Option>, es2015_new_target: Option>, es3_property_literal: Option>, } @@ -125,6 +126,7 @@ impl<'a> Transformer<'a> { es2015_template_literals: TemplateLiterals::new(Rc::clone(&ast), &options), es2015_duplicate_keys: DuplicateKeys::new(Rc::clone(&ast), &options), es2015_instanceof: Instanceof::new(Rc::clone(&ast), ctx.clone(), &options), + es2015_literals: Literals::new(ctx.clone(), &options), es2015_new_target: NewTarget::new(Rc::clone(&ast),ctx.clone(), &options), // other es3_property_literal: PropertyLiteral::new(Rc::clone(&ast), &options), @@ -303,10 +305,15 @@ impl<'a> VisitMut<'a> for Transformer<'a> { .map(|t: &mut JsonStrings| t.transform_directive(directive)); } - fn visit_string_literal(&mut self, lit: &mut StringLiteral) { + fn visit_number_literal(&mut self, lit: &mut NumericLiteral<'a>) { + self.es2015_literals.as_mut().map(|t| t.transform_number_literal(lit)); + } + + fn visit_string_literal(&mut self, lit: &mut StringLiteral<'a>) { self.es2019_json_strings .as_mut() .map(|t: &mut JsonStrings| t.transform_string_literal(lit)); + self.es2015_literals.as_mut().map(|t| t.transform_string_literal(lit)); } fn visit_method_definition(&mut self, def: &mut MethodDefinition<'a>) { diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index e658cefe3a74..4127cb553bd3 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -30,6 +30,7 @@ pub struct TransformOptions { pub function_name: bool, pub arrow_functions: Option, pub shorthand_properties: bool, + pub literals: bool, pub sticky_regex: bool, pub template_literals: bool, pub property_literals: bool, diff --git a/tasks/transform_conformance/src/test_case.rs b/tasks/transform_conformance/src/test_case.rs index 081073e7d49e..129bf8e771a7 100644 --- a/tasks/transform_conformance/src/test_case.rs +++ b/tasks/transform_conformance/src/test_case.rs @@ -101,6 +101,7 @@ pub trait TestCase { assumptions: options.assumptions, class_static_block: options.get_plugin("transform-class-static-block").is_some(), instanceof: options.get_plugin("transform-instanceof").is_some(), + literals: options.get_plugin("transform-literals").is_some(), function_name: options.get_plugin("transform-function-name").is_some(), arrow_functions: options .get_plugin("transform-arrow-functions") From b59d0cfbc0249e310d1a13acb7cb53bbc3f18ff8 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 19:15:38 +0330 Subject: [PATCH 08/16] fix(trans): clippy issues. --- crates/oxc_transformer/src/es2015/literals.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/oxc_transformer/src/es2015/literals.rs b/crates/oxc_transformer/src/es2015/literals.rs index 9791418daee4..081553ac7b4f 100644 --- a/crates/oxc_transformer/src/es2015/literals.rs +++ b/crates/oxc_transformer/src/es2015/literals.rs @@ -17,6 +17,8 @@ pub struct Literals<'a> { } impl<'a> Literals<'a> { + #![allow(clippy::unused_self)] + pub fn new(ctx: TransformerCtx<'a>, options: &TransformOptions) -> Option { (options.target < TransformTarget::ES2015 || options.literals) .then_some(Self { _ast: ctx.ast }) @@ -29,11 +31,10 @@ impl<'a> Literals<'a> { return; } - match lit.raw[0..3].as_bytes() { + if [b'0', b'b' | b'B' | b'o' | b'O'] == lit.raw[0..3].as_bytes() { // Set binary and octal raw values to empty, It would force the codegen, // go generate them from their value. - [b'0', b'b' | b'B' | b'o' | b'O'] => lit.raw = "", - _ => {} + lit.raw = ""; } } From 08609eb44657abb338e53459ebfacfffd1eb4c37 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 19:35:36 +0330 Subject: [PATCH 09/16] fix(trans): issue in pattern matching octal and binary lits. --- crates/oxc_transformer/src/es2015/literals.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/oxc_transformer/src/es2015/literals.rs b/crates/oxc_transformer/src/es2015/literals.rs index 081553ac7b4f..75c439bdb829 100644 --- a/crates/oxc_transformer/src/es2015/literals.rs +++ b/crates/oxc_transformer/src/es2015/literals.rs @@ -31,7 +31,7 @@ impl<'a> Literals<'a> { return; } - if [b'0', b'b' | b'B' | b'o' | b'O'] == lit.raw[0..3].as_bytes() { + if let [b'0', b'b' | b'B' | b'o' | b'O'] = lit.raw[0..2].as_bytes() { // Set binary and octal raw values to empty, It would force the codegen, // go generate them from their value. lit.raw = ""; From 89dbb0b0c9b449f9a8ab140e78d80fd70c05432a Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 19:55:04 +0330 Subject: [PATCH 10/16] fix(codegen): lowering characters casing in numeric literals. --- crates/oxc_codegen/src/gen.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 49039a1d8e48..0bb0b387aeec 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1099,7 +1099,8 @@ impl<'a, const MINIFY: bool> Gen for NumericLiteral<'a> { p.print_str(bytes); need_space_before_dot(bytes, p); } else { - let bytes = self.raw.as_bytes(); + let lower = self.raw.to_lowercase(); + let bytes = lower.as_bytes(); p.print_str(bytes); need_space_before_dot(bytes, p); }; From ba7c4d05a9f308c4cc08db4d5ee98e8a20a7eb8d Mon Sep 17 00:00:00 2001 From: rzvxa Date: Sun, 24 Mar 2024 20:47:11 +0330 Subject: [PATCH 11/16] fix(codegen): revert lowering numberic literals in the generator. --- crates/oxc_codegen/src/gen.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 0bb0b387aeec..49039a1d8e48 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -1099,8 +1099,7 @@ impl<'a, const MINIFY: bool> Gen for NumericLiteral<'a> { p.print_str(bytes); need_space_before_dot(bytes, p); } else { - let lower = self.raw.to_lowercase(); - let bytes = lower.as_bytes(); + let bytes = self.raw.as_bytes(); p.print_str(bytes); need_space_before_dot(bytes, p); }; From d007d8efe877160dd3e48eee57832c7d0a1772a0 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Mon, 25 Mar 2024 18:15:30 +0330 Subject: [PATCH 12/16] refactor(ast): revert `BigIntLiteral` `raw` field. --- crates/oxc_ast/src/ast/js.rs | 2 +- crates/oxc_ast/src/ast/literal.rs | 6 ++---- crates/oxc_ast/src/ast_builder.rs | 8 ++++---- crates/oxc_parser/src/js/expression.rs | 2 +- crates/oxc_transformer/src/es2021/numeric_separator.rs | 6 +++--- 5 files changed, 11 insertions(+), 13 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index a336a7b0607b..679190744119 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -501,7 +501,7 @@ impl<'a> PropertyKey<'a> { Expression::StringLiteral(lit) => Some(lit.value.to_compact_str()), Expression::RegExpLiteral(lit) => Some(lit.regex.to_string().into()), Expression::NumericLiteral(lit) => Some(lit.value.to_string().into()), - Expression::BigintLiteral(lit) => Some(lit.raw.clone()), + Expression::BigintLiteral(lit) => Some(lit.raw.to_compact_str()), Expression::NullLiteral(_) => Some("null".into()), Expression::TemplateLiteral(lit) => lit .expressions diff --git a/crates/oxc_ast/src/ast/literal.rs b/crates/oxc_ast/src/ast/literal.rs index f2556d78a658..8edf72289421 100644 --- a/crates/oxc_ast/src/ast/literal.rs +++ b/crates/oxc_ast/src/ast/literal.rs @@ -6,11 +6,10 @@ use std::{ fmt, hash::{Hash, Hasher}, - marker::PhantomData, }; use bitflags::bitflags; -use oxc_span::{Atom, CompactStr, Span}; +use oxc_span::{Atom, Span}; use oxc_syntax::{BigintBase, NumberBase}; #[cfg(feature = "serialize")] use serde::Serialize; @@ -117,10 +116,9 @@ impl<'a> Hash for NumericLiteral<'a> { pub struct BigIntLiteral<'a> { #[cfg_attr(feature = "serialize", serde(flatten))] pub span: Span, - pub raw: CompactStr, + pub raw: Atom<'a>, #[cfg_attr(feature = "serialize", serde(skip))] pub base: BigintBase, - pub(crate) _marker: PhantomData<&'a ()>, } impl<'a> BigIntLiteral<'a> { diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index b2ae36d8507b..a0a4600bca19 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -5,10 +5,10 @@ clippy::unused_self, )] -use std::{marker::PhantomData, mem}; +use std::mem; use oxc_allocator::{Allocator, Box, String, Vec}; -use oxc_span::{Atom, CompactStr, GetSpan, SourceType, Span, SPAN}; +use oxc_span::{Atom, GetSpan, SourceType, Span, SPAN}; use oxc_syntax::{ operator::{ AssignmentOperator, BinaryOperator, LogicalOperator, UnaryOperator, UpdateOperator, @@ -140,8 +140,8 @@ impl<'a> AstBuilder<'a> { BooleanLiteral { span, value } } - pub fn bigint_literal(&self, span: Span, raw: &'a str, base: BigintBase) -> BigIntLiteral<'a> { - BigIntLiteral { span, raw: CompactStr::new(raw), base, _marker: PhantomData {} } + pub fn bigint_literal(&self, span: Span, raw: Atom<'a>, base: BigintBase) -> BigIntLiteral<'a> { + BigIntLiteral { span, raw, base } } pub fn template_literal( diff --git a/crates/oxc_parser/src/js/expression.rs b/crates/oxc_parser/src/js/expression.rs index 0a0a0186b6c9..bf8169244877 100644 --- a/crates/oxc_parser/src/js/expression.rs +++ b/crates/oxc_parser/src/js/expression.rs @@ -325,7 +325,7 @@ impl<'a> ParserImpl<'a> { 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), raw, base)) + Ok(self.ast.bigint_literal(self.end_span(span), Atom::from(raw), base)) } pub(crate) fn parse_literal_regexp(&mut self) -> RegExpLiteral<'a> { diff --git a/crates/oxc_transformer/src/es2021/numeric_separator.rs b/crates/oxc_transformer/src/es2021/numeric_separator.rs index cf55fc076bb9..3c31e2d7b5e9 100644 --- a/crates/oxc_transformer/src/es2021/numeric_separator.rs +++ b/crates/oxc_transformer/src/es2021/numeric_separator.rs @@ -1,4 +1,5 @@ use oxc_ast::ast::*; +use oxc_span::Atom; use crate::{ context::TransformerCtx, @@ -28,9 +29,8 @@ impl<'a> NumericSeparator<'a> { } pub fn transform_bigint_literal(&mut self, lit: &mut BigIntLiteral<'a>) { - let raw = &lit.raw; - if !raw.is_empty() { - lit.raw = raw.replace('_', "").into(); + if !lit.raw.is_empty() { + lit.raw = Atom::from(self.ctx.ast.new_str(lit.raw.replace('_', "").as_str())); } } } From 2eb7568d7af83f44c317bb44cfd1716de10124cd Mon Sep 17 00:00:00 2001 From: rzvxa Date: Mon, 25 Mar 2024 18:19:16 +0330 Subject: [PATCH 13/16] test(conformance/babel): update the snapshot to allow failing test. --- tasks/transform_conformance/babel.snap.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 0f2bd8237d76..da3e571b8671 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,7 +1,6 @@ Passed: 352/1369 # All Passed: -* babel-plugin-transform-numeric-separator * babel-plugin-transform-optional-catch-binding * babel-plugin-transform-json-strings * babel-plugin-transform-shorthand-properties @@ -10,6 +9,9 @@ Passed: 352/1369 * babel-plugin-transform-property-literals +# babel-plugin-transform-numeric-separator (1/2) +* used-with-transform-es2015-literals/input.js + # babel-plugin-transform-unicode-sets-regex (0/4) * basic/basic/input.js * basic/string-properties/input.js From 43502fa996bd434c4cb85549781f2437e8fed464 Mon Sep 17 00:00:00 2001 From: rzvxa Date: Mon, 25 Mar 2024 18:27:34 +0330 Subject: [PATCH 14/16] test(conformance/babel): update snapshot. --- tasks/transform_conformance/babel.snap.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index da3e571b8671..a31f169b507b 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -9,9 +9,6 @@ Passed: 352/1369 * babel-plugin-transform-property-literals -# babel-plugin-transform-numeric-separator (1/2) -* used-with-transform-es2015-literals/input.js - # babel-plugin-transform-unicode-sets-regex (0/4) * basic/basic/input.js * basic/string-properties/input.js @@ -517,6 +514,9 @@ Passed: 352/1369 # babel-plugin-transform-logical-assignment-operators (5/6) * logical-assignment/null-coalescing/input.js +# babel-plugin-transform-numeric-separator (1/2) +* used-with-transform-es2015-literals/input.js + # babel-plugin-transform-export-namespace-from (0/4) * export-namespace/namespace-default/input.mjs * export-namespace/namespace-es6/input.mjs From 682188601e4fa545c049ee10c9ee33f2c52025aa Mon Sep 17 00:00:00 2001 From: rzvxa Date: Mon, 25 Mar 2024 18:31:41 +0330 Subject: [PATCH 15/16] test(conformance/babel): update snapshot. --- tasks/transform_conformance/babel.snap.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index a31f169b507b..a4148c307b84 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 352/1369 +Passed: 351/1369 # All Passed: * babel-plugin-transform-optional-catch-binding From a6e459f3ebd0a4b36421c1adbd1bf88142c2ea37 Mon Sep 17 00:00:00 2001 From: Ali Rezvani <3788964+rzvxa@users.noreply.github.com> Date: Tue, 26 Mar 2024 01:54:52 +0000 Subject: [PATCH 16/16] fix(conformance): revert babel snapshot. --- tasks/transform_conformance/babel.snap.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index a4148c307b84..0f2bd8237d76 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,6 +1,7 @@ -Passed: 351/1369 +Passed: 352/1369 # All Passed: +* babel-plugin-transform-numeric-separator * babel-plugin-transform-optional-catch-binding * babel-plugin-transform-json-strings * babel-plugin-transform-shorthand-properties @@ -514,9 +515,6 @@ Passed: 351/1369 # babel-plugin-transform-logical-assignment-operators (5/6) * logical-assignment/null-coalescing/input.js -# babel-plugin-transform-numeric-separator (1/2) -* used-with-transform-es2015-literals/input.js - # babel-plugin-transform-export-namespace-from (0/4) * export-namespace/namespace-default/input.mjs * export-namespace/namespace-es6/input.mjs