Skip to content

Commit

Permalink
feat(traverse): support automatically variable hoisting during genera…
Browse files Browse the repository at this point in the history
…ting bindings
  • Loading branch information
Dunqing committed Nov 22, 2024
1 parent 52784d2 commit d1dbaa1
Show file tree
Hide file tree
Showing 8 changed files with 364 additions and 1,228 deletions.
9 changes: 1 addition & 8 deletions crates/oxc_transformer/src/jsx/refresh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -576,14 +576,7 @@ impl<'a, 'ctx> ReactRefresh<'a, 'ctx> {
arguments.push(function);
}

// TODO: Handle var hoisted in ctx API
let target_scope_id = ctx
.scopes()
.ancestors(ctx.current_scope_id())
.find(|&scope_id| ctx.scopes().get_flags(scope_id).is_var())
.unwrap_or_else(|| ctx.current_scope_id());

let binding = ctx.generate_uid("s", target_scope_id, SymbolFlags::FunctionScopedVariable);
let binding = ctx.generate_uid_in_current_scope("s", SymbolFlags::FunctionScopedVariable);

// _s();
let call_expression = ctx.ast.statement_expression(
Expand Down
15 changes: 12 additions & 3 deletions crates/oxc_traverse/scripts/lib/walk.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,20 @@ function generateWalkForStruct(type, types) {
// but we don't take that into account.
// Visitor should not do that though, so maybe it's OK.
// In final version, we should not make `scope_id` fields `Cell`s to prevent this.

// const Var = Self::Top.bits() | Self::Function.bits() | Self::ClassStaticBlock.bits() | Self::TsModuleBlock.bits();
let isVarHoistingScope = type.name == "Function" || ["Top", "Function", "ClassStaticBlock", "TsModuleBlock"]
.some(flag => scopeArgs.flags.includes(flag));

console.log("isVarHoistingScope", type.name, isVarHoistingScope, scopeArgs);
enterScopeCode = `
let previous_scope_id = ctx.current_scope_id();
ctx.set_current_scope_id((*(${makeFieldCode(scopeIdField)})).get().unwrap());
let previous_scope_id = ctx.current_scope_id();
${isVarHoistingScope ? `let previous_var_hoisting_scope_id = ctx.scoping.current_var_hoisting_scope_id();` : ''}
let current_scope_id = (*(${makeFieldCode(scopeIdField)})).get().unwrap();
ctx.set_current_scope_id(current_scope_id, ${isVarHoistingScope ? "Some(current_scope_id)" : "None"});
`;
exitScopeCode = `ctx.set_current_scope_id(previous_scope_id);`;

exitScopeCode = `ctx.set_current_scope_id(previous_scope_id, ${isVarHoistingScope ? "Some(previous_var_hoisting_scope_id)" : "None"} );`;
}

const fieldsCodes = visitedFields.map((field, index) => {
Expand Down
19 changes: 9 additions & 10 deletions crates/oxc_traverse/src/context/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,8 @@ use oxc_ast::{
ast::{Expression, IdentifierReference, Statement},
AstBuilder,
};
use oxc_semantic::{NodeId, ScopeTree, SymbolTable};
use oxc_span::{Atom, CompactStr, Span, SPAN};
use oxc_semantic::{ScopeTree, SymbolTable};
use oxc_span::{Atom, CompactStr, Span};
use oxc_syntax::{
reference::{ReferenceFlags, ReferenceId},
scope::{ScopeFlags, ScopeId},
Expand Down Expand Up @@ -356,12 +356,7 @@ impl<'a> TraverseCtx<'a> {
// Get name for UID
let name = self.generate_uid_name(name);
let name_atom = self.ast.atom(&name);

// Add binding to scope
let symbol_id =
self.symbols_mut().create_symbol(SPAN, name.clone(), flags, scope_id, NodeId::DUMMY);
self.scopes_mut().add_binding(scope_id, name, symbol_id);

let symbol_id = self.scoping.add_binding(scope_id, name, flags);
BoundIdentifier::new(name_atom, symbol_id)
}

Expand Down Expand Up @@ -620,7 +615,11 @@ impl<'a> TraverseCtx<'a> {

/// Shortcut for `ctx.scoping.set_current_scope_id`, to make `walk_*` methods less verbose.
#[inline]
pub(crate) fn set_current_scope_id(&mut self, scope_id: ScopeId) {
self.scoping.set_current_scope_id(scope_id);
pub(crate) fn set_current_scope_id(
&mut self,
scope_id: ScopeId,
var_hoisting_scope_id: Option<ScopeId>,
) {
self.scoping.set_current_scope_id(scope_id, var_hoisting_scope_id);
}
}
48 changes: 45 additions & 3 deletions crates/oxc_traverse/src/context/scoping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ pub struct TraverseScoping {
symbols: SymbolTable,
uid_names: Option<FxHashSet<CompactStr>>,
current_scope_id: ScopeId,
current_var_hoisting_scope_id: ScopeId,
}

// Public methods
Expand All @@ -40,6 +41,12 @@ impl TraverseScoping {
self.current_scope_id
}

/// Get current scope ID
#[inline]
pub(crate) fn current_var_hoisting_scope_id(&self) -> ScopeId {
self.current_var_hoisting_scope_id
}

/// Get current scope flags
#[inline]
pub fn current_scope_flags(&self) -> ScopeFlags {
Expand Down Expand Up @@ -164,6 +171,33 @@ impl TraverseScoping {
self.scopes.delete_scope(scope_id);
}

pub fn add_binding(
&mut self,
scope_id: ScopeId,
name: CompactStr,
flags: SymbolFlags,
) -> SymbolId {
// println!(
// "{name:?} {scope_id:?} {:?} {:?}",
// self.scopes.get_flags(scope_id),
// self.current_var_hoisting_scope_id
// );
let scope_id = if scope_id == self.current_var_hoisting_scope_id
|| flags != SymbolFlags::FunctionScopedVariable
|| self.scopes.get_flags(scope_id).is_var()
{
scope_id
} else {
self.current_var_hoisting_scope_id
};

let symbol_id =
self.symbols.create_symbol(SPAN, name.clone(), flags, scope_id, NodeId::DUMMY);
self.scopes.add_binding(scope_id, name, symbol_id);

symbol_id
}

/// Generate binding.
///
/// Creates a symbol with the provided name and flags and adds it to the specified scope.
Expand All @@ -174,8 +208,6 @@ impl TraverseScoping {
flags: SymbolFlags,
) -> BoundIdentifier<'a> {
let owned_name = name.to_compact_str();

// Add binding to scope
let symbol_id =
self.symbols.create_symbol(SPAN, owned_name.clone(), flags, scope_id, NodeId::DUMMY);
self.scopes.add_binding(scope_id, owned_name, symbol_id);
Expand Down Expand Up @@ -388,13 +420,23 @@ impl TraverseScoping {
uid_names: None,
// Dummy value. Immediately overwritten in `walk_program`.
current_scope_id: ScopeId::new(0),
// Dummy value. Immediately overwritten in `walk_program`.
current_var_hoisting_scope_id: ScopeId::new(0),
}
}

/// Set current scope ID
#[inline]
pub(crate) fn set_current_scope_id(&mut self, scope_id: ScopeId) {
pub(crate) fn set_current_scope_id(
&mut self,
scope_id: ScopeId,
var_hoisting_scope_id: Option<ScopeId>,
) {
self.current_scope_id = scope_id;
if let Some(var_hoisting_scope_id) = var_hoisting_scope_id {
self.current_var_hoisting_scope_id = var_hoisting_scope_id;
}
// println!("{:?} {:?} {:?}", var_hoisting_scope_id, scope_id, self.current_scope_flags());
}

/// Get `uid_names`.
Expand Down
Loading

0 comments on commit d1dbaa1

Please sign in to comment.