Skip to content

Commit

Permalink
feat(transformer/arrow-functions): the output that uses this inside…
Browse files Browse the repository at this point in the history
  • Loading branch information
Dunqing committed Aug 25, 2024
1 parent 115b6af commit a9cebcf
Show file tree
Hide file tree
Showing 7 changed files with 126 additions and 64 deletions.
116 changes: 61 additions & 55 deletions crates/oxc_transformer/src/es2015/arrow_functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -83,26 +83,33 @@ pub struct ArrowFunctionsOptions {
pub struct ArrowFunctions<'a> {
ctx: Ctx<'a>,
_options: ArrowFunctionsOptions,
this_var: Option<BoundIdentifier<'a>>,
this_vars: std::vec::Vec<Option<BoundIdentifier<'a>>>,
/// Stack to keep track of whether we are inside an arrow function or not.
stacks: std::vec::Vec<bool>,
// var _this = this;
this_statements: std::vec::Vec<Option<Statement<'a>>>,
}

impl<'a> ArrowFunctions<'a> {
pub fn new(options: ArrowFunctionsOptions, ctx: Ctx<'a>) -> Self {
Self { ctx, _options: options, this_var: None, stacks: vec![], this_statements: vec![] }
Self {
ctx,
_options: options,
// Reserve for the global scope
this_vars: vec![None],
stacks: vec![],
}
}
}

impl<'a> Traverse<'a> for ArrowFunctions<'a> {
fn enter_statements(
&mut self,
_stmts: &mut Vec<'a, Statement<'a>>,
_ctx: &mut TraverseCtx<'a>,
) {
self.this_statements.push(None);
/// Insert `var _this = this;` for the global scope.
fn exit_program(&mut self, program: &mut Program<'a>, _ctx: &mut TraverseCtx<'a>) {
self.insert_this_var_statement_at_the_top_of_statements(&mut program.body);
}

fn enter_function(&mut self, func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
if func.body.is_some() {
self.this_vars.push(None);
}
}

/// ```ts
Expand All @@ -116,49 +123,12 @@ impl<'a> Traverse<'a> for ArrowFunctions<'a> {
/// }
/// ```
/// Insert the var _this = this; statement outside the arrow function
fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, _ctx: &mut TraverseCtx<'a>) {
// Insert the var _this = this;
if let Some(Some(stmt)) = self.this_statements.pop() {
stmts.insert(0, stmt);
}

if let Some(id) = &self.this_var {
let binding_pattern = self.ctx.ast.binding_pattern(
self.ctx
.ast
.binding_pattern_kind_from_binding_identifier(id.create_binding_identifier()),
Option::<TSTypeAnnotation>::None,
false,
);

let variable_declarator = self.ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
binding_pattern,
Some(self.ctx.ast.expression_this(SPAN)),
false,
);

let stmt = self.ctx.ast.alloc_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
self.ctx.ast.vec1(variable_declarator),
false,
);

let stmt = Statement::VariableDeclaration(stmt);
// store it, insert it in last statements
self.this_statements.last_mut().unwrap().replace(stmt);
fn exit_function(&mut self, func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
let Some(body) = func.body.as_mut() else {
return;
};

// TODO: This isn't quite right. In this case, output is invalid:
// ```js
// function foo() {
// let f = () => this;
// let f2 = () => this;
// }
// ```
self.this_var = None;
}
self.insert_this_var_statement_at_the_top_of_statements(&mut body.statements);
}

/// Change <this></this> to <_this></_this>, and mark it as found
Expand Down Expand Up @@ -245,14 +215,15 @@ impl<'a> ArrowFunctions<'a> {
}

