mirror of
https://github.com/danbulant/oxc
synced 2026-05-25 04:42:10 +00:00
feat(transformer): finish 2016 exponentiation operator (#996)
This commit is contained in:
parent
6c18b3e8ec
commit
0f72066f2e
17 changed files with 378 additions and 84 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
|
@ -1807,6 +1807,7 @@ dependencies = [
|
||||||
"oxc_allocator",
|
"oxc_allocator",
|
||||||
"oxc_codegen",
|
"oxc_codegen",
|
||||||
"oxc_parser",
|
"oxc_parser",
|
||||||
|
"oxc_semantic",
|
||||||
"oxc_span",
|
"oxc_span",
|
||||||
"oxc_tasks_common",
|
"oxc_tasks_common",
|
||||||
"oxc_transformer",
|
"oxc_transformer",
|
||||||
|
|
|
||||||
|
|
@ -92,29 +92,18 @@ pub enum Expression<'a> {
|
||||||
impl<'a> Expression<'a> {
|
impl<'a> Expression<'a> {
|
||||||
/// `PrimaryExpression`
|
/// `PrimaryExpression`
|
||||||
/// [tc39/ecma262#prod-PrimaryExpression](https://tc39.es/ecma262/#prod-PrimaryExpression)
|
/// [tc39/ecma262#prod-PrimaryExpression](https://tc39.es/ecma262/#prod-PrimaryExpression)
|
||||||
|
#[rustfmt::skip]
|
||||||
pub fn is_primary_expression(&self) -> bool {
|
pub fn is_primary_expression(&self) -> bool {
|
||||||
self.is_literal_expression()
|
self.is_literal() || matches!(self, Self::Identifier(_) | Self::ThisExpression(_) | Self::FunctionExpression(_)
|
||||||
|| matches!(
|
| Self::ClassExpression(_) | Self::ParenthesizedExpression(_)
|
||||||
self,
|
| Self::ArrayExpression(_) | Self::ObjectExpression(_))
|
||||||
Self::Identifier(_)
|
|
||||||
| Self::ThisExpression(_)
|
|
||||||
| Self::FunctionExpression(_)
|
|
||||||
| Self::ClassExpression(_)
|
|
||||||
| Self::ParenthesizedExpression(_)
|
|
||||||
| Self::ArrayExpression(_)
|
|
||||||
| Self::ObjectExpression(_)
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_literal_expression(&self) -> bool {
|
#[rustfmt::skip]
|
||||||
matches!(
|
pub fn is_literal(&self) -> bool {
|
||||||
self,
|
// Note: TemplateLiteral is not `Literal`
|
||||||
Self::BooleanLiteral(_)
|
matches!(self, Self::BooleanLiteral(_) | Self::NullLiteral(_) | Self::NumberLiteral(_)
|
||||||
| Self::NullLiteral(_)
|
| Self::BigintLiteral(_) | Self::RegExpLiteral(_) | Self::StringLiteral(_)
|
||||||
| Self::NumberLiteral(_)
|
|
||||||
| Self::BigintLiteral(_)
|
|
||||||
| Self::RegExpLiteral(_)
|
|
||||||
| Self::StringLiteral(_) // TemplateLiteral is not `Literal` type per oxc_ast
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -540,6 +529,10 @@ pub enum MemberExpression<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> MemberExpression<'a> {
|
impl<'a> MemberExpression<'a> {
|
||||||
|
pub fn is_computed(&self) -> bool {
|
||||||
|
matches!(self, MemberExpression::ComputedMemberExpression(_))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn optional(&self) -> bool {
|
pub fn optional(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
MemberExpression::ComputedMemberExpression(expr) => expr.optional,
|
MemberExpression::ComputedMemberExpression(expr) => expr.optional,
|
||||||
|
|
@ -814,6 +807,13 @@ impl<'a> AssignmentTarget<'a> {
|
||||||
pub fn is_destructuring_pattern(&self) -> bool {
|
pub fn is_destructuring_pattern(&self) -> bool {
|
||||||
matches!(self, Self::AssignmentTargetPattern(_))
|
matches!(self, Self::AssignmentTargetPattern(_))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_identifier(&self) -> bool {
|
||||||
|
matches!(
|
||||||
|
self,
|
||||||
|
Self::SimpleAssignmentTarget(SimpleAssignmentTarget::AssignmentTargetIdentifier(_))
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Hash)]
|
#[derive(Debug, Hash)]
|
||||||
|
|
|
||||||
|
|
@ -211,6 +211,10 @@ pub struct StringLiteral {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StringLiteral {
|
impl StringLiteral {
|
||||||
|
pub fn new(span: Span, value: Atom) -> Self {
|
||||||
|
Self { span, value }
|
||||||
|
}
|
||||||
|
|
||||||
/// Static Semantics: `IsStringWellFormedUnicode`
|
/// Static Semantics: `IsStringWellFormedUnicode`
|
||||||
/// test for \uD800-\uDFFF
|
/// test for \uD800-\uDFFF
|
||||||
pub fn is_string_well_formed_unicode(&self) -> bool {
|
pub fn is_string_well_formed_unicode(&self) -> bool {
|
||||||
|
|
|
||||||
|
|
@ -77,6 +77,16 @@ impl<'a> AstBuilder<'a> {
|
||||||
mem::replace(stmts, self.new_vec())
|
mem::replace(stmts, self.new_vec())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn move_assignment_target(
|
||||||
|
&self,
|
||||||
|
target: &mut AssignmentTarget<'a>,
|
||||||
|
) -> AssignmentTarget<'a> {
|
||||||
|
let ident = IdentifierReference::new(Span::default(), "".into());
|
||||||
|
let dummy = self.simple_assignment_target_identifier(ident);
|
||||||
|
let dummy = AssignmentTarget::SimpleAssignmentTarget(dummy);
|
||||||
|
mem::replace(target, dummy)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn program(
|
pub fn program(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
@ -90,10 +100,6 @@ impl<'a> AstBuilder<'a> {
|
||||||
|
|
||||||
/* ---------- Literals ---------- */
|
/* ---------- Literals ---------- */
|
||||||
|
|
||||||
pub fn string_literal(&self, span: Span, value: Atom) -> StringLiteral {
|
|
||||||
StringLiteral { span, value }
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn number_literal(
|
pub fn number_literal(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
@ -415,6 +421,13 @@ impl<'a> AstBuilder<'a> {
|
||||||
SimpleAssignmentTarget::AssignmentTargetIdentifier(self.alloc(ident))
|
SimpleAssignmentTarget::AssignmentTargetIdentifier(self.alloc(ident))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn simple_assignment_target_member_expression(
|
||||||
|
&self,
|
||||||
|
expr: MemberExpression<'a>,
|
||||||
|
) -> SimpleAssignmentTarget<'a> {
|
||||||
|
SimpleAssignmentTarget::MemberAssignmentTarget(self.alloc(expr))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn await_expression(&self, span: Span, argument: Expression<'a>) -> Expression<'a> {
|
pub fn await_expression(&self, span: Span, argument: Expression<'a>) -> Expression<'a> {
|
||||||
Expression::AwaitExpression(self.alloc(AwaitExpression { span, argument }))
|
Expression::AwaitExpression(self.alloc(AwaitExpression { span, argument }))
|
||||||
}
|
}
|
||||||
|
|
@ -496,6 +509,21 @@ impl<'a> AstBuilder<'a> {
|
||||||
Expression::MemberExpression(self.alloc(expr))
|
Expression::MemberExpression(self.alloc(expr))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn computed_member(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
object: Expression<'a>,
|
||||||
|
expression: Expression<'a>,
|
||||||
|
optional: bool, // for optional chaining
|
||||||
|
) -> MemberExpression<'a> {
|
||||||
|
MemberExpression::ComputedMemberExpression(ComputedMemberExpression {
|
||||||
|
span,
|
||||||
|
object,
|
||||||
|
expression,
|
||||||
|
optional,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn computed_member_expression(
|
pub fn computed_member_expression(
|
||||||
&self,
|
&self,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
|
@ -503,9 +531,22 @@ impl<'a> AstBuilder<'a> {
|
||||||
expression: Expression<'a>,
|
expression: Expression<'a>,
|
||||||
optional: bool, // for optional chaining
|
optional: bool, // for optional chaining
|
||||||
) -> Expression<'a> {
|
) -> Expression<'a> {
|
||||||
self.member_expression(MemberExpression::ComputedMemberExpression(
|
self.member_expression(self.computed_member(span, object, expression, optional))
|
||||||
ComputedMemberExpression { span, object, expression, optional },
|
}
|
||||||
))
|
|
||||||
|
pub fn static_member(
|
||||||
|
&self,
|
||||||
|
span: Span,
|
||||||
|
object: Expression<'a>,
|
||||||
|
property: IdentifierName,
|
||||||
|
optional: bool, // for optional chaining
|
||||||
|
) -> MemberExpression<'a> {
|
||||||
|
MemberExpression::StaticMemberExpression(StaticMemberExpression {
|
||||||
|
span,
|
||||||
|
object,
|
||||||
|
property,
|
||||||
|
optional,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn static_member_expression(
|
pub fn static_member_expression(
|
||||||
|
|
@ -515,12 +556,7 @@ impl<'a> AstBuilder<'a> {
|
||||||
property: IdentifierName,
|
property: IdentifierName,
|
||||||
optional: bool, // for optional chaining
|
optional: bool, // for optional chaining
|
||||||
) -> Expression<'a> {
|
) -> Expression<'a> {
|
||||||
self.member_expression(MemberExpression::StaticMemberExpression(StaticMemberExpression {
|
self.member_expression(self.static_member(span, object, property, optional))
|
||||||
span,
|
|
||||||
object,
|
|
||||||
property,
|
|
||||||
optional,
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn private_field_expression(
|
pub fn private_field_expression(
|
||||||
|
|
|
||||||
|
|
@ -36,7 +36,7 @@ pub fn is_static_boolean<'a>(expr: &Expression<'a>, ctx: &LintContext<'a>) -> bo
|
||||||
/// Checks if a branch node of `LogicalExpression` short circuits the whole condition
|
/// Checks if a branch node of `LogicalExpression` short circuits the whole condition
|
||||||
fn is_logical_identity(op: LogicalOperator, expr: &Expression) -> bool {
|
fn is_logical_identity(op: LogicalOperator, expr: &Expression) -> bool {
|
||||||
match expr {
|
match expr {
|
||||||
expr if expr.is_literal_expression() => {
|
expr if expr.is_literal() => {
|
||||||
let boolean_value = expr.get_boolean_value();
|
let boolean_value = expr.get_boolean_value();
|
||||||
(op == LogicalOperator::Or && boolean_value == Some(true))
|
(op == LogicalOperator::Or && boolean_value == Some(true))
|
||||||
|| (op == LogicalOperator::And && boolean_value == Some(false))
|
|| (op == LogicalOperator::And && boolean_value == Some(false))
|
||||||
|
|
@ -142,7 +142,7 @@ impl<'a, 'b> IsConstant<'a, 'b> for Expression<'a> {
|
||||||
Self::Identifier(ident) => {
|
Self::Identifier(ident) => {
|
||||||
ident.name == "undefined" && ctx.semantic().is_reference_to_global_variable(ident)
|
ident.name == "undefined" && ctx.semantic().is_reference_to_global_variable(ident)
|
||||||
}
|
}
|
||||||
_ if self.is_literal_expression() => true,
|
_ if self.is_literal() => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -167,7 +167,7 @@ impl NoConstantBinaryExpression {
|
||||||
| Expression::UpdateExpression(_)
|
| Expression::UpdateExpression(_)
|
||||||
| Expression::BinaryExpression(_)
|
| Expression::BinaryExpression(_)
|
||||||
| Expression::UnaryExpression(_) => true,
|
| Expression::UnaryExpression(_) => true,
|
||||||
expr if expr.is_literal_expression() => true,
|
expr if expr.is_literal() => true,
|
||||||
Expression::CallExpression(call_expr) => {
|
Expression::CallExpression(call_expr) => {
|
||||||
if let Expression::Identifier(ident) = &call_expr.callee {
|
if let Expression::Identifier(ident) = &call_expr.callee {
|
||||||
return ["Boolean", "String", "Number"].contains(&ident.name.as_str())
|
return ["Boolean", "String", "Number"].contains(&ident.name.as_str())
|
||||||
|
|
@ -267,7 +267,7 @@ impl NoConstantBinaryExpression {
|
||||||
Expression::ParenthesizedExpression(paren_expr) => {
|
Expression::ParenthesizedExpression(paren_expr) => {
|
||||||
Self::has_constant_loose_boolean_comparison(&paren_expr.expression, ctx)
|
Self::has_constant_loose_boolean_comparison(&paren_expr.expression, ctx)
|
||||||
}
|
}
|
||||||
expr if expr.is_literal_expression() => true,
|
expr if expr.is_literal() => true,
|
||||||
expr if expr.evaluate_to_undefined() => true,
|
expr if expr.evaluate_to_undefined() => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
|
|
@ -288,7 +288,7 @@ impl NoConstantBinaryExpression {
|
||||||
| Expression::NewExpression(_)
|
| Expression::NewExpression(_)
|
||||||
| Expression::TemplateLiteral(_)
|
| Expression::TemplateLiteral(_)
|
||||||
| Expression::UpdateExpression(_) => true,
|
| Expression::UpdateExpression(_) => true,
|
||||||
expr if expr.is_literal_expression() => true,
|
expr if expr.is_literal() => true,
|
||||||
Expression::BinaryExpression(binary_expr) => {
|
Expression::BinaryExpression(binary_expr) => {
|
||||||
binary_expr.operator.is_numeric_or_string_binary_operator()
|
binary_expr.operator.is_numeric_or_string_binary_operator()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -264,7 +264,7 @@ impl<'a> Compressor<'a> {
|
||||||
let right_string = get_string_value(right)?;
|
let right_string = get_string_value(right)?;
|
||||||
// let value = left_string.to_owned().
|
// let value = left_string.to_owned().
|
||||||
let value = left_string + right_string;
|
let value = left_string + right_string;
|
||||||
let string_literal = self.ast.string_literal(span, Atom::from(value));
|
let string_literal = StringLiteral::new(span, Atom::from(value));
|
||||||
Some(self.ast.literal_string_expression(string_literal))
|
Some(self.ast.literal_string_expression(string_literal))
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -587,7 +587,7 @@ impl<'a> Compressor<'a> {
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(type_name) = type_name {
|
if let Some(type_name) = type_name {
|
||||||
let string_literal = self.ast.string_literal(span, Atom::from(type_name));
|
let string_literal = StringLiteral::new(span, Atom::from(type_name));
|
||||||
return Some(self.ast.literal_string_expression(string_literal));
|
return Some(self.ast.literal_string_expression(string_literal));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -92,6 +92,10 @@ impl SymbolTable {
|
||||||
&self.references[reference_id]
|
&self.references[reference_id]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_binding(&self, reference_id: ReferenceId) -> bool {
|
||||||
|
self.references[reference_id].symbol_id().is_some()
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_global_reference(&self, reference_id: ReferenceId) -> bool {
|
pub fn is_global_reference(&self, reference_id: ReferenceId) -> bool {
|
||||||
self.references[reference_id].symbol_id().is_none()
|
self.references[reference_id].symbol_id().is_none()
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{env, path::Path};
|
use std::{cell::RefCell, env, path::Path, rc::Rc};
|
||||||
|
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
use oxc_codegen::{Codegen, CodegenOptions};
|
use oxc_codegen::{Codegen, CodegenOptions};
|
||||||
|
|
@ -34,12 +34,16 @@ fn main() {
|
||||||
println!("{printed}");
|
println!("{printed}");
|
||||||
|
|
||||||
let program = allocator.alloc(ret.program);
|
let program = allocator.alloc(ret.program);
|
||||||
let _ = SemanticBuilder::new(&source_text, source_type).build(program);
|
|
||||||
|
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 transform_options = TransformOptions {
|
let transform_options = TransformOptions {
|
||||||
target: TransformTarget::ES2015,
|
target: TransformTarget::ES2015,
|
||||||
react: Some(TransformReactOptions::default()),
|
react: Some(TransformReactOptions::default()),
|
||||||
};
|
};
|
||||||
Transformer::new(&allocator, source_type, transform_options).build(program);
|
Transformer::new(&allocator, source_type, &symbols, transform_options).build(program);
|
||||||
let printed = Codegen::<false>::new(source_text.len(), codegen_options).build(program);
|
let printed = Codegen::<false>::new(source_text.len(), codegen_options).build(program);
|
||||||
println!("Transformed:\n");
|
println!("Transformed:\n");
|
||||||
println!("{printed}");
|
println!("{printed}");
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,257 @@
|
||||||
|
use oxc_allocator::Vec;
|
||||||
use oxc_ast::{ast::*, AstBuilder};
|
use oxc_ast::{ast::*, AstBuilder};
|
||||||
use oxc_span::Span;
|
use oxc_semantic::SymbolTable;
|
||||||
use oxc_syntax::operator::BinaryOperator;
|
use oxc_span::{Atom, Span};
|
||||||
|
use oxc_syntax::operator::{AssignmentOperator, BinaryOperator};
|
||||||
|
|
||||||
use std::rc::Rc;
|
use std::{cell::RefCell, mem, rc::Rc};
|
||||||
|
|
||||||
/// ES2016: Exponentiation Operator
|
/// ES2016: Exponentiation Operator
|
||||||
///
|
///
|
||||||
/// References:
|
/// References:
|
||||||
/// * <https://babel.dev/docs/babel-plugin-transform-exponentiation-operator>
|
/// * <https://babel.dev/docs/babel-plugin-transform-exponentiation-operator>
|
||||||
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-exponentiation-operator>
|
/// * <https://github.com/babel/babel/blob/main/packages/babel-plugin-transform-exponentiation-operator>
|
||||||
|
/// * <https://github.com/babel/babel/blob/main/packages/babel-helper-builder-binary-assignment-operator-visitor>
|
||||||
pub struct ExponentiationOperator<'a> {
|
pub struct ExponentiationOperator<'a> {
|
||||||
ast: Rc<AstBuilder<'a>>,
|
ast: Rc<AstBuilder<'a>>,
|
||||||
|
symbols: Rc<RefCell<SymbolTable>>,
|
||||||
|
vars: Vec<'a, VariableDeclarator<'a>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Exploded<'a> {
|
||||||
|
reference: AssignmentTarget<'a>,
|
||||||
|
uid: Expression<'a>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> ExponentiationOperator<'a> {
|
impl<'a> ExponentiationOperator<'a> {
|
||||||
pub fn new(ast: Rc<AstBuilder<'a>>) -> Self {
|
pub fn new(ast: Rc<AstBuilder<'a>>, symbols: Rc<RefCell<SymbolTable>>) -> Self {
|
||||||
Self { ast }
|
let vars = ast.new_vec();
|
||||||
|
Self { ast, symbols, vars }
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn transform_expression<'b>(&mut self, expr: &'b mut Expression<'a>) {
|
pub fn leave_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
|
||||||
let Expression::BinaryExpression(binary_expression) = expr else { return };
|
if self.vars.is_empty() {
|
||||||
if binary_expression.operator != BinaryOperator::Exponential {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// left ** right
|
let decls = mem::replace(&mut self.vars, self.ast.new_vec());
|
||||||
let left = self.ast.move_expression(&mut binary_expression.left);
|
let kind = VariableDeclarationKind::Var;
|
||||||
let right = self.ast.move_expression(&mut binary_expression.right);
|
let decl = self.ast.variable_declaration(Span::default(), kind, decls, Modifiers::empty());
|
||||||
// Math.pow
|
let stmt = Statement::Declaration(Declaration::VariableDeclaration(decl));
|
||||||
let ident_math = IdentifierReference::new(Span::default(), "Math".into());
|
stmts.insert(0, stmt);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn transform_expression(&mut self, expr: &mut Expression<'a>) {
|
||||||
|
// left ** right
|
||||||
|
if let Expression::BinaryExpression(binary_expr) = expr {
|
||||||
|
if binary_expr.operator == BinaryOperator::Exponential {
|
||||||
|
let left = self.ast.move_expression(&mut binary_expr.left);
|
||||||
|
let right = self.ast.move_expression(&mut binary_expr.right);
|
||||||
|
*expr = self.math_pow(left, right);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// left **= right
|
||||||
|
if let Expression::AssignmentExpression(assign_expr) = expr {
|
||||||
|
if assign_expr.operator == AssignmentOperator::Exponential {
|
||||||
|
let mut nodes = self.ast.new_vec();
|
||||||
|
let left = self.ast.move_assignment_target(&mut assign_expr.left);
|
||||||
|
let Some(Exploded { reference, uid }) = self.explode(left, &mut nodes) else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let right = self.ast.move_expression(&mut assign_expr.right);
|
||||||
|
let right = self.math_pow(uid, right);
|
||||||
|
let assign_expr = self.ast.assignment_expression(
|
||||||
|
Span::default(),
|
||||||
|
AssignmentOperator::Assign,
|
||||||
|
reference,
|
||||||
|
right,
|
||||||
|
);
|
||||||
|
nodes.push(assign_expr);
|
||||||
|
*expr = self.ast.sequence_expression(Span::default(), nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// `left ** right` -> `Math.pow(left, right)`
|
||||||
|
fn math_pow(&mut self, left: Expression<'a>, right: Expression<'a>) -> Expression<'a> {
|
||||||
|
let ident_math = IdentifierReference::new(Span::default(), Atom::from("Math"));
|
||||||
let object = self.ast.identifier_reference_expression(ident_math);
|
let object = self.ast.identifier_reference_expression(ident_math);
|
||||||
let property = IdentifierName::new(Span::default(), "pow".into());
|
let property = IdentifierName::new(Span::default(), Atom::from("pow"));
|
||||||
let callee = self.ast.static_member_expression(Span::default(), object, property, false);
|
let callee = self.ast.static_member_expression(Span::default(), object, property, false);
|
||||||
// Math.pow(left, right)
|
|
||||||
let mut arguments = self.ast.new_vec_with_capacity(2);
|
let mut arguments = self.ast.new_vec_with_capacity(2);
|
||||||
arguments.push(Argument::Expression(left));
|
arguments.push(Argument::Expression(left));
|
||||||
arguments.push(Argument::Expression(right));
|
arguments.push(Argument::Expression(right));
|
||||||
let call_expr = self.ast.call_expression(Span::default(), callee, arguments, false, None);
|
self.ast.call_expression(Span::default(), callee, arguments, false, None)
|
||||||
*expr = call_expr;
|
}
|
||||||
|
|
||||||
|
/// Change `lhs **= 2` to `var temp; temp = lhs, lhs = Math.pow(temp, 2);`.
|
||||||
|
/// If the lhs is a member expression `obj.ref` or `obj[ref]`, assign them to a temporary variable so side-effects are not computed twice.
|
||||||
|
/// For `obj.ref`, change it to `var _obj; _obj = obj, _obj["ref"] = Math.pow(_obj["ref"], 2)`.
|
||||||
|
/// For `obj[ref]`, change it to `var _obj, _ref; _obj = obj, _ref = ref, _obj[_ref] = Math.pow(_obj[_ref], 2);`.
|
||||||
|
fn explode(
|
||||||
|
&mut self,
|
||||||
|
node: AssignmentTarget<'a>,
|
||||||
|
nodes: &mut Vec<'a, Expression<'a>>,
|
||||||
|
) -> Option<Exploded<'a>> {
|
||||||
|
let node = match node {
|
||||||
|
AssignmentTarget::SimpleAssignmentTarget(target) => target,
|
||||||
|
AssignmentTarget::AssignmentTargetPattern(_) => {
|
||||||
|
// Invalid Syntax
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let obj = self.get_obj_ref(self.ast.copy(&node), nodes)?;
|
||||||
|
let (reference, uid) = match node {
|
||||||
|
SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||||
|
let reference = AssignmentTarget::SimpleAssignmentTarget(
|
||||||
|
SimpleAssignmentTarget::AssignmentTargetIdentifier(ident),
|
||||||
|
);
|
||||||
|
(reference, obj)
|
||||||
|
}
|
||||||
|
SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) => {
|
||||||
|
let computed = member_expr.is_computed();
|
||||||
|
let prop = self.get_prop_ref(member_expr.unbox(), nodes)?;
|
||||||
|
let optional = false;
|
||||||
|
let obj_clone = self.ast.copy(&obj);
|
||||||
|
let span = Span::default();
|
||||||
|
let (reference, uid) = match &prop {
|
||||||
|
Expression::Identifier(ident) if !computed => {
|
||||||
|
let ident = IdentifierName::new(span, ident.name.clone());
|
||||||
|
(
|
||||||
|
self.ast.static_member(span, obj_clone, ident.clone(), optional),
|
||||||
|
self.ast.static_member_expression(span, obj, ident, optional),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
let prop_clone = self.ast.copy(&prop);
|
||||||
|
(
|
||||||
|
self.ast.computed_member(span, obj_clone, prop_clone, optional),
|
||||||
|
self.ast.computed_member_expression(span, obj, prop, optional),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
(
|
||||||
|
AssignmentTarget::SimpleAssignmentTarget(
|
||||||
|
self.ast.simple_assignment_target_member_expression(reference),
|
||||||
|
),
|
||||||
|
uid,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(Exploded { reference, uid })
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make sure side-effects of evaluating `obj` of `obj.ref` and `obj[ref]` only happen once.
|
||||||
|
fn get_obj_ref(
|
||||||
|
&mut self,
|
||||||
|
node: SimpleAssignmentTarget<'a>,
|
||||||
|
nodes: &mut Vec<'a, Expression<'a>>,
|
||||||
|
) -> Option<Expression<'a>> {
|
||||||
|
let reference = match node {
|
||||||
|
SimpleAssignmentTarget::AssignmentTargetIdentifier(ident) => {
|
||||||
|
if ident
|
||||||
|
.reference_id
|
||||||
|
.get()
|
||||||
|
.is_some_and(|reference_id| self.symbols.borrow().has_binding(reference_id))
|
||||||
|
{
|
||||||
|
// this variable is declared in scope so we can be 100% sure
|
||||||
|
// that evaluating it multiple times won't trigger a getter
|
||||||
|
// or something else
|
||||||
|
return Some(self.ast.identifier_reference_expression(ident.unbox()));
|
||||||
|
}
|
||||||
|
// could possibly trigger a getter so we need to only evaluate it once
|
||||||
|
self.ast.identifier_reference_expression(ident.unbox())
|
||||||
|
}
|
||||||
|
SimpleAssignmentTarget::MemberAssignmentTarget(member_expr) => {
|
||||||
|
let expr = match member_expr.unbox() {
|
||||||
|
MemberExpression::ComputedMemberExpression(e) => e.object,
|
||||||
|
MemberExpression::StaticMemberExpression(e) => e.object,
|
||||||
|
MemberExpression::PrivateFieldExpression(e) => e.object,
|
||||||
|
};
|
||||||
|
// the object reference that we need to save is locally declared
|
||||||
|
// so as per the previous comment we can be 100% sure evaluating
|
||||||
|
// it multiple times will be safe
|
||||||
|
// Super cannot be directly assigned so lets return it also
|
||||||
|
if matches!(expr, Expression::Super(_))
|
||||||
|
|| matches!(&expr, Expression::Identifier(ident) if
|
||||||
|
ident.reference_id.get().is_some_and(|reference_id| self.symbols.borrow().has_binding(reference_id)))
|
||||||
|
{
|
||||||
|
return Some(expr);
|
||||||
|
}
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
_ => return None,
|
||||||
|
};
|
||||||
|
Some(self.add_new_reference(reference, nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Make sure side-effects of evaluating `ref` of `obj.ref` and `obj[ref]` only happen once.
|
||||||
|
fn get_prop_ref(
|
||||||
|
&mut self,
|
||||||
|
node: MemberExpression<'a>,
|
||||||
|
nodes: &mut Vec<'a, Expression<'a>>,
|
||||||
|
) -> Option<Expression<'a>> {
|
||||||
|
let prop = match node {
|
||||||
|
MemberExpression::ComputedMemberExpression(expr) => {
|
||||||
|
let expr = expr.expression;
|
||||||
|
if expr.is_literal() {
|
||||||
|
return Some(expr);
|
||||||
|
}
|
||||||
|
expr
|
||||||
|
}
|
||||||
|
MemberExpression::StaticMemberExpression(expr) => {
|
||||||
|
let ident = expr.property;
|
||||||
|
let string_literal = StringLiteral::new(Span::default(), ident.name);
|
||||||
|
return Some(self.ast.literal_string_expression(string_literal));
|
||||||
|
}
|
||||||
|
MemberExpression::PrivateFieldExpression(_) => {
|
||||||
|
// From babel: "We can't generate property ref for private name, please install `@babel/plugin-transform-class-properties`"
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
Some(self.add_new_reference(prop, nodes))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_new_reference(
|
||||||
|
&mut self,
|
||||||
|
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);
|
||||||
|
|
||||||
|
// Add new reference `_name = name` to nodes
|
||||||
|
let ident = IdentifierReference::new(Span::default(), name);
|
||||||
|
let target = self.ast.simple_assignment_target_identifier(ident.clone());
|
||||||
|
let target = AssignmentTarget::SimpleAssignmentTarget(target);
|
||||||
|
let op = AssignmentOperator::Assign;
|
||||||
|
nodes.push(self.ast.assignment_expression(Span::default(), op, target, expr));
|
||||||
|
self.ast.identifier_reference_expression(ident)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO:
|
||||||
|
// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L543>
|
||||||
|
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<Atom>) {
|
||||||
|
match expr {
|
||||||
|
Expression::Identifier(ident) => parts.push(ident.name.clone()),
|
||||||
|
_ => parts.push(Atom::from("ref")),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,18 +17,18 @@ mod react_jsx;
|
||||||
mod regexp;
|
mod regexp;
|
||||||
mod typescript;
|
mod typescript;
|
||||||
|
|
||||||
use oxc_allocator::Allocator;
|
use std::{cell::RefCell, rc::Rc};
|
||||||
use oxc_ast::{ast::*, AstBuilder, VisitMut};
|
|
||||||
use oxc_span::SourceType;
|
|
||||||
use regexp::RegexpFlags;
|
|
||||||
use std::rc::Rc;
|
|
||||||
|
|
||||||
use es2015::ShorthandProperties;
|
use oxc_allocator::{Allocator, Vec};
|
||||||
use es2016::ExponentiationOperator;
|
use oxc_ast::{ast::*, AstBuilder, VisitMut};
|
||||||
use es2019::OptionalCatchBinding;
|
use oxc_semantic::SymbolTable;
|
||||||
use es2021::LogicalAssignmentOperators;
|
use oxc_span::SourceType;
|
||||||
use react_jsx::ReactJsx;
|
|
||||||
use typescript::TypeScript;
|
use crate::{
|
||||||
|
es2015::ShorthandProperties, es2016::ExponentiationOperator, es2019::OptionalCatchBinding,
|
||||||
|
es2021::LogicalAssignmentOperators, react_jsx::ReactJsx, regexp::RegexpFlags,
|
||||||
|
typescript::TypeScript,
|
||||||
|
};
|
||||||
|
|
||||||
pub use crate::options::{
|
pub use crate::options::{
|
||||||
TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget,
|
TransformOptions, TransformReactOptions, TransformReactRuntime, TransformTarget,
|
||||||
|
|
@ -55,6 +55,7 @@ impl<'a> Transformer<'a> {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
allocator: &'a Allocator,
|
allocator: &'a Allocator,
|
||||||
source_type: SourceType,
|
source_type: SourceType,
|
||||||
|
symbols: &Rc<RefCell<SymbolTable>>,
|
||||||
options: TransformOptions,
|
options: TransformOptions,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let ast = Rc::new(AstBuilder::new(allocator));
|
let ast = Rc::new(AstBuilder::new(allocator));
|
||||||
|
|
@ -80,7 +81,8 @@ impl<'a> Transformer<'a> {
|
||||||
t.es2019_optional_catch_binding.replace(OptionalCatchBinding::new(Rc::clone(&ast)));
|
t.es2019_optional_catch_binding.replace(OptionalCatchBinding::new(Rc::clone(&ast)));
|
||||||
}
|
}
|
||||||
if options.target < TransformTarget::ES2016 {
|
if options.target < TransformTarget::ES2016 {
|
||||||
t.es2016_exponentiation_operator.replace(ExponentiationOperator::new(Rc::clone(&ast)));
|
t.es2016_exponentiation_operator
|
||||||
|
.replace(ExponentiationOperator::new(Rc::clone(&ast), Rc::clone(symbols)));
|
||||||
}
|
}
|
||||||
if options.target < TransformTarget::ES2015 {
|
if options.target < TransformTarget::ES2015 {
|
||||||
t.es2015_shorthand_properties.replace(ShorthandProperties::new(Rc::clone(&ast)));
|
t.es2015_shorthand_properties.replace(ShorthandProperties::new(Rc::clone(&ast)));
|
||||||
|
|
@ -94,6 +96,13 @@ impl<'a> Transformer<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> VisitMut<'a> for Transformer<'a> {
|
impl<'a> VisitMut<'a> for Transformer<'a> {
|
||||||
|
fn visit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>) {
|
||||||
|
for stmt in stmts.iter_mut() {
|
||||||
|
self.visit_statement(stmt);
|
||||||
|
}
|
||||||
|
self.es2016_exponentiation_operator.as_mut().map(|t| t.leave_statements(stmts));
|
||||||
|
}
|
||||||
|
|
||||||
fn visit_expression(&mut self, expr: &mut Expression<'a>) {
|
fn visit_expression(&mut self, expr: &mut Expression<'a>) {
|
||||||
// self.typescript.as_mut().map(|t| t.transform_expression(expr));
|
// self.typescript.as_mut().map(|t| t.transform_expression(expr));
|
||||||
// self.react_jsx.as_mut().map(|t| t.transform_expression(expr));
|
// self.react_jsx.as_mut().map(|t| t.transform_expression(expr));
|
||||||
|
|
|
||||||
|
|
@ -37,8 +37,8 @@ impl<'a> RegexpFlags<'a> {
|
||||||
}
|
}
|
||||||
let ident = IdentifierReference::new(Span::default(), Atom::from("RegExp"));
|
let ident = IdentifierReference::new(Span::default(), Atom::from("RegExp"));
|
||||||
let callee = self.ast.identifier_reference_expression(ident);
|
let callee = self.ast.identifier_reference_expression(ident);
|
||||||
let pattern = self.ast.string_literal(Span::default(), Atom::from(regex.pattern.as_str()));
|
let pattern = StringLiteral::new(Span::default(), Atom::from(regex.pattern.as_str()));
|
||||||
let flags = self.ast.string_literal(Span::default(), Atom::from(regex.flags.to_string()));
|
let flags = StringLiteral::new(Span::default(), Atom::from(regex.flags.to_string()));
|
||||||
let pattern_literal = self.ast.literal_string_expression(pattern);
|
let pattern_literal = self.ast.literal_string_expression(pattern);
|
||||||
let flags_literal = self.ast.literal_string_expression(flags);
|
let flags_literal = self.ast.literal_string_expression(flags);
|
||||||
let mut arguments = self.ast.new_vec_with_capacity(2);
|
let mut arguments = self.ast.new_vec_with_capacity(2);
|
||||||
|
|
|
||||||
|
|
@ -210,8 +210,13 @@ impl Oxc {
|
||||||
}
|
}
|
||||||
|
|
||||||
if run_options.transform() {
|
if run_options.transform() {
|
||||||
|
// FIXME: this should not be duplicated with the linter semantic,
|
||||||
|
// we need to fix the API so symbols and scopes can be shared.
|
||||||
|
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 };
|
||||||
Transformer::new(&allocator, source_type, options).build(program);
|
Transformer::new(&allocator, source_type, &symbols, options).build(program);
|
||||||
}
|
}
|
||||||
|
|
||||||
let program = allocator.alloc(program);
|
let program = allocator.alloc(program);
|
||||||
|
|
|
||||||
|
|
@ -6,11 +6,12 @@ static GLOBAL: jemallocator::Jemalloc = jemallocator::Jemalloc;
|
||||||
#[global_allocator]
|
#[global_allocator]
|
||||||
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
static GLOBAL: mimalloc::MiMalloc = mimalloc::MiMalloc;
|
||||||
|
|
||||||
use std::{fs, hint::black_box};
|
use std::{cell::RefCell, fs, hint::black_box, rc::Rc};
|
||||||
|
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
use oxc_benchmark::{criterion_group, criterion_main, BenchmarkId, Criterion};
|
||||||
use oxc_parser::Parser;
|
use oxc_parser::Parser;
|
||||||
|
use oxc_semantic::SemanticBuilder;
|
||||||
use oxc_span::SourceType;
|
use oxc_span::SourceType;
|
||||||
use oxc_tasks_common::project_root;
|
use oxc_tasks_common::project_root;
|
||||||
use oxc_transformer::{TransformOptions, Transformer};
|
use oxc_transformer::{TransformOptions, Transformer};
|
||||||
|
|
@ -28,11 +29,14 @@ fn bench_transformer(criterion: &mut Criterion) {
|
||||||
let id = BenchmarkId::from_parameter(file);
|
let id = BenchmarkId::from_parameter(file);
|
||||||
group.bench_with_input(id, &source_text, |b, source_text| {
|
group.bench_with_input(id, &source_text, |b, source_text| {
|
||||||
let allocator = Allocator::default();
|
let allocator = Allocator::default();
|
||||||
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
let program = Parser::new(&allocator, source_text, source_type).parse().program;
|
||||||
let program = allocator.alloc(ret.program);
|
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 program = allocator.alloc(program);
|
||||||
b.iter(|| {
|
b.iter(|| {
|
||||||
let transform_options = TransformOptions::default();
|
let transform_options = TransformOptions::default();
|
||||||
Transformer::new(&allocator, source_type, transform_options)
|
Transformer::new(&allocator, source_type, &symbols, transform_options)
|
||||||
.build(black_box(program));
|
.build(black_box(program));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,7 @@ doctest = false
|
||||||
oxc_span = { workspace = true }
|
oxc_span = { workspace = true }
|
||||||
oxc_allocator = { workspace = true }
|
oxc_allocator = { workspace = true }
|
||||||
oxc_parser = { workspace = true }
|
oxc_parser = { workspace = true }
|
||||||
|
oxc_semantic = { workspace = true }
|
||||||
oxc_codegen = { workspace = true }
|
oxc_codegen = { workspace = true }
|
||||||
oxc_transformer = { workspace = true }
|
oxc_transformer = { workspace = true }
|
||||||
oxc_tasks_common = { workspace = true }
|
oxc_tasks_common = { workspace = true }
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
Passed: 92/1091
|
Passed: 94/1091
|
||||||
|
|
||||||
# babel-plugin-transform-unicode-sets-regex (0/4)
|
# babel-plugin-transform-unicode-sets-regex (0/4)
|
||||||
* Failed: basic/basic/input.js
|
* Failed: basic/basic/input.js
|
||||||
|
|
@ -736,10 +736,8 @@ Passed: 92/1091
|
||||||
* Failed: regression/gh-6923/input.js
|
* Failed: regression/gh-6923/input.js
|
||||||
* Failed: regression/in-uncompiled-class-fields/input.js
|
* Failed: regression/in-uncompiled-class-fields/input.js
|
||||||
|
|
||||||
# babel-plugin-transform-exponentiation-operator (1/4)
|
# babel-plugin-transform-exponentiation-operator (3/4)
|
||||||
* Failed: exponentiation-operator/assignment/input.js
|
|
||||||
* Failed: regression/4349/input.js
|
* Failed: regression/4349/input.js
|
||||||
* Failed: regression/4403/input.js
|
|
||||||
|
|
||||||
# babel-plugin-transform-shorthand-properties (All passed)
|
# babel-plugin-transform-shorthand-properties (All passed)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
use std::{
|
use std::{
|
||||||
|
cell::RefCell,
|
||||||
fs::{self, File},
|
fs::{self, File},
|
||||||
io::Write,
|
io::Write,
|
||||||
path::{Path, PathBuf},
|
path::{Path, PathBuf},
|
||||||
|
rc::Rc,
|
||||||
};
|
};
|
||||||
use walkdir::WalkDir;
|
use walkdir::WalkDir;
|
||||||
|
|
||||||
use oxc_allocator::Allocator;
|
use oxc_allocator::Allocator;
|
||||||
use oxc_codegen::{Codegen, CodegenOptions};
|
use oxc_codegen::{Codegen, CodegenOptions};
|
||||||
use oxc_parser::Parser;
|
use oxc_parser::Parser;
|
||||||
|
use oxc_semantic::SemanticBuilder;
|
||||||
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
use oxc_span::{SourceType, VALID_EXTENSIONS};
|
||||||
use oxc_tasks_common::{normalize_path, project_root};
|
use oxc_tasks_common::{normalize_path, project_root};
|
||||||
use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer};
|
use oxc_transformer::{TransformOptions, TransformReactOptions, TransformTarget, Transformer};
|
||||||
|
|
@ -140,13 +143,22 @@ fn babel_test(input_path: &Path, options: &BabelOptions) -> bool {
|
||||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(&expected_program);
|
Codegen::<false>::new(source_text.len(), CodegenOptions).build(&expected_program);
|
||||||
|
|
||||||
// Get transformed text.
|
// Get transformed text.
|
||||||
|
|
||||||
let transformed_program = Parser::new(&allocator, &source_text, source_type).parse().program;
|
let transformed_program = Parser::new(&allocator, &source_text, source_type).parse().program;
|
||||||
let transform_options = TransformOptions {
|
let transform_options = TransformOptions {
|
||||||
target: TransformTarget::ES5,
|
target: TransformTarget::ES5,
|
||||||
react: Some(TransformReactOptions::default()),
|
react: Some(TransformReactOptions::default()),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
let semantic =
|
||||||
|
SemanticBuilder::new(&source_text, source_type).build(&transformed_program).semantic;
|
||||||
|
let (symbols, _scope_tree) = semantic.into_symbol_table_and_scope_tree();
|
||||||
|
let symbols = Rc::new(RefCell::new(symbols));
|
||||||
|
|
||||||
let transformed_program = allocator.alloc(transformed_program);
|
let transformed_program = allocator.alloc(transformed_program);
|
||||||
Transformer::new(&allocator, source_type, transform_options).build(transformed_program);
|
|
||||||
|
Transformer::new(&allocator, source_type, &symbols, transform_options)
|
||||||
|
.build(transformed_program);
|
||||||
let transformed_code =
|
let transformed_code =
|
||||||
Codegen::<false>::new(source_text.len(), CodegenOptions).build(transformed_program);
|
Codegen::<false>::new(source_text.len(), CodegenOptions).build(transformed_program);
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue