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 26, 2024
1 parent 713e210 commit 2ee960a
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 120 deletions.
17 changes: 16 additions & 1 deletion crates/oxc_traverse/scripts/lib/walk.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -109,11 +109,26 @@ 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));

enterScopeCode = `
let previous_scope_id = ctx.current_scope_id();
ctx.set_current_scope_id((*(${makeFieldCode(scopeIdField)})).get().unwrap());
${isVarHoistingScope ? `let previous_hoist_scope_id = ctx.current_hoist_scope_id();` : ''}
let current_scope_id = (*(${makeFieldCode(scopeIdField)})).get().unwrap();
ctx.set_current_scope_id(current_scope_id);
`;

if (isVarHoistingScope) {
enterScopeCode += `ctx.set_current_hoist_scope_id(current_scope_id);`;
}

exitScopeCode = `ctx.set_current_scope_id(previous_scope_id);`;
if (isVarHoistingScope) {
exitScopeCode += `ctx.set_current_hoist_scope_id(previous_hoist_scope_id);`;
}
}

const fieldsCodes = visitedFields.map((field, index) => {
Expand Down
34 changes: 26 additions & 8 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 @@ -186,6 +186,14 @@ impl<'a> TraverseCtx<'a> {
self.scoping.current_scope_id()
}

/// Get current scope ID.
///
/// Shortcut for `ctx.scoping.current_hoist_scope_id`.
#[inline]
pub fn current_hoist_scope_id(&self) -> ScopeId {
self.scoping.current_hoist_scope_id()
}

/// Get current scope flags.
///
/// Shortcut for `ctx.scoping.current_scope_flags`.
Expand Down Expand Up @@ -356,12 +364,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 @@ -421,6 +424,15 @@ impl<'a> TraverseCtx<'a> {
self.generate_uid_based_on_node(node, self.current_scope_id(), flags)
}

/// Generate UID in current hoist scope.
///
/// See also comments on [`TraverseScoping::generate_uid_name`] for important information
/// on how UIDs are generated. There are some potential "gotchas".
#[inline]
pub fn generate_uid_in_current_hoist_scope(&mut self, name: &str) -> BoundIdentifier<'a> {
self.generate_uid(name, self.current_hoist_scope_id(), SymbolFlags::FunctionScopedVariable)
}

/// Create a reference bound to a `SymbolId`.
///
/// This is a shortcut for `ctx.scoping.create_bound_reference`.
Expand Down Expand Up @@ -623,4 +635,10 @@ impl<'a> TraverseCtx<'a> {
pub(crate) fn set_current_scope_id(&mut self, scope_id: ScopeId) {
self.scoping.set_current_scope_id(scope_id);
}

/// Shortcut for `ctx.scoping.set_current_hoist_scope_id`, to make `walk_*` methods less verbose.
#[inline]
pub(crate) fn set_current_hoist_scope_id(&mut self, scope_id: ScopeId) {
self.scoping.set_current_hoist_scope_id(scope_id);
}
}
32 changes: 30 additions & 2 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_hoist_scope_id: ScopeId,
}

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

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

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

/// Add binding to [`ScopeTree`] and [`SymbolTable`].
#[inline]
pub(crate) fn add_binding(
&mut self,
scope_id: ScopeId,
name: CompactStr,
flags: SymbolFlags,
) -> SymbolId {
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 +196,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,6 +408,8 @@ 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_hoist_scope_id: ScopeId::new(0),
}
}

Expand All @@ -397,6 +419,12 @@ impl TraverseScoping {
self.current_scope_id = scope_id;
}

/// Set current hoist scope ID
#[inline]
pub(crate) fn set_current_hoist_scope_id(&mut self, scope_id: ScopeId) {
self.current_hoist_scope_id = scope_id;
}

/// Get `uid_names`.
///
/// Iterate through all symbols and unresolved references in AST and identify any var names
Expand Down
Loading

0 comments on commit 2ee960a

Please sign in to comment.