fn get_this_name(&mut self, ctx: &mut TraverseCtx<'a>) -> BoundIdentifier<'a> {
if self.this_var.is_none() {
self.this_var = Some(BoundIdentifier::new_uid_in_current_scope(
let this_var = self.this_vars.last_mut().unwrap();
if this_var.is_none() {
this_var.replace(BoundIdentifier::new_uid_in_current_scope(
"this",
SymbolFlags::FunctionScopedVariable,
ctx,
));
}
self.this_var.as_ref().unwrap().clone()
this_var.as_ref().unwrap().clone()
}

fn transform_arrow_function_expression(
Expand Down Expand Up @@ -315,4 +286,39 @@ impl<'a> ArrowFunctions<'a> {
// `() => {};` => `(function () {});`
self.ctx.ast.expression_parenthesized(SPAN, expr)
}

/// Insert `var _this = this;` at the top of the statements.
fn insert_this_var_statement_at_the_top_of_statements(
&mut self,
statements: &mut Vec<'a, Statement<'a>>,
) {
if let Some(id) = &self.this_vars.pop().unwrap() {
let binding_pattern = self.ctx.ast.binding_pattern(
self.ctx
.ast
.binding_pattern_kind_from_binding_identifier(id.create_binding_identifier()),
Option::<TSTypeAnnotation>::None,
false,
);

let variable_declarator = self.ctx.ast.variable_declarator(
SPAN,
VariableDeclarationKind::Var,
binding_pattern,
Some(self.ctx.ast.expression_this(SPAN)),
false,
);

let stmt = self.ctx.ast.alloc_variable_declaration(
SPAN,
VariableDeclarationKind::Var,
self.ctx.ast.vec1(variable_declarator),
false,
);

let stmt = Statement::VariableDeclaration(stmt);

statements.insert(0, stmt);
}
}
}
15 changes: 10 additions & 5 deletions crates/oxc_transformer/src/es2015/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::rc::Rc;

pub use arrow_functions::{ArrowFunctions, ArrowFunctionsOptions};
pub use options::ES2015Options;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};

Expand Down Expand Up @@ -34,15 +33,21 @@ impl<'a> ES2015<'a> {
}

impl<'a> Traverse<'a> for ES2015<'a> {
fn enter_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.arrow_function.is_some() {
self.arrow_functions.enter_statements(stmts, ctx);
self.arrow_functions.exit_program(program, ctx);
}
}

fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.arrow_function.is_some() {
self.arrow_functions.exit_statements(stmts, ctx);
self.arrow_functions.enter_function(func, ctx);
}
}

fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
if self.options.arrow_function.is_some() {
self.arrow_functions.exit_function(func, ctx);
}
}

Expand Down
11 changes: 8 additions & 3 deletions crates/oxc_transformer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ impl<'a> Traverse<'a> for Transformer<'a> {
fn exit_program(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
self.x1_react.transform_program_on_exit(program, ctx);
self.x0_typescript.transform_program_on_exit(program, ctx);
self.x3_es2015.exit_program(program, ctx);
}

// ALPHASORT
Expand Down Expand Up @@ -202,8 +203,14 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x0_typescript.transform_formal_parameter(param);
}

fn enter_function(&mut self, func: &mut Function<'a>, _ctx: &mut TraverseCtx<'a>) {
fn enter_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_function(func);
self.x3_es2015.enter_function(func, ctx);
}

fn exit_function(&mut self, func: &mut Function<'a>, ctx: &mut TraverseCtx<'a>) {
self.x0_typescript.transform_function(func);
self.x3_es2015.exit_function(func, ctx);
}

fn enter_jsx_element(&mut self, node: &mut JSXElement<'a>, _ctx: &mut TraverseCtx<'a>) {
Expand Down Expand Up @@ -261,7 +268,6 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x2_es2021.enter_statements(stmts, ctx);
self.x2_es2020.enter_statements(stmts, ctx);
self.x2_es2016.enter_statements(stmts, ctx);
self.x3_es2015.enter_statements(stmts, ctx);
}

fn exit_statements(&mut self, stmts: &mut Vec<'a, Statement<'a>>, ctx: &mut TraverseCtx<'a>) {
Expand All @@ -270,7 +276,6 @@ impl<'a> Traverse<'a> for Transformer<'a> {
self.x2_es2021.exit_statements(stmts, ctx);
self.x2_es2020.exit_statements(stmts, ctx);
self.x2_es2016.exit_statements(stmts, ctx);
self.x3_es2015.exit_statements(stmts, ctx);
}

fn enter_tagged_template_expression(
Expand Down
28 changes: 27 additions & 1 deletion tasks/transform_conformance/oxc.snap.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,37 @@
commit: 12619ffe

Passed: 9/35
Passed: 9/36

# All Passed:
* babel-plugin-transform-optional-catch-binding


# babel-plugin-transform-arrow-functions (0/1)
* use-this-inside-blocks/input.js
x Bindings mismatch:
| after transform: ScopeId(1): []
| rebuilt : ScopeId(1): ["_this"]

x Bindings mismatch:
| after transform: ScopeId(3): ["_this"]
| rebuilt : ScopeId(3): []

x Symbol scope ID mismatch:
| after transform: SymbolId(3): ScopeId(3)
| rebuilt : SymbolId(1): ScopeId(1)

x Symbol flags mismatch:
| after transform: SymbolId(1): SymbolFlags(BlockScopedVariable |
| ArrowFunction)
| rebuilt : SymbolId(2): SymbolFlags(BlockScopedVariable)

x Symbol flags mismatch:
| after transform: SymbolId(2): SymbolFlags(BlockScopedVariable |
| ArrowFunction)
| rebuilt : SymbolId(3): SymbolFlags(BlockScopedVariable)



# babel-plugin-transform-typescript (2/7)
* computed-constant-value/input.ts
x Semantic Collector failed after transform
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"plugins": [["transform-arrow-functions"]]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
function foo() {
{ let f = () => this; }
{ let f2 = () => this; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
function foo() {
var _this = this;
{
let f = function() {
return _this;
};
}
{
let f2 = function() {
return _this;
};
}
}

0 comments on commit a9cebcf

Please sign in to comment.