refactor(transformer): HelperLoader common transform: Helper enum (#6595)

`HelperLoaderStore::load` take a `Helper` instead of a string.

`Helper` has 2 purposes:

1. Constrain what helpers you can add to a fixed list (prevent typos).
2. Makes the hashmap lookup cheaper - hashing a single-byte `enum` (`u8`) is cheaper than hashing a string.
This commit is contained in:
overlookmotel 2024-10-16 06:48:01 +00:00
parent 0cbd4d0270
commit c346ebb0ca
3 changed files with 34 additions and 21 deletions

View file

@ -128,6 +128,21 @@ fn default_as_module_name() -> Cow<'static, str> {
Cow::Borrowed("@babel/runtime")
}
/// Available helpers.
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
pub enum Helper {
ObjectSpread2,
}
impl Helper {
const fn name(self) -> &'static str {
match self {
Self::ObjectSpread2 => "objectSpread2",
}
}
}
/// Helper loader transform.
pub struct HelperLoader<'a, 'ctx> {
ctx: &'ctx TransformCtx<'a>,
}
@ -144,7 +159,7 @@ impl<'a, 'ctx> Traverse<'a> for HelperLoader<'a, 'ctx> {
}
}
struct Helper<'a> {
struct LoadedHelper<'a> {
source: Atom<'a>,
local: BoundIdentifier<'a>,
}
@ -154,7 +169,7 @@ pub struct HelperLoaderStore<'a> {
module_name: Cow<'static, str>,
mode: HelperLoaderMode,
/// Loaded helpers, determined what helpers are loaded and what imports should be added.
loaded_helpers: RefCell<FxHashMap<Atom<'a>, Helper<'a>>>,
loaded_helpers: RefCell<FxHashMap<Helper, LoadedHelper<'a>>>,
}
// Public methods
@ -171,11 +186,11 @@ impl<'a> HelperLoaderStore<'a> {
#[expect(dead_code)]
pub fn call(
&mut self,
helper_name: Atom<'a>,
helper: Helper,
arguments: Vec<'a, Argument<'a>>,
ctx: &mut TraverseCtx<'a>,
) -> CallExpression<'a> {
let callee = self.load(helper_name, ctx);
let callee = self.load(helper, ctx);
ctx.ast.call_expression(
SPAN,
callee,
@ -189,11 +204,11 @@ impl<'a> HelperLoaderStore<'a> {
#[expect(dead_code)]
pub fn call_expr(
&mut self,
helper_name: Atom<'a>,
helper: Helper,
arguments: Vec<'a, Argument<'a>>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let callee = self.load(helper_name, ctx);
let callee = self.load(helper, ctx);
ctx.ast.expression_call(
SPAN,
callee,
@ -204,10 +219,10 @@ impl<'a> HelperLoaderStore<'a> {
}
/// Load a helper function and return the callee expression.
pub fn load(&self, helper_name: Atom<'a>, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
pub fn load(&self, helper: Helper, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
match self.mode {
HelperLoaderMode::Runtime => self.transform_for_runtime_helper(helper_name, ctx),
HelperLoaderMode::External => Self::transform_for_external_helper(helper_name, ctx),
HelperLoaderMode::Runtime => self.transform_for_runtime_helper(helper, ctx),
HelperLoaderMode::External => Self::transform_for_external_helper(helper, ctx),
HelperLoaderMode::Inline => {
unreachable!("Inline helpers are not supported yet");
}
@ -219,29 +234,27 @@ impl<'a> HelperLoaderStore<'a> {
impl<'a> HelperLoaderStore<'a> {
fn transform_for_runtime_helper(
&self,
helper_name: Atom<'a>,
helper: Helper,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
let mut loaded_helpers = self.loaded_helpers.borrow_mut();
let helper = loaded_helpers.entry(helper_name).or_insert_with_key(|helper_name| {
let loaded_helper = loaded_helpers.entry(helper).or_insert_with(|| {
let helper_name = helper.name();
let source = ctx.ast.atom(&format!("{}/helpers/{helper_name}", self.module_name));
let local = ctx.generate_uid_in_root_scope(helper_name, SymbolFlags::Import);
Helper { source, local }
LoadedHelper { source, local }
});
helper.local.create_read_expression(ctx)
loaded_helper.local.create_read_expression(ctx)
}
fn transform_for_external_helper(
helper_name: Atom<'a>,
ctx: &mut TraverseCtx<'a>,
) -> Expression<'a> {
fn transform_for_external_helper(helper: Helper, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
static HELPER_VAR: &str = "babelHelpers";
let symbol_id = ctx.scopes().find_binding(ctx.current_scope_id(), HELPER_VAR);
let ident =
ctx.create_reference_id(SPAN, Atom::from(HELPER_VAR), symbol_id, ReferenceFlags::Read);
let object = ctx.ast.expression_from_identifier_reference(ident);
let property = ctx.ast.identifier_name(SPAN, helper_name);
let property = ctx.ast.identifier_name(SPAN, Atom::from(helper.name()));
Expression::from(ctx.ast.member_expression_static(SPAN, object, property, false))
}

View file

@ -1,6 +1,5 @@
//! Utility transforms which are in common between other transforms.
use helper_loader::HelperLoader;
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_traverse::{Traverse, TraverseCtx};
@ -12,6 +11,7 @@ pub mod module_imports;
pub mod top_level_statements;
pub mod var_declarations;
use helper_loader::HelperLoader;
use module_imports::ModuleImports;
use top_level_statements::TopLevelStatements;
use var_declarations::VarDeclarations;

View file

@ -31,7 +31,7 @@ use oxc_semantic::{ReferenceFlags, SymbolId};
use oxc_span::SPAN;
use oxc_traverse::{Traverse, TraverseCtx};
use crate::context::TransformCtx;
use crate::{common::helper_loader::Helper, TransformCtx};
use super::ObjectRestSpreadOptions;
@ -132,6 +132,6 @@ impl<'a, 'ctx> ObjectSpread<'a, 'ctx> {
}
fn babel_external_helper(&self, ctx: &mut TraverseCtx<'a>) -> Expression<'a> {
self.ctx.helper_loader.load(Atom::from("objectSpread2"), ctx)
self.ctx.helper_loader.load(Helper::ObjectSpread2, ctx)
}
}