Skip to content

Commit

Permalink
refactor(isolated-declarations): simplify handling VariableDeclaratio…
Browse files Browse the repository at this point in the history
…n transform (#5916)
  • Loading branch information
Dunqing committed Sep 20, 2024
1 parent 2fd5c2a commit 67b4220
Show file tree
Hide file tree
Showing 2 changed files with 82 additions and 108 deletions.
185 changes: 82 additions & 103 deletions crates/oxc_isolated_declarations/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ mod scope;
mod signatures;
mod types;

use std::{cell::RefCell, collections::VecDeque, mem};
use std::{cell::RefCell, mem};

use diagnostics::function_with_assigning_properties;
use oxc_allocator::{Allocator, CloneIn};
Expand Down Expand Up @@ -156,7 +156,6 @@ impl<'a> IsolatedDeclarations<'a> {
None
}
});

let filtered_stmts = Self::remove_function_overloads_implementation(filtered_stmts);

let mut stmts = self.ast.vec_from_iter(filtered_stmts);
Expand All @@ -183,10 +182,10 @@ impl<'a> IsolatedDeclarations<'a> {
// https://github.com/microsoft/TypeScript/pull/58912
let mut need_empty_export_marker = true;

let mut new_stmts = Vec::new();
let mut variables_declarations = VecDeque::new();
let mut variable_transformed_indexes = VecDeque::new();
let mut transformed_indexes = FxHashSet::default();
let mut new_stmts = self.ast.vec();

// Use span to identify transformed nodes
let mut transformed_spans: FxHashSet<Span> = FxHashSet::default();

let filtered_stmts = stmts.iter().filter_map(|stmt| {
if stmt.is_declaration() || stmt.is_module_declaration() {
Expand All @@ -205,32 +204,17 @@ impl<'a> IsolatedDeclarations<'a> {
for mut stmt in filtered_stmts {
match stmt {
match_declaration!(Statement) => {
match stmt.to_declaration() {
Declaration::VariableDeclaration(decl) => {
if self.has_internal_annotation(decl.span) {
continue;
}
variables_declarations.push_back(
// SAFETY: `ast.copy` is unsound! We need to fix.
unsafe { self.ast.copy(&decl.declarations) }
.into_iter()
.collect::<Vec<_>>(),
);
variable_transformed_indexes.push_back(FxHashSet::default());
if let Declaration::TSModuleDeclaration(decl) = stmt.to_declaration() {
if self.has_internal_annotation(decl.span) {
continue;
}
Declaration::TSModuleDeclaration(decl) => {
if self.has_internal_annotation(decl.span) {
continue;
}
// declare global { ... } or declare module "foo" { ... }
// We need to emit it anyway
if decl.kind.is_global() || decl.id.is_string_literal() {
// We need to visit the module declaration to collect all references
self.scope.visit_ts_module_declaration(decl);
transformed_indexes.insert(new_stmts.len());
}
// declare global { ... } or declare module "foo" { ... }
// We need to emit it anyway
if decl.kind.is_global() || decl.id.is_string_literal() {
// We need to visit the module declaration to collect all references
self.scope.visit_ts_module_declaration(decl);
transformed_spans.insert(decl.span);
}
_ => {}
}
if !self.has_internal_annotation(stmt.span()) {
new_stmts.push(stmt);
Expand All @@ -242,16 +226,16 @@ impl<'a> IsolatedDeclarations<'a> {
if self.has_internal_annotation(decl.span) {
continue;
}
transformed_indexes.insert(new_stmts.len());
transformed_spans.insert(decl.span);
if let Some((var_decl, new_decl)) =
self.transform_export_default_declaration(decl)
{
if let Some(var_decl) = var_decl {
transformed_spans.insert(var_decl.span);
self.scope.visit_variable_declaration(&var_decl);
new_stmts.push(Statement::VariableDeclaration(
self.ast.alloc(var_decl),
));
transformed_indexes.insert(new_stmts.len());
}

self.scope.visit_export_default_declaration(&new_decl);
Expand All @@ -266,7 +250,7 @@ impl<'a> IsolatedDeclarations<'a> {
if self.has_internal_annotation(decl.span) {
continue;
}
transformed_indexes.insert(new_stmts.len());
transformed_spans.insert(decl.span);
if let Some(new_decl) = self.transform_export_named_declaration(decl) {
*decl = self.ast.alloc(new_decl);
} else {
Expand All @@ -281,7 +265,7 @@ impl<'a> IsolatedDeclarations<'a> {
if self.has_internal_annotation(module_declaration.span()) {
continue;
}
transformed_indexes.insert(new_stmts.len());
transformed_spans.insert(module_declaration.span());
self.scope.visit_module_declaration(module_declaration);
}
}
Expand All @@ -292,105 +276,100 @@ impl<'a> IsolatedDeclarations<'a> {
}
}

let last_transformed_len = transformed_spans.len();
// 5. Transform statements until no more transformation can be done
let last_transformed_len = transformed_indexes.len();
let mut last_reference_len = 0;
while last_reference_len != self.scope.references_len() {
last_reference_len = self.scope.references_len();

let mut variables_declarations_iter = variables_declarations.iter_mut();
let mut variable_transformed_indexes_iter = variable_transformed_indexes.iter_mut();
loop {
let cur_transformed_len = transformed_spans.len();
for stmt in new_stmts.iter_mut() {
let Some(decl) = stmt.as_declaration_mut() else { continue };

for (i, stmt) in new_stmts.iter_mut().enumerate() {
if transformed_indexes.contains(&i) {
// Skip transformed declarations
if transformed_spans.contains(&decl.span()) {
continue;
}
let Some(decl) = stmt.as_declaration() else { continue };

if let Declaration::VariableDeclaration(_) = decl {
let Some(cur_variable_declarations) = variables_declarations_iter.next() else {
unreachable!()
};
let Some(cur_transformed_indexes) = variable_transformed_indexes_iter.next()
else {
unreachable!()
};

for (ii, declarator) in cur_variable_declarations.iter_mut().enumerate() {
if cur_transformed_indexes.contains(&ii) {

if let Declaration::VariableDeclaration(declaration) = decl {
let mut all_declarator_has_transformed = true;
for declarator in declaration.declarations.iter_mut() {
if transformed_spans.contains(&declarator.span) {
continue;
}

if let Some(decl) = self.transform_variable_declarator(declarator, true) {
self.scope.visit_variable_declarator(&decl);
cur_transformed_indexes.insert(ii);
*declarator = decl;
if let Some(new_declarator) =
self.transform_variable_declarator(declarator, true)
{
self.scope.visit_variable_declarator(&new_declarator);
transformed_spans.insert(new_declarator.span());
*declarator = new_declarator;
} else {
all_declarator_has_transformed = false;
}
}
} else if let Some(decl) = self.transform_declaration(decl, true) {
self.scope.visit_declaration(&decl);
transformed_indexes.insert(i);
*stmt = Statement::from(decl);
if all_declarator_has_transformed {
declaration.declare = self.is_declare();
transformed_spans.insert(declaration.span);
}
} else if let Some(new_decl) = self.transform_declaration(decl, true) {
self.scope.visit_declaration(&new_decl);
transformed_spans.insert(new_decl.span());
*decl = new_decl;
}
}

// Use transformed_spans to weather we need to continue
if cur_transformed_len == transformed_spans.len() {
break;
}
}

// If any declaration is transformed in previous step, we don't need to add empty export marker
if last_transformed_len != 0 && last_transformed_len == transformed_spans.len() {
need_empty_export_marker = false;
}

// 6. Transform variable/using declarations, import statements, remove unused imports
// 7. Return transformed statements
let mut new_ast_stmts = self.ast.vec_with_capacity(transformed_indexes.len());
for (index, stmt) in new_stmts.into_iter().enumerate() {
new_stmts.retain_mut(|stmt| {
if transformed_spans.contains(&stmt.span()) {
return true;
}
match stmt {
_ if transformed_indexes.contains(&index) => {
new_ast_stmts.push(stmt);
}
Statement::VariableDeclaration(decl) => {
let indexes =
variable_transformed_indexes.pop_front().unwrap_or_else(|| unreachable!());
let declarations =
variables_declarations.pop_front().unwrap_or_else(|| unreachable!());

if !indexes.is_empty() {
let variables_declaration = self
.transform_variable_declaration_with_new_declarations(
&decl,
self.ast.vec_from_iter(
declarations
.into_iter()
.enumerate()
.filter(|(i, _)| indexes.contains(i))
.map(|(_, decl)| decl),
),
);
new_ast_stmts.push(Statement::VariableDeclaration(variables_declaration));
transformed_indexes.insert(index);
}
}
Statement::ImportDeclaration(decl) => {
// We must transform this in the end, because we need to know all references
if decl.specifiers.is_none() {
new_ast_stmts.push(Statement::ImportDeclaration(decl));
} else if let Some(decl) = self.transform_import_declaration(&decl) {
new_ast_stmts.push(Statement::ImportDeclaration(decl));
true
} else if let Some(new_decl) = self.transform_import_declaration(decl) {
*decl = new_decl;
true
} else {
false
}
}
_ => {}
Statement::VariableDeclaration(ref mut decl) => {
// If here just one declaration, that means this variable declaration doesn't referenced by any other
if decl.declarations.len() == 1 {
false
} else {
// Remove unreferenced declarations
decl.declarations
.retain(|declarator| transformed_spans.contains(&declarator.span));
decl.declare = self.is_declare();
true
}
}
_ => false,
}
}

if !transformed_indexes.is_empty() && last_transformed_len == transformed_indexes.len() {
need_empty_export_marker = false;
}
});

if need_empty_export_marker {
let specifiers = self.ast.vec();
let kind = ImportOrExportKind::Value;
let empty_export =
self.ast.alloc_export_named_declaration(SPAN, None, specifiers, None, kind, NONE);
new_ast_stmts
new_stmts
.push(Statement::from(ModuleDeclaration::ExportNamedDeclaration(empty_export)));
}

new_ast_stmts
new_stmts
}

pub fn remove_function_overloads_implementation<T>(
Expand Down
5 changes: 0 additions & 5 deletions crates/oxc_isolated_declarations/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -55,11 +55,6 @@ impl<'a> ScopeTree<'a> {
scope.value_references.contains(name) || scope.type_references.contains(name)
}

pub fn references_len(&self) -> usize {
let scope = self.levels.last().unwrap();
scope.value_references.len() + scope.type_references.len()
}

fn add_value_binding(&mut self, ident: Atom<'a>) {
let scope = self.levels.last_mut().unwrap();
scope.value_bindings.insert(ident);
Expand Down

0 comments on commit 67b4220

Please sign in to comment.