From 678db1d955765ab057d02f183f048800df862e1a Mon Sep 17 00:00:00 2001 From: Boshen Date: Mon, 16 Oct 2023 21:45:58 +0800 Subject: [PATCH] feat(transformer): ES2020 Nullish Coalescing Operator (#1004) The test runner needs an update for reading options.jon, which I'll work on in the up coming PR. --- crates/oxc_ast/src/ast_builder.rs | 9 ++ crates/oxc_codegen/src/gen.rs | 13 +-- crates/oxc_minifier/src/compressor/mod.rs | 17 ++-- crates/oxc_semantic/src/symbol.rs | 23 +++++ .../oxc_transformer/examples/transformer.rs | 5 +- .../src/es2016/exponentiation_operator.rs | 54 +++--------- crates/oxc_transformer/src/es2020/mod.rs | 3 + .../src/es2020/nullish_coalescing_operator.rs | 88 +++++++++++++++++++ crates/oxc_transformer/src/lib.rs | 15 +++- crates/oxc_transformer/src/options.rs | 18 +++- crates/oxc_transformer/src/tester.rs | 3 +- crates/oxc_transformer/src/utils.rs | 85 ++++++++++++++++++ crates/oxc_wasm/src/lib.rs | 8 +- tasks/transform_conformance/babel.snap.md | 13 ++- tasks/transform_conformance/src/lib.rs | 5 +- 15 files changed, 282 insertions(+), 77 deletions(-) create mode 100644 crates/oxc_transformer/src/es2020/mod.rs create mode 100644 crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs create mode 100644 crates/oxc_transformer/src/utils.rs diff --git a/crates/oxc_ast/src/ast_builder.rs b/crates/oxc_ast/src/ast_builder.rs index d1b474056..23c126351 100644 --- a/crates/oxc_ast/src/ast_builder.rs +++ b/crates/oxc_ast/src/ast_builder.rs @@ -101,6 +101,15 @@ impl<'a> AstBuilder<'a> { Program { span, source_type, directives, hashbang, body } } + /* ---------- Constructors ---------- */ + + /// `void 0` + pub fn void_0(&self) -> Expression<'a> { + let left = self.number_literal(Span::default(), 0.0, "0", NumberBase::Decimal); + let num = self.literal_number_expression(left); + self.unary_expression(Span::default(), UnaryOperator::Void, num) + } + /* ---------- Literals ---------- */ pub fn number_literal( diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index ac3d826b3..b514604db 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -2,7 +2,7 @@ use oxc_allocator::{Box, Vec}; #[allow(clippy::wildcard_imports)] use oxc_ast::ast::*; use oxc_syntax::{ - operator::{BinaryOperator, LogicalOperator, UnaryOperator}, + operator::{BinaryOperator, UnaryOperator}, precedence::{GetPrecedence, Precedence}, NumberBase, }; @@ -1368,6 +1368,7 @@ impl<'a, const MINIFY: bool> GenExpr for BinaryExpression<'a> { p.print_space_before_identifier(); } self.operator.gen(p, ctx); + p.print_soft_space(); self.right.gen_expr(p, self.precedence(), ctx.union_in_if(wrap)); }); } @@ -1406,11 +1407,9 @@ impl<'a, const MINIFY: bool> GenExpr for LogicalExpression<'a> { ); p.wrap(mixed || (precedence > self.precedence()), |p| { self.left.gen_expr(p, self.precedence(), ctx); + p.print_soft_space(); p.print_str(self.operator.as_str().as_bytes()); - let _precedence = match self.operator { - LogicalOperator::And | LogicalOperator::Coalesce => Precedence::BitwiseOr, - LogicalOperator::Or => Precedence::LogicalAnd, - }; + p.print_soft_space(); self.right.gen_expr(p, self.precedence(), ctx); }); } @@ -1421,9 +1420,13 @@ impl<'a, const MINIFY: bool> GenExpr for ConditionalExpression<'a> { let wrap = precedence > self.precedence(); p.wrap(wrap, |p| { self.test.gen_expr(p, self.precedence(), ctx); + p.print_soft_space(); p.print(b'?'); + p.print_soft_space(); self.consequent.gen_expr(p, Precedence::Assign, ctx.and_in(true)); + p.print_soft_space(); p.print(b':'); + p.print_soft_space(); self.alternate.gen_expr(p, Precedence::Assign, ctx.union_in_if(wrap)); }); } diff --git a/crates/oxc_minifier/src/compressor/mod.rs b/crates/oxc_minifier/src/compressor/mod.rs index d5ec5e36a..d6fc4331b 100644 --- a/crates/oxc_minifier/src/compressor/mod.rs +++ b/crates/oxc_minifier/src/compressor/mod.rs @@ -40,13 +40,6 @@ impl<'a> Compressor<'a> { /* Utilities */ - /// `void 0` - fn create_void_0(&mut self) -> Expression<'a> { - let left = self.ast.number_literal(SPAN, 0.0, "0", NumberBase::Decimal); - let num = self.ast.literal_number_expression(left); - self.ast.unary_expression(SPAN, UnaryOperator::Void, num) - } - /// `1/0` #[allow(unused)] fn create_one_div_zero(&mut self) -> Expression<'a> { @@ -88,7 +81,7 @@ impl<'a> Compressor<'a> { fn compress_console(&mut self, expr: &mut Expression<'a>) -> bool { if self.options.drop_console && util::is_console(expr) { - *expr = self.create_void_0(); + *expr = self.ast.void_0(); true } else { false @@ -163,12 +156,12 @@ impl<'a> Compressor<'a> { /* Expressions */ /// Transforms `undefined` => `void 0` - fn compress_undefined(&mut self, expr: &mut Expression<'a>) -> bool { + fn compress_undefined(&self, expr: &mut Expression<'a>) -> bool { let Expression::Identifier(ident) = expr else { return false }; if ident.name == "undefined" { // if let Some(reference_id) = ident.reference_id.get() { // && self.semantic.symbols().is_global_reference(reference_id) - *expr = self.create_void_0(); + *expr = self.ast.void_0(); return true; // } } @@ -209,14 +202,14 @@ impl<'a> Compressor<'a> { /// Transforms `typeof foo == "undefined"` into `foo === void 0` /// Enabled by `compress.typeofs` - fn compress_typeof_undefined(&mut self, expr: &mut BinaryExpression<'a>) { + fn compress_typeof_undefined(&self, expr: &mut BinaryExpression<'a>) { if expr.operator.is_equality() && self.options.typeofs { if let Expression::UnaryExpression(unary_expr) = &expr.left { if unary_expr.operator == UnaryOperator::Typeof { if let Expression::Identifier(ident) = &unary_expr.argument { if expr.right.is_specific_string_literal("undefined") { let left = self.ast.identifier_reference_expression((*ident).clone()); - let right = self.create_void_0(); + let right = self.ast.void_0(); let operator = BinaryOperator::StrictEquality; *expr = BinaryExpression { span: SPAN, left, operator, right }; } diff --git a/crates/oxc_semantic/src/symbol.rs b/crates/oxc_semantic/src/symbol.rs index 8ae0c7040..41317096e 100644 --- a/crates/oxc_semantic/src/symbol.rs +++ b/crates/oxc_semantic/src/symbol.rs @@ -1,3 +1,4 @@ +use oxc_ast::ast::Expression; use oxc_index::IndexVec; use oxc_span::{Atom, Span}; pub use oxc_syntax::{ @@ -112,4 +113,26 @@ impl SymbolTable { .iter() .map(|reference_id| &self.references[*reference_id]) } + + /// Determine whether evaluating the specific input `node` is a consequenceless reference. ie. + /// evaluating it won't result in potentially arbitrary code from being ran. The following are + /// allowed and determined not to cause side effects: + /// + /// - `this` expressions + /// - `super` expressions + /// - Bound identifiers + /// + /// Reference: + /// + pub fn is_static(&self, expr: &Expression) -> bool { + match expr { + Expression::ThisExpression(_) | Expression::Super(_) => true, + Expression::Identifier(ident) + if ident.reference_id.get().is_some_and(|id| self.has_binding(id)) => + { + true + } + _ => false, + } + } } diff --git a/crates/oxc_transformer/examples/transformer.rs b/crates/oxc_transformer/examples/transformer.rs index ff6571d6e..0bccb340d 100644 --- a/crates/oxc_transformer/examples/transformer.rs +++ b/crates/oxc_transformer/examples/transformer.rs @@ -5,7 +5,9 @@ use oxc_codegen::{Codegen, CodegenOptions}; use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::SourceType; -use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer}; +use oxc_transformer::{ + Assumptions, TransformOptions, TransformReactOptions, TransformTarget, Transformer, +}; // Instruction: // create a `test.js`, @@ -41,6 +43,7 @@ fn main() { let transform_options = TransformOptions { target: TransformTarget::ES2015, react: Some(TransformReactOptions::default()), + assumptions: Assumptions::default(), }; Transformer::new(&allocator, source_type, &symbols, transform_options).build(program); let printed = Codegen::::new(source_text.len(), codegen_options).build(program); diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index 1272d01ce..70d610786 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -1,10 +1,12 @@ +use std::{cell::RefCell, rc::Rc}; + use oxc_allocator::Vec; use oxc_ast::{ast::*, AstBuilder}; use oxc_semantic::SymbolTable; use oxc_span::{Atom, Span}; use oxc_syntax::operator::{AssignmentOperator, BinaryOperator}; -use std::{cell::RefCell, mem, rc::Rc}; +use crate::utils::CreateVars; /// ES2016: Exponentiation Operator /// @@ -23,23 +25,22 @@ struct Exploded<'a> { uid: Expression<'a>, } +impl<'a> CreateVars<'a> for ExponentiationOperator<'a> { + fn ast(&self) -> &AstBuilder<'a> { + &self.ast + } + + fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { + &mut self.vars + } +} + impl<'a> ExponentiationOperator<'a> { pub fn new(ast: Rc>, symbols: Rc>) -> Self { let vars = ast.new_vec(); Self { ast, symbols, vars } } - pub fn leave_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) { - if self.vars.is_empty() { - return; - } - let decls = mem::replace(&mut self.vars, self.ast.new_vec()); - let kind = VariableDeclarationKind::Var; - let decl = self.ast.variable_declaration(Span::default(), kind, decls, Modifiers::empty()); - let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl)); - stmts.insert(0, stmt); - } - pub fn transform_expression(&mut self, expr: &mut Expression<'a>) { // left ** right if let Expression::BinaryExpression(binary_expr) = expr { @@ -218,17 +219,7 @@ impl<'a> ExponentiationOperator<'a> { expr: Expression<'a>, nodes: &mut Vec<'a, Expression<'a>>, ) -> Expression<'a> { - let name = generate_uid_identifier_based_on_node(&expr); - // TODO: scope.push({ id: temp }); - - // Add `var name` to scope - let binding_identifier = BindingIdentifier::new(Span::default(), name.clone()); - let binding_pattern_kind = self.ast.binding_pattern_identifier(binding_identifier); - let binding = self.ast.binding_pattern(binding_pattern_kind, None, false); - let kind = VariableDeclarationKind::Var; - let decl = self.ast.variable_declarator(Span::default(), kind, binding, None, false); - self.vars.push(decl); - + let name = self.create_new_var(&expr); // Add new reference `_name = name` to nodes let ident = IdentifierReference::new(Span::default(), name); let target = self.ast.simple_assignment_target_identifier(ident.clone()); @@ -239,23 +230,6 @@ impl<'a> ExponentiationOperator<'a> { } } -// TODO: -// -fn generate_uid_identifier_based_on_node(expr: &Expression) -> Atom { - let mut parts = std::vec::Vec::with_capacity(1); - gather_node_parts(expr, &mut parts); - let name = parts.join("$"); - Atom::from(format!("_{name}")) -} - -// TODO: use a trait and add this to oxc_ast (syntax directed operations) -fn gather_node_parts(expr: &Expression, parts: &mut std::vec::Vec) { - match expr { - Expression::Identifier(ident) => parts.push(ident.name.clone()), - _ => parts.push(Atom::from("ref")), - } -} - #[test] fn test() { use crate::{ diff --git a/crates/oxc_transformer/src/es2020/mod.rs b/crates/oxc_transformer/src/es2020/mod.rs new file mode 100644 index 000000000..d10ee4116 --- /dev/null +++ b/crates/oxc_transformer/src/es2020/mod.rs @@ -0,0 +1,3 @@ +mod nullish_coalescing_operator; + +pub use nullish_coalescing_operator::NullishCoalescingOperator; diff --git a/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs new file mode 100644 index 000000000..2bd6ed0fa --- /dev/null +++ b/crates/oxc_transformer/src/es2020/nullish_coalescing_operator.rs @@ -0,0 +1,88 @@ +use std::{cell::RefCell, rc::Rc}; + +use oxc_allocator::Vec; +use oxc_ast::{ast::*, AstBuilder}; +use oxc_semantic::SymbolTable; +use oxc_span::Span; +use oxc_syntax::operator::{AssignmentOperator, BinaryOperator, LogicalOperator}; + +use crate::{options::Assumptions, utils::CreateVars}; + +/// ES2020: Nullish Coalescing Operator +/// +/// References: +/// * +/// * +pub struct NullishCoalescingOperator<'a> { + ast: Rc>, + symbols: Rc>, + assumptions: Assumptions, + vars: Vec<'a, VariableDeclarator<'a>>, +} + +impl<'a> CreateVars<'a> for NullishCoalescingOperator<'a> { + fn ast(&self) -> &AstBuilder<'a> { + &self.ast + } + + fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>> { + &mut self.vars + } +} + +impl<'a> NullishCoalescingOperator<'a> { + pub fn new( + ast: Rc>, + symbols: Rc>, + assumptions: Assumptions, + ) -> Self { + let vars = ast.new_vec(); + Self { ast, symbols, assumptions, vars } + } + + pub fn transform_expression(&mut self, expr: &mut Expression<'a>) { + // left ?? right + let Expression::LogicalExpression(logical_expr) = expr else { return }; + if logical_expr.operator != LogicalOperator::Coalesce { + return; + } + + let span = Span::default(); + let reference; + let assignment; + + // skip creating extra reference when `left` is static + if self.symbols.borrow().is_static(&logical_expr.left) { + reference = self.ast.copy(&logical_expr.left); + assignment = self.ast.copy(&logical_expr.left); + } else { + let name = self.create_new_var(&logical_expr.left); + let ident = IdentifierReference::new(span, name); + reference = self.ast.identifier_reference_expression(ident.clone()); + let left = AssignmentTarget::SimpleAssignmentTarget( + self.ast.simple_assignment_target_identifier(ident), + ); + let right = self.ast.copy(&logical_expr.left); + assignment = + self.ast.assignment_expression(span, AssignmentOperator::Assign, left, right); + }; + + let test = if self.assumptions.no_document_all { + let null = self.ast.literal_null_expression(NullLiteral::new(span)); + self.ast.binary_expression(span, assignment, BinaryOperator::Inequality, null) + } else { + let op = BinaryOperator::StrictInequality; + let null = self.ast.literal_null_expression(NullLiteral::new(span)); + let left = self.ast.binary_expression(span, self.ast.copy(&assignment), op, null); + + let right = + self.ast.binary_expression(span, self.ast.copy(&reference), op, self.ast.void_0()); + + self.ast.logical_expression(span, left, LogicalOperator::And, right) + }; + + let right = self.ast.move_expression(&mut logical_expr.right); + + *expr = self.ast.conditional_expression(span, test, reference, right); + } +} diff --git a/crates/oxc_transformer/src/lib.rs b/crates/oxc_transformer/src/lib.rs index 38a20f2f0..050cd5783 100644 --- a/crates/oxc_transformer/src/lib.rs +++ b/crates/oxc_transformer/src/lib.rs @@ -10,6 +10,7 @@ mod es2015; mod es2016; mod es2019; +mod es2020; mod es2021; mod es2022; mod options; @@ -18,6 +19,7 @@ mod regexp; #[cfg(test)] mod tester; mod typescript; +mod utils; use std::{cell::RefCell, rc::Rc}; @@ -28,12 +30,12 @@ use oxc_span::SourceType; use crate::{ es2015::ShorthandProperties, es2016::ExponentiationOperator, es2019::OptionalCatchBinding, - es2021::LogicalAssignmentOperators, react_jsx::ReactJsx, regexp::RegexpFlags, - typescript::TypeScript, + es2020::NullishCoalescingOperator, es2021::LogicalAssignmentOperators, react_jsx::ReactJsx, + regexp::RegexpFlags, typescript::TypeScript, utils::CreateVars, }; pub use crate::options::{ - TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget, + Assumptions, TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget, }; #[derive(Default)] @@ -47,6 +49,8 @@ pub struct Transformer<'a> { es2022_class_static_block: Option>, // es2021 es2021_logical_assignment_operators: Option>, + // es2020 + es2020_nullish_coalescing_operators: Option>, // es2019 es2019_optional_catch_binding: Option>, // es2016 @@ -70,6 +74,7 @@ impl<'a> Transformer<'a> { regexp_flags: RegexpFlags::new(Rc::clone(&ast), options.target), es2022_class_static_block: (options.target < TransformTarget::ES2022).then(|| es2022::ClassStaticBlock::new(Rc::clone(&ast))), es2021_logical_assignment_operators: (options.target < TransformTarget::ES2021).then(|| LogicalAssignmentOperators::new(Rc::clone(&ast))), + es2020_nullish_coalescing_operators: (options.target < TransformTarget::ES2020).then(|| NullishCoalescingOperator::new(Rc::clone(&ast), Rc::clone(symbols), options.assumptions)), es2019_optional_catch_binding: (options.target < TransformTarget::ES2019).then(|| OptionalCatchBinding::new(Rc::clone(&ast))), es2016_exponentiation_operator: (options.target < TransformTarget::ES2016).then(|| ExponentiationOperator::new(Rc::clone(&ast), Rc::clone(symbols))), es2015_shorthand_properties: (options.target < TransformTarget::ES2015).then(|| ShorthandProperties::new(Rc::clone(&ast))), @@ -86,7 +91,8 @@ impl<'a> VisitMut<'a> for Transformer<'a> { for stmt in stmts.iter_mut() { self.visit_statement(stmt); } - self.es2016_exponentiation_operator.as_mut().map(|t| t.leave_statements(stmts)); + self.es2016_exponentiation_operator.as_mut().map(|t| t.add_vars_to_statements(stmts)); + self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.add_vars_to_statements(stmts)); } fn visit_expression(&mut self, expr: &mut Expression<'a>) { @@ -95,6 +101,7 @@ impl<'a> VisitMut<'a> for Transformer<'a> { self.regexp_flags.as_mut().map(|t| t.transform_expression(expr)); self.es2021_logical_assignment_operators.as_mut().map(|t| t.transform_expression(expr)); + self.es2020_nullish_coalescing_operators.as_mut().map(|t| t.transform_expression(expr)); self.es2016_exponentiation_operator.as_mut().map(|t| t.transform_expression(expr)); self.visit_expression_match(expr); diff --git a/crates/oxc_transformer/src/options.rs b/crates/oxc_transformer/src/options.rs index f9d7e8c9d..e79c749d9 100644 --- a/crates/oxc_transformer/src/options.rs +++ b/crates/oxc_transformer/src/options.rs @@ -1,7 +1,8 @@ -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub struct TransformOptions { pub target: TransformTarget, pub react: Option, + pub assumptions: Assumptions, } /// See @@ -12,6 +13,7 @@ pub enum TransformTarget { ES2016, ES2018, ES2019, + ES2020, ES2021, ES2022, ES2024, @@ -19,14 +21,24 @@ pub enum TransformTarget { ESNext, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub struct TransformReactOptions { _runtime: TransformReactRuntime, } -#[derive(Debug, Default, Clone)] +#[derive(Debug, Default, Clone, Copy)] pub enum TransformReactRuntime { #[default] Classic, Automatic, } + +/// Compiler assumptions +/// +/// See +#[derive(Debug, Default, Clone, Copy)] +pub struct Assumptions { + /// When using operators that check for null or undefined, assume that they are never used with the special value document.all. + /// See . + pub no_document_all: bool, +} diff --git a/crates/oxc_transformer/src/tester.rs b/crates/oxc_transformer/src/tester.rs index a5425666e..3c3d76653 100644 --- a/crates/oxc_transformer/src/tester.rs +++ b/crates/oxc_transformer/src/tester.rs @@ -38,8 +38,7 @@ impl Tester { let symbols = Rc::new(RefCell::new(symbols)); let program = self.allocator.alloc(program); - Transformer::new(&self.allocator, self.source_type, &symbols, self.options.clone()) - .build(program); + Transformer::new(&self.allocator, self.source_type, &symbols, self.options).build(program); Codegen::::new(source_text.len(), CodegenOptions).build(program) } diff --git a/crates/oxc_transformer/src/utils.rs b/crates/oxc_transformer/src/utils.rs new file mode 100644 index 000000000..d5f3befd8 --- /dev/null +++ b/crates/oxc_transformer/src/utils.rs @@ -0,0 +1,85 @@ +use std::mem; + +use oxc_allocator::Vec; +use oxc_ast::{ast::*, AstBuilder}; +use oxc_span::{Atom, Span}; + +// TODO: +// +pub fn generate_uid_based_on_node(expr: &Expression) -> Atom { + let mut parts = std::vec::Vec::with_capacity(1); + expr.gather(&mut |part| parts.push(part)); + let name = parts.join("$"); + Atom::from(format!("_{name}")) +} + +// TODO: +pub trait GatherNodeParts { + fn gather(&self, f: &mut F); +} + +impl<'a> GatherNodeParts for Expression<'a> { + fn gather(&self, f: &mut F) { + match self { + Self::Identifier(ident) => f(ident.name.clone()), + Self::MemberExpression(expr) => expr.gather(f), + _ => f(Atom::from("ref")), + } + } +} + +impl<'a> GatherNodeParts for MemberExpression<'a> { + fn gather(&self, f: &mut F) { + self.object().gather(f); + match self { + MemberExpression::ComputedMemberExpression(expr) => expr.expression.gather(f), + MemberExpression::StaticMemberExpression(expr) => expr.property.gather(f), + MemberExpression::PrivateFieldExpression(expr) => expr.field.gather(f), + } + } +} + +impl GatherNodeParts for IdentifierName { + fn gather(&self, f: &mut F) { + f(self.name.clone()); + } +} + +impl GatherNodeParts for PrivateIdentifier { + fn gather(&self, f: &mut F) { + f(self.name.clone()); + } +} + +pub trait CreateVars<'a> { + fn ast(&self) -> &AstBuilder<'a>; + + fn vars_mut(&mut self) -> &mut Vec<'a, VariableDeclarator<'a>>; + + fn add_vars_to_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) { + if self.vars_mut().is_empty() { + return; + } + let new_vec = self.ast().new_vec(); + let decls = mem::replace(self.vars_mut(), new_vec); + let kind = VariableDeclarationKind::Var; + let decl = + self.ast().variable_declaration(Span::default(), kind, decls, Modifiers::empty()); + let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl)); + stmts.insert(0, stmt); + } + + fn create_new_var(&mut self, expr: &Expression<'a>) -> Atom { + let name = generate_uid_based_on_node(expr); + // TODO: scope.push({ id: temp }); + + // Add `var name` to scope + let binding_identifier = BindingIdentifier::new(Span::default(), name.clone()); + let binding_pattern_kind = self.ast().binding_pattern_identifier(binding_identifier); + let binding = self.ast().binding_pattern(binding_pattern_kind, None, false); + let kind = VariableDeclarationKind::Var; + let decl = self.ast().variable_declarator(Span::default(), kind, binding, None, false); + self.vars_mut().push(decl); + name + } +} diff --git a/crates/oxc_wasm/src/lib.rs b/crates/oxc_wasm/src/lib.rs index 43555aa01..f59d226e5 100644 --- a/crates/oxc_wasm/src/lib.rs +++ b/crates/oxc_wasm/src/lib.rs @@ -11,7 +11,7 @@ use oxc::{ parser::{Parser, ParserReturn}, semantic::{SemanticBuilder, SemanticBuilderReturn}, span::SourceType, - transformer::{TransformOptions, TransformTarget, Transformer}, + transformer::{Assumptions, TransformOptions, TransformTarget, Transformer}, }; use oxc_linter::{LintContext, Linter}; use oxc_query::{schema, Adapter, SCHEMA_TEXT}; @@ -215,7 +215,11 @@ impl Oxc { let semantic = SemanticBuilder::new(source_text, source_type).build(program).semantic; let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree(); let symbols = Rc::new(RefCell::new(symbols)); - let options = TransformOptions { target: TransformTarget::ES2015, react: None }; + let options = TransformOptions { + target: TransformTarget::ES2015, + react: None, + assumptions: Assumptions::default(), + }; Transformer::new(&allocator, source_type, &symbols, options).build(program); } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 808a5081d..c8fb697db 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,4 +1,4 @@ -Passed: 94/1091 +Passed: 95/1091 # babel-plugin-transform-unicode-sets-regex (0/4) * Failed: basic/basic/input.js @@ -491,8 +491,11 @@ Passed: 94/1091 * Failed: to-native-fields/static-shadow/input.js * Failed: to-native-fields/static-shadowed-binding/input.js -# babel-plugin-transform-logical-assignment-operators (3/6) +# babel-plugin-transform-logical-assignment-operators (0/6) +* Failed: logical-assignment/anonymous-functions-transform/input.js +* Failed: logical-assignment/arrow-functions-transform/input.js * Failed: logical-assignment/general-semantics/input.js +* Failed: logical-assignment/named-functions-transform/input.js * Failed: logical-assignment/null-coalescing/input.js * Failed: logical-assignment/null-coalescing-without-other/input.js @@ -532,19 +535,15 @@ Passed: 94/1091 * Failed: export-namespace/namespace-string/input.mjs * Failed: export-namespace/namespace-typescript/input.mjs -# babel-plugin-transform-nullish-coalescing-operator (0/12) +# babel-plugin-transform-nullish-coalescing-operator (4/12) * Failed: assumption-noDocumentAll/transform/input.js * Failed: assumption-noDocumentAll/transform-in-default-destructuring/input.js * Failed: assumption-noDocumentAll/transform-in-default-param/input.js * Failed: assumption-noDocumentAll/transform-in-function/input.js * Failed: assumption-noDocumentAll/transform-static-refs-in-default/input.js * Failed: assumption-noDocumentAll/transform-static-refs-in-function/input.js -* Failed: nullish-coalescing/transform-in-default-destructuring/input.js * Failed: nullish-coalescing/transform-in-default-param/input.js -* Failed: nullish-coalescing/transform-in-function/input.js * Failed: nullish-coalescing/transform-loose/input.js -* Failed: nullish-coalescing/transform-static-refs-in-default/input.js -* Failed: nullish-coalescing/transform-static-refs-in-function/input.js # babel-plugin-transform-optional-chaining (1/46) * Failed: assumption-noDocumentAll/assignment/input.js diff --git a/tasks/transform_conformance/src/lib.rs b/tasks/transform_conformance/src/lib.rs index 451c7e8c9..28aee50e4 100644 --- a/tasks/transform_conformance/src/lib.rs +++ b/tasks/transform_conformance/src/lib.rs @@ -13,7 +13,9 @@ use oxc_parser::Parser; use oxc_semantic::SemanticBuilder; use oxc_span::{SourceType, VALID_EXTENSIONS}; use oxc_tasks_common::{normalize_path, project_root}; -use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer}; +use oxc_transformer::{ + Assumptions, TransformOptions, TransformReactOptions, TransformTarget, Transformer, +}; #[test] #[cfg(any(coverage, coverage_nightly))] @@ -155,6 +157,7 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool { let transform_options = TransformOptions { target: TransformTarget::ES5, react: Some(TransformReactOptions::default()), + assumptions: Assumptions::default(), }; let semantic =