Skip to content
Merged
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
7 changes: 5 additions & 2 deletions turbopack/crates/turbopack-ecmascript/benches/analyzer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,12 @@ pub fn benchmark(c: &mut Criterion) {
for result in results {
let entry = result.unwrap();
if entry.file_type().unwrap().is_dir() {
let name = entry.file_name().into_string().unwrap();
let input = entry.path().join("input.js");

if !input.exists() {
eprintln!("Missing file: {input:?} skipping");
continue;
}
let name = entry.file_name().into_string().unwrap();
let cm = Arc::new(SourceMap::new(FilePathMapping::empty()));
let fm = cm.load_file(&input).unwrap();
GLOBALS.set(&swc_core::common::Globals::new(), || {
Expand Down
146 changes: 101 additions & 45 deletions turbopack/crates/turbopack-ecmascript/src/analyzer/graph.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,11 @@ impl EffectsBlock {
pub fn is_empty(&self) -> bool {
self.effects.is_empty()
}
fn normalize(&mut self) {
for e in self.effects.iter_mut() {
e.normalize();
}
}
}

#[derive(Debug)]
Expand All @@ -70,47 +75,52 @@ pub enum ConditionalKind {
r#else: Box<EffectsBlock>,
},
/// The expression on the right side of the `&&` operator.
And { expr: Box<EffectsBlock> },
And { rhs_effects: Vec<Effect> },
/// The expression on the right side of the `||` operator.
Or { expr: Box<EffectsBlock> },
Or { rhs_effects: Vec<Effect> },
/// The expression on the right side of the `??` operator.
NullishCoalescing { expr: Box<EffectsBlock> },
NullishCoalescing { rhs_effects: Vec<Effect> },
/// The expression on the right side of a labeled statement.
Labeled { body: Box<EffectsBlock> },
}

impl ConditionalKind {
fn is_empty(&self) -> bool {
match self {
ConditionalKind::If { then: block }
| ConditionalKind::Else { r#else: block }
| ConditionalKind::Labeled { body: block } => block.effects.is_empty(),
ConditionalKind::IfElse { then, r#else, .. }
| ConditionalKind::Ternary { then, r#else, .. } => then.is_empty() && r#else.is_empty(),
ConditionalKind::And { rhs_effects, .. }
| ConditionalKind::Or { rhs_effects, .. }
| ConditionalKind::NullishCoalescing { rhs_effects, .. } => rhs_effects.is_empty(),
ConditionalKind::IfElseMultiple { then, r#else, .. } => {
then.iter().chain(r#else.iter()).all(|b| b.is_empty())
}
}
}
/// Normalizes all contained values.
pub fn normalize(&mut self) {
match self {
ConditionalKind::If { then: block }
| ConditionalKind::Else { r#else: block }
| ConditionalKind::And { expr: block, .. }
| ConditionalKind::Or { expr: block, .. }
| ConditionalKind::NullishCoalescing { expr: block, .. } => {
for effect in &mut block.effects {
effect.normalize();
}
}
| ConditionalKind::Labeled { body: block } => block.normalize(),
ConditionalKind::IfElse { then, r#else, .. }
| ConditionalKind::Ternary { then, r#else, .. } => {
for effect in &mut then.effects {
effect.normalize();
}
for effect in &mut r#else.effects {
then.normalize();
r#else.normalize();
}
ConditionalKind::And { rhs_effects, .. }
| ConditionalKind::Or { rhs_effects, .. }
| ConditionalKind::NullishCoalescing { rhs_effects, .. } => {
for effect in rhs_effects.iter_mut() {
effect.normalize();
}
}
ConditionalKind::IfElseMultiple { then, r#else, .. } => {
for block in then.iter_mut().chain(r#else.iter_mut()) {
for effect in &mut block.effects {
effect.normalize();
}
}
}
ConditionalKind::Labeled { body } => {
for effect in &mut body.effects {
effect.normalize();
block.normalize();
}
}
}
Expand All @@ -131,9 +141,7 @@ impl EffectArg {
EffectArg::Value(value) => value.normalize(),
EffectArg::Closure(value, effects) => {
value.normalize();
for effect in &mut effects.effects {
effect.normalize();
}
effects.normalize();
}
EffectArg::Spread => {}
}
Expand Down Expand Up @@ -2352,6 +2360,49 @@ impl VisitAstPath for Analyzer<'_> {
);
}

fn visit_bin_expr<'ast: 'r, 'r>(
&mut self,
expr: &'ast BinExpr,
ast_path: &mut AstNodePath<AstParentNodeRef<'r>>,
) {
// Some binary operators have control flow semantics.
match expr.op {
BinaryOp::LogicalAnd | BinaryOp::LogicalOr | BinaryOp::NullishCoalescing => {
// Visit the left hand node
{
let mut ast_path =
ast_path.with_guard(AstParentNodeRef::BinExpr(expr, BinExprField::Left));
expr.left.visit_with_ast_path(self, &mut ast_path);
};
let prev_effects = take(&mut self.effects);
let rhs_effects = {
let mut ast_path =
ast_path.with_guard(AstParentNodeRef::BinExpr(expr, BinExprField::Right));
expr.right.visit_with_ast_path(self, &mut ast_path);
take(&mut self.effects)
};
self.effects = prev_effects;
self.add_conditional_effect(
&expr.left,
ast_path,
AstParentKind::BinExpr(BinExprField::Left),
expr.span(),
match expr.op {
BinaryOp::LogicalAnd => ConditionalKind::And { rhs_effects },
BinaryOp::LogicalOr => ConditionalKind::Or { rhs_effects },
BinaryOp::NullishCoalescing => {
ConditionalKind::NullishCoalescing { rhs_effects }
}
_ => unreachable!(),
},
);
}
_ => <BinExpr as VisitWithAstPath<Self>>::visit_children_with_ast_path(
expr, self, ast_path,
),
}
}

fn visit_if_stmt<'ast: 'r, 'r>(
&mut self,
stmt: &'ast IfStmt,
Expand Down Expand Up @@ -2622,7 +2673,7 @@ impl Analyzer<'_> {
{
return;
}
let condition = Box::new(self.eval_context.eval(test));
let condition = self.eval_context.eval(test);
if condition.is_unknown() {
if let Some(mut then) = then {
self.effects.append(&mut then.effects);
Expand All @@ -2632,6 +2683,7 @@ impl Analyzer<'_> {
}
return;
}
let condition = Box::new(condition);
match (early_return_when_true, early_return_when_false) {
(true, false) => {
self.early_return_stack.push(EarlyReturn::Conditional {
Expand Down Expand Up @@ -2659,20 +2711,21 @@ impl Analyzer<'_> {
}
(false, false) | (true, true) => {
let kind = match (then, r#else) {
(Some(then), Some(r#else)) => ConditionalKind::IfElse { then, r#else },
(Some(then), None) => ConditionalKind::If { then },
(None, Some(r#else)) => ConditionalKind::Else { r#else },
(None, None) => {
// No effects, ignore
return;
}
(Some(then), Some(r#else)) => Some(ConditionalKind::IfElse { then, r#else }),
(Some(then), None) => Some(ConditionalKind::If { then }),
(None, Some(r#else)) => Some(ConditionalKind::Else { r#else }),
(None, None) => None,
};
self.add_effect(Effect::Conditional {
condition,
kind: Box::new(kind),
ast_path: as_parent_path_with(ast_path, condition_ast_kind),
span,
});
if let Some(kind) = kind
&& !kind.is_empty()
{
self.add_effect(Effect::Conditional {
condition,
kind: Box::new(kind),
ast_path: as_parent_path_with(ast_path, condition_ast_kind),
span,
});
}
if early_return_when_false && early_return_when_true {
self.early_return_stack.push(EarlyReturn::Always {
prev_effects: take(&mut self.effects),
Expand All @@ -2691,7 +2744,10 @@ impl Analyzer<'_> {
span: Span,
mut cond_kind: ConditionalKind,
) {
let condition = Box::new(self.eval_context.eval(test));
if cond_kind.is_empty() {
return;
}
let condition = self.eval_context.eval(test);
if condition.is_unknown() {
match &mut cond_kind {
ConditionalKind::If { then } => {
Expand All @@ -2713,18 +2769,18 @@ impl Analyzer<'_> {
self.effects.append(&mut block.effects);
}
}
ConditionalKind::And { expr }
| ConditionalKind::Or { expr }
| ConditionalKind::NullishCoalescing { expr } => {
self.effects.append(&mut expr.effects);
ConditionalKind::And { rhs_effects }
| ConditionalKind::Or { rhs_effects }
| ConditionalKind::NullishCoalescing { rhs_effects } => {
self.effects.append(rhs_effects);
}
ConditionalKind::Labeled { body } => {
self.effects.append(&mut body.effects);
}
}
} else {
self.add_effect(Effect::Conditional {
condition,
condition: Box::new(condition),
kind: Box::new(cond_kind),
ast_path: as_parent_path_with(ast_path, ast_kind),
span,
Expand Down
21 changes: 9 additions & 12 deletions turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4107,15 +4107,14 @@ mod tests {
.await;
resolved.push((format!("{parent} -> {i} conditional"), condition));
match *kind {
ConditionalKind::If { then } => {
queue
.extend(then.effects.into_iter().rev().map(|e| (i, e)));
}
ConditionalKind::Else { r#else } => {
ConditionalKind::If { then: block }
| ConditionalKind::Else { r#else: block }
| ConditionalKind::Labeled { body: block } => {
queue.extend(
r#else.effects.into_iter().rev().map(|e| (i, e)),
block.effects.into_iter().rev().map(|e| (i, e)),
);
}

ConditionalKind::IfElse { then, r#else }
| ConditionalKind::Ternary { then, r#else } => {
queue.extend(
Expand All @@ -4136,12 +4135,10 @@ mod tests {
);
}
}
ConditionalKind::And { expr }
| ConditionalKind::Or { expr }
| ConditionalKind::NullishCoalescing { expr }
| ConditionalKind::Labeled { body: expr } => {
queue
.extend(expr.effects.into_iter().rev().map(|e| (i, e)));
ConditionalKind::And { rhs_effects }
| ConditionalKind::Or { rhs_effects }
| ConditionalKind::NullishCoalescing { rhs_effects } => {
queue.extend(rhs_effects.into_iter().rev().map(|e| (i, e)));
}
};
steps
Expand Down
7 changes: 5 additions & 2 deletions turbopack/crates/turbopack-ecmascript/src/code_gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ use crate::{
exports_info::{ExportsInfoBinding, ExportsInfoRef},
ident::IdentReplacement,
member::MemberReplacement,
replace_parent_with_child::ReplaceParentWithChild,
require_context::RequireContextAssetReferenceCodeGen,
unreachable::Unreachable,
worker::WorkerAssetReferenceCodeGen,
Expand Down Expand Up @@ -199,6 +200,7 @@ pub enum CodeGen {
RequireContextAssetReferenceCodeGen(RequireContextAssetReferenceCodeGen),
UrlAssetReferenceCodeGen(UrlAssetReferenceCodeGen),
WorkerAssetReferenceCodeGen(WorkerAssetReferenceCodeGen),
ReplaceParentWithChild(ReplaceParentWithChild),
}

impl CodeGen {
Expand All @@ -212,8 +214,8 @@ impl CodeGen {
match self {
Self::AmdDefineWithDependenciesCodeGen(v) => v.code_generation(ctx).await,
Self::CjsRequireCacheAccess(v) => v.code_generation(ctx).await,
Self::ConstantConditionCodeGen(v) => v.code_generation(ctx).await,
Self::ConstantValueCodeGen(v) => v.code_generation(ctx).await,
Self::ConstantConditionCodeGen(v) => v.code_generation(),
Self::ConstantValueCodeGen(v) => v.code_generation(),
Self::DynamicExpression(v) => v.code_generation(ctx).await,
Self::EsmBinding(v) => v.code_generation(ctx, scope_hoisting_context).await,
Self::EsmModuleItem(v) => v.code_generation(ctx).await,
Expand All @@ -231,6 +233,7 @@ impl CodeGen {
Self::RequireContextAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
Self::UrlAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
Self::WorkerAssetReferenceCodeGen(v) => v.code_generation(ctx).await,
Self::ReplaceParentWithChild(v) => v.code_generation(),
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion turbopack/crates/turbopack-ecmascript/src/path_visitor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -88,7 +88,7 @@ impl<'a> ApplyVisitors<'a, '_> {
let kind = ast_path[index];
if let Some(visitors) = find_range(current_visitors, &kind, index) {
// visitors contains all items that match kind at index. Some of them terminate
// here, some need furth visiting. The terminating items are at the start due to
// here, some need further visiting. The terminating items are at the start due to
// sorting of the list.
index += 1;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
use anyhow::Result;
use serde::{Deserialize, Serialize};
use swc_core::quote;
use turbo_tasks::{NonLocalValue, Vc, debug::ValueDebugFormat, trace::TraceRawVcs};
use turbopack_core::chunk::ChunkingContext;
use turbo_tasks::{NonLocalValue, debug::ValueDebugFormat, trace::TraceRawVcs};

use super::AstPath;
use crate::{
Expand Down Expand Up @@ -32,10 +31,7 @@ impl ConstantConditionCodeGen {
ConstantConditionCodeGen { value, path }
}

pub async fn code_generation(
&self,
_chunking_context: Vc<Box<dyn ChunkingContext>>,
) -> Result<CodeGeneration> {
pub fn code_generation(&self) -> Result<CodeGeneration> {
let value = self.value;
let visitors = [create_visitor!(
exact,
Expand Down
Loading
Loading