Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 4 additions & 2 deletions crates/swc_bundler/src/id.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,15 +81,17 @@ impl Id {
}

impl IdentLike for Id {
type Id = (Atom, SyntaxContext);

fn from_ident(i: &Ident) -> Self {
i.into()
}

fn to_id(&self) -> (Atom, SyntaxContext) {
fn to_id(&self) -> Self::Id {
(self.0.clone(), self.1)
}

fn into_id(self) -> (Atom, SyntaxContext) {
fn into_id(self) -> Self::Id {
(self.0, self.1)
}
}
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_bundler/src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ pub(crate) trait ExprExt: Into<Expr> {
#[track_caller]
fn assign_to<T>(self, lhs: T) -> VarDeclarator
where
T: IdentLike,
T: IdentLike<Id = Id>,
{
let init = self.into();
let lhs = lhs.into_id();
Expand Down
1 change: 1 addition & 0 deletions crates/swc_ecma_ast/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ shrink-to-fit = [
arbitrary = { workspace = true, features = ["derive"], optional = true }
bitflags = { workspace = true }
bytecheck = { workspace = true, optional = true }
indexmap = { workspace = true }
is-macro = { workspace = true }
num-bigint = { workspace = true, features = ["serde"] }
once_cell = { workspace = true }
Expand Down
45 changes: 45 additions & 0 deletions crates/swc_ecma_ast/src/ident.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
use std::{
borrow::Cow,
fmt::Display,
hash::{Hash, Hasher},
ops::{Deref, DerefMut},
};

use indexmap::map::RawEntryApiV1;
use once_cell::sync::Lazy;
use phf::phf_set;
use rustc_hash::FxHashSet;
Expand Down Expand Up @@ -527,6 +529,49 @@ pub unsafe fn unsafe_id_from_ident(id: &Ident) -> UnsafeId {
/// See [Ident] for documentation.
pub type Id = (Atom, SyntaxContext);

#[derive(PartialEq, Eq, Hash, Clone, Copy, Debug)]
pub struct IdIdx(u32);

#[derive(Default, Debug)]
pub struct Ids {
map: indexmap::IndexMap<Id, (), rustc_hash::FxBuildHasher>,
}

impl Ids {
#[inline(always)]
pub fn intern_ident(&mut self, ident: &Ident) -> IdIdx {
self.intern(&ident.sym, ident.ctxt)
}

pub fn intern(&mut self, atom: &Atom, ctxt: SyntaxContext) -> IdIdx {
let mut hasher = rustc_hash::FxHasher::default();
atom.hash(&mut hasher);
ctxt.hash(&mut hasher);
let hash = hasher.finish();

use indexmap::map::raw_entry_v1::RawEntryMut::*;
let idx = match self
.map
.raw_entry_mut_v1()
.from_hash(hash, |id| id.1 == ctxt && id.0.eq(atom))
{
Occupied(occ) => occ.index(),
Vacant(vac) => {
let id = (atom.clone(), ctxt);
let idx = vac.index();
vac.insert_hashed_nocheck(hash, id, ());
idx
}
};
IdIdx(idx as u32)
}

#[inline(always)]
pub fn get(&self, idx: IdIdx) -> &Id {
self.map.get_index(idx.0 as usize).unwrap().0
}
}

impl Take for Ident {
fn dummy() -> Self {
Ident::new_no_ctxt(atom!(""), DUMMY_SP)
Expand Down
4 changes: 2 additions & 2 deletions crates/swc_ecma_ast/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@ pub use self::{
expr::*,
function::{Function, Param, ParamOrTsParamProp},
ident::{
unsafe_id, unsafe_id_from_ident, BindingIdent, EsReserved, Id, Ident, IdentName,
PrivateName, UnsafeId,
unsafe_id, unsafe_id_from_ident, BindingIdent, EsReserved, Id, IdIdx, Ident, IdentName,
Ids, PrivateName, UnsafeId,
},
jsx::{
JSXAttr, JSXAttrName, JSXAttrOrSpread, JSXAttrValue, JSXClosingElement, JSXClosingFragment,
Expand Down
28 changes: 18 additions & 10 deletions crates/swc_ecma_minifier/src/compress/hoist_decls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use rustc_hash::FxHashSet;
use swc_common::{pass::Repeated, util::take::Take, DUMMY_SP};
use swc_ecma_ast::*;
use swc_ecma_usage_analyzer::analyzer::UsageAnalyzer;
use swc_ecma_utils::{find_pat_ids, StmtLike};
use swc_ecma_utils::{find_pat_ids, find_pat_ids_with_idx, StmtLike};
use swc_ecma_visit::{noop_visit_mut_type, VisitMut, VisitMutWith, VisitWith};

use super::util::drop_invalid_stmts;
Expand All @@ -18,18 +18,24 @@ pub(super) struct DeclHoisterConfig {
pub _top_level: bool,
}

pub(super) fn decl_hoister(config: DeclHoisterConfig, data: &ProgramData) -> Hoister {
pub(super) fn decl_hoister<'a>(
config: DeclHoisterConfig,
data: &'a ProgramData,
id_map: &'a mut Ids,
) -> Hoister<'a> {
Hoister {
config,
changed: false,
data,
id_map,
}
}

pub(super) struct Hoister<'a> {
config: DeclHoisterConfig,
changed: bool,
data: &'a ProgramData,
id_map: &'a mut Ids,
}

impl Repeated for Hoister<'_> {
Expand All @@ -42,11 +48,11 @@ impl Repeated for Hoister<'_> {
}
}

impl Hoister<'_> {
impl<'a> Hoister<'a> {
fn handle_stmt_likes<T>(&mut self, stmts: &mut Vec<T>)
where
T: StmtLike + IsModuleItem + ModuleItemExt,
Vec<T>: for<'aa> VisitMutWith<Hoister<'aa>> + VisitWith<UsageAnalyzer<ProgramData>>,
Vec<T>: for<'aa> VisitMutWith<Hoister<'aa>> + VisitWith<UsageAnalyzer<'a, ProgramData>>,
{
stmts.visit_mut_children_with(self);
let len = stmts.len();
Expand All @@ -55,7 +61,7 @@ impl Hoister<'_> {
Some(stmt) => match stmt {
Stmt::Decl(Decl::Fn(..)) if self.config.hoist_fns => 1,
Stmt::Decl(Decl::Var(var)) if self.config.hoist_vars => {
let ids: Vec<Id> = find_pat_ids(&var.decls);
let ids: Vec<IdIdx> = find_pat_ids_with_idx(&var.decls, self.id_map);

if ids.iter().any(|id| {
self.data
Expand Down Expand Up @@ -95,7 +101,7 @@ impl Hoister<'_> {
let mut var_decls = Vec::new();
let mut fn_decls = Vec::with_capacity(stmts.len());
let mut new_stmts = Vec::with_capacity(stmts.len());
let mut done = FxHashSet::default();
let mut done: FxHashSet<IdIdx> = FxHashSet::default();

let mut found_non_var_decl = false;
for stmt in stmts.take() {
Expand All @@ -122,14 +128,15 @@ impl Hoister<'_> {
let ids: Vec<Ident> = find_pat_ids(&decl.name);

for id in ids {
if done.insert(id.to_id()) {
let idx = self.id_map.intern_ident(&id);
if done.insert(idx) {
// If the enclosing function declares parameter with same
// name, we can drop a varaible.
if decl.init.is_none()
&& self
.data
.vars
.get(&id.to_id())
.get(&idx)
.map(|v| {
v.flags.contains(
VarUsageInfoFlags::DECLARED_AS_FN_PARAM,
Expand Down Expand Up @@ -209,13 +216,14 @@ impl Hoister<'_> {

let preserve = match &decl.name {
Pat::Ident(name) => {
let id = self.id_map.intern_ident(&name);
// If the enclosing function declares parameter with same
// name, we can drop a varaible. (If it's side-effect free).
if decl.init.is_none()
&& self
.data
.vars
.get(&name.to_id())
.get(&id)
.map(|v| {
v.flags.contains(
VarUsageInfoFlags::DECLARED_AS_FN_PARAM,
Expand All @@ -226,7 +234,7 @@ impl Hoister<'_> {
return false;
}

done.insert(name.to_id())
done.insert(id)
}
_ => true,
};
Expand Down
10 changes: 8 additions & 2 deletions crates/swc_ecma_minifier/src/compress/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ pub(crate) fn compressor<'a, M>(
options: &'a CompressOptions,
mangle_options: Option<&'a MangleOptions>,
mode: &'a M,
id_map: &'a mut Ids,
) -> impl 'a + Pass
where
M: Mode,
Expand All @@ -47,6 +48,7 @@ where
changed: false,
pass: 1,
mode,
id_map,
}
}

Expand All @@ -58,6 +60,7 @@ struct Compressor<'a> {
pass: usize,

mode: &'a dyn Mode,
id_map: &'a mut Ids,
}

impl CompilerPass for Compressor<'_> {
Expand All @@ -80,7 +83,7 @@ impl Compressor<'_> {
);

if self.options.hoist_vars || self.options.hoist_fns {
let data = analyze(&*n, Some(self.marks), false);
let data = analyze(&*n, Some(self.marks), false, self.id_map);

let mut v = decl_hoister(
DeclHoisterConfig {
Expand All @@ -89,6 +92,7 @@ impl Compressor<'_> {
_top_level: self.options.top_level(),
},
&data,
self.id_map,
);
n.visit_mut_with(&mut v);
self.changed |= v.changed();
Expand Down Expand Up @@ -145,6 +149,7 @@ impl Compressor<'_> {

let mut visitor = pure_optimizer(
self.options,
self.id_map,
self.marks,
PureOptimizerConfig {
enable_join_vars: self.pass > 1,
Expand Down Expand Up @@ -173,7 +178,7 @@ impl Compressor<'_> {
{
let _timer = timer!("apply full optimizer");

let mut data = analyze(&*n, Some(self.marks), false);
let mut data = analyze(&*n, Some(self.marks), false, self.id_map);

// TODO: reset_opt_flags
//
Expand All @@ -185,6 +190,7 @@ impl Compressor<'_> {
self.mangle_options,
&mut data,
self.mode,
self.id_map,
);
n.visit_mut_with(&mut visitor);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ impl Optimizer<'_> {
Pat::Ident(i) => self
.data
.vars
.get(&i.id.to_id())
.get(&self.id_map.intern_ident(&i.id))
.map(|v| v.declared_count >= 2)
.unwrap_or(false),
_ => true,
Expand Down
2 changes: 1 addition & 1 deletion crates/swc_ecma_minifier/src/compress/optimize/bools.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ impl Optimizer<'_> {
) if &**l_v == "undefined" => {
// TODO?
if let Expr::Ident(arg) = &**arg {
if let Some(usage) = o.data.vars.get(&arg.to_id()) {
if let Some(usage) = o.data.vars.get(&o.id_map.intern_ident(arg)) {
if !usage.flags.contains(VarUsageInfoFlags::DECLARED) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -387,7 +387,7 @@ impl Optimizer<'_> {
// to f(a, a = 1 ? true : false)
let side_effects_in_test = test.may_have_side_effects(self.ctx.expr_ctx);

if self.data.contains_unresolved(test) {
if self.data.contains_unresolved(test, self.id_map) {
return None;
}

Expand All @@ -404,7 +404,7 @@ impl Optimizer<'_> {
let side_effect_free = self
.data
.vars
.get(&cons_callee.to_id())
.get(&self.id_map.intern_ident(cons_callee))
.map(|v| {
v.flags.contains(
VarUsageInfoFlags::IS_FN_LOCAL.union(VarUsageInfoFlags::DECLARED),
Expand Down Expand Up @@ -526,7 +526,7 @@ impl Optimizer<'_> {
}

(Expr::New(cons), Expr::New(alt)) => {
if self.data.contains_unresolved(test) {
if self.data.contains_unresolved(test, self.id_map) {
return None;
}

Expand Down Expand Up @@ -590,7 +590,7 @@ impl Optimizer<'_> {
) if cons.left.eq_ignore_span(&alt.left) && cons.left.as_ident().is_some() => {
if self
.data
.ident_is_unresolved(&cons.left.as_ident().unwrap().id)
.ident_is_unresolved(&cons.left.as_ident().unwrap().id, self.id_map)
{
return None;
}
Expand Down
4 changes: 2 additions & 2 deletions crates/swc_ecma_minifier/src/compress/optimize/dead_code.rs
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ impl Optimizer<'_> {
if self
.data
.vars
.get(&lhs.to_id())
.get(&self.id_map.intern_ident(lhs))
.map(|var| {
var.flags.contains(
VarUsageInfoFlags::DECLARED.union(VarUsageInfoFlags::IS_FN_LOCAL),
Expand Down Expand Up @@ -97,7 +97,7 @@ impl Optimizer<'_> {
if self
.data
.vars
.get(&lhs.to_id())
.get(&self.id_map.intern_ident(lhs))
.map(|var| {
var.flags.contains(
VarUsageInfoFlags::DECLARED.union(VarUsageInfoFlags::IS_FN_LOCAL),
Expand Down
8 changes: 4 additions & 4 deletions crates/swc_ecma_minifier/src/compress/optimize/evaluate.rs
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ impl Optimizer<'_> {
}) = e
{
if let Expr::Ident(obj) = &**obj {
let metadata = *self.functions.get(&obj.to_id())?;

let usage = self.data.vars.get(&obj.to_id())?;
let hashed_id = self.id_map.intern_ident(obj);
let metadata = *self.functions.get(&hashed_id)?;
let usage = self.data.vars.get(&hashed_id)?;

if usage.flags.contains(VarUsageInfoFlags::REASSIGNED) {
return None;
Expand Down Expand Up @@ -101,7 +101,7 @@ impl Optimizer<'_> {
if self
.data
.vars
.get(&i.to_id())
.get(&self.id_map.intern_ident(i))
.map(|var| var.flags.contains(VarUsageInfoFlags::DECLARED))
.unwrap_or(false)
{
Expand Down
Loading
Loading