feat(traverse): add is_static method to TraverseCtx, which is moves out from SymbolTable (#5067)

The [scope.isStatic](419644f27c/packages/babel-traverse/src/scope/index.ts (L557)) method we port it from Babel. This method is useful for `transformer`. So we should move it out from `SymbolTable`, and it would be better to put it to `TraverseCtx`.
This commit is contained in:
Dunqing 2024-08-23 07:14:08 +00:00
parent c7b81f5762
commit 8cf8f7a58a
5 changed files with 46 additions and 28 deletions

View file

@ -1,6 +1,5 @@
#![allow(non_snake_case)] // Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
use oxc_ast::ast::Expression;
use oxc_index::IndexVec;
use oxc_span::{CompactStr, Span};
pub use oxc_syntax::{
@ -204,31 +203,6 @@ impl SymbolTable {
.map(|reference_id| &self.references[*reference_id])
}
/// Determine whether evaluating the specific input `node` is a consequenceless reference. ie.
/// evaluating it won't result in potentially arbitrary code from being ran. The following are
/// allowed and determined not to cause side effects:
///
/// - `this` expressions
/// - `super` expressions
/// - Bound identifiers
///
/// Reference:
/// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L557>
pub fn is_static(&self, expr: &Expression) -> bool {
match expr {
Expression::ThisExpression(_) | Expression::Super(_) => true,
Expression::Identifier(ident) => {
ident.reference_id.get().map_or(false, |reference_id| {
self.get_reference(reference_id).symbol_id().map_or_else(
|| self.has_binding(reference_id),
|symbol_id| self.get_resolved_references(symbol_id).all(|r| !r.is_write()),
)
})
}
_ => false,
}
}
pub fn reserve(&mut self, additional_symbols: usize, additional_references: usize) {
self.spans.reserve(additional_symbols);
self.names.reserve(additional_symbols);

View file

@ -89,7 +89,7 @@ impl<'a> Traverse<'a> for NullishCoalescingOperator<'a> {
};
// skip creating extra reference when `left` is static
if ctx.symbols().is_static(&logical_expr.left) {
if ctx.is_static(&logical_expr.left) {
*expr = Self::create_conditional_expression(
Self::clone_expression(&logical_expr.left, ctx),
logical_expr.left,

View file

@ -351,7 +351,7 @@ impl<'a> LogicalAssignmentOperators<'a> {
expr: &Expression<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Option<IdentifierReference<'a>> {
if ctx.symbols().is_static(expr) {
if ctx.is_static(expr) {
return None;
}

View file

@ -417,6 +417,21 @@ impl<'a> TraverseCtx<'a> {
) -> IdentifierReference<'a> {
self.scoping.clone_identifier_reference(ident, flags)
}
/// Determine whether evaluating the specific input `node` is a consequenceless reference.
///
/// I.E evaluating it won't result in potentially arbitrary code from being ran. The following are
/// allowed and determined not to cause side effects:
///
/// - `this` expressions
/// - `super` expressions
/// - Bound identifiers
///
/// This is a shortcut for `ctx.scoping.is_static`.
#[inline]
pub fn is_static(&self, expr: &Expression) -> bool {
self.scoping.is_static(expr)
}
}
// Methods used internally within crate

View file

@ -372,6 +372,35 @@ impl TraverseScoping {
let symbol_id = reference.symbol_id();
self.create_reference_id(ident.span, ident.name.clone(), symbol_id, flags)
}
/// Determine whether evaluating the specific input `node` is a consequenceless reference.
///
/// I.E evaluating it won't result in potentially arbitrary code from being ran. The following are
/// allowed and determined not to cause side effects:
///
/// - `this` expressions
/// - `super` expressions
/// - Bound identifiers
///
/// Based on Babel's `scope.isStatic` logic.
/// <https://github.com/babel/babel/blob/419644f27c5c59deb19e71aaabd417a3bc5483ca/packages/babel-traverse/src/scope/index.ts#L557>
///
/// # Panics
/// Can only panic if [`IdentifierReference`] does not have a reference_id, which it always should.
#[inline]
pub fn is_static(&self, expr: &Expression) -> bool {
match expr {
Expression::ThisExpression(_) | Expression::Super(_) => true,
Expression::Identifier(ident) => self
.symbols
.get_reference(ident.reference_id.get().unwrap())
.symbol_id()
.is_some_and(|symbol_id| {
self.symbols.get_resolved_references(symbol_id).all(|r| !r.is_write())
}),
_ => false,
}
}
}
// Methods used internally within crate