Skip to content
Merged
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
64 changes: 18 additions & 46 deletions turbopack/crates/turbopack-ecmascript/src/analyzer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2449,7 +2449,7 @@ impl JsValue {
LogicalOperator::And => all_if_known(list, JsValue::is_truthy),
LogicalOperator::Or => any_if_known(list, JsValue::is_truthy),
LogicalOperator::NullishCoalescing => {
shortcircuit_if_known(list, JsValue::is_not_nullish, JsValue::is_truthy)
eval_shortcircuit(list, JsValue::is_not_nullish)?.is_truthy()
}
},
JsValue::Binary(_, box a, op, box b) => {
Expand All @@ -2460,25 +2460,6 @@ impl JsValue {
JsValue::Constant(a),
JsValue::Constant(b),
) if a.is_value_type() => Some(a == b),
(
PositiveBinaryOperator::StrictEqual,
JsValue::Constant(a),
JsValue::Constant(b),
) if a.is_value_type() => {
let same_type = {
use ConstantValue::*;
matches!(
(a, b),
(Num(_), Num(_))
| (Str(_), Str(_))
| (BigInt(_), BigInt(_))
| (True | False, True | False)
| (Undefined, Undefined)
| (Null, Null)
)
};
if same_type { Some(a == b) } else { None }
}
(
PositiveBinaryOperator::Equal,
JsValue::Constant(ConstantValue::Str(a)),
Expand Down Expand Up @@ -2527,12 +2508,8 @@ impl JsValue {
_ => merge_if_known(values, JsValue::is_nullish),
},
JsValue::Logical(_, op, list) => match op {
LogicalOperator::And => {
shortcircuit_if_known(list, JsValue::is_falsy, JsValue::is_nullish)
}
LogicalOperator::Or => {
shortcircuit_if_known(list, JsValue::is_truthy, JsValue::is_nullish)
}
LogicalOperator::And => eval_shortcircuit(list, JsValue::is_falsy)?.is_nullish(),
LogicalOperator::Or => eval_shortcircuit(list, JsValue::is_truthy)?.is_nullish(),
LogicalOperator::NullishCoalescing => all_if_known(list, JsValue::is_nullish),
},
_ => None,
Expand Down Expand Up @@ -2560,13 +2537,13 @@ impl JsValue {
} => merge_if_known(values, JsValue::is_empty_string),
JsValue::Logical(_, op, list) => match op {
LogicalOperator::And => {
shortcircuit_if_known(list, JsValue::is_falsy, JsValue::is_empty_string)
eval_shortcircuit(list, JsValue::is_falsy)?.is_empty_string()
}
LogicalOperator::Or => {
shortcircuit_if_known(list, JsValue::is_truthy, JsValue::is_empty_string)
eval_shortcircuit(list, JsValue::is_truthy)?.is_empty_string()
}
LogicalOperator::NullishCoalescing => {
shortcircuit_if_known(list, JsValue::is_not_nullish, JsValue::is_empty_string)
eval_shortcircuit(list, JsValue::is_not_nullish)?.is_empty_string()
}
},
// Booleans are not empty strings
Expand Down Expand Up @@ -2620,14 +2597,10 @@ impl JsValue {

JsValue::Add(_, list) => any_if_known(list, JsValue::is_string),
JsValue::Logical(_, op, list) => match op {
LogicalOperator::And => {
shortcircuit_if_known(list, JsValue::is_falsy, JsValue::is_string)
}
LogicalOperator::Or => {
shortcircuit_if_known(list, JsValue::is_truthy, JsValue::is_string)
}
LogicalOperator::And => eval_shortcircuit(list, JsValue::is_falsy)?.is_string(),
LogicalOperator::Or => eval_shortcircuit(list, JsValue::is_truthy)?.is_string(),
LogicalOperator::NullishCoalescing => {
shortcircuit_if_known(list, JsValue::is_not_nullish, JsValue::is_string)
eval_shortcircuit(list, JsValue::is_not_nullish)?.is_string()
}
},

Expand Down Expand Up @@ -2790,26 +2763,25 @@ fn any_if_known<T: Copy>(
all_if_known(list, |x| func(x).map(|x| !x)).map(|x| !x)
}

/// Selects the first element of the list where `use_item` is compile-time true.
/// For this element returns the result of `item_value`. Otherwise returns None.
fn shortcircuit_if_known<T: Copy>(
/// Selects the first element of the list where `matches` is compile-time true.
/// Returns this element; if no elements match, it returns the last item.
fn eval_shortcircuit<T: Copy>(
list: impl IntoIterator<Item = T>,
use_item: impl Fn(T) -> Option<bool>,
item_value: impl FnOnce(T) -> Option<bool>,
) -> Option<bool> {
matches: impl Fn(T) -> Option<bool>,
) -> Option<T> {
let mut it = list.into_iter().peekable();
while let Some(item) = it.next() {
if it.peek().is_none() {
return item_value(item);
return Some(item);
} else {
match use_item(item) {
Some(true) => return item_value(item),
match matches(item) {
Some(true) => return Some(item),
None => return None,
_ => {}
}
}
}
None
unreachable!("Binary operators should always have operands.")
}

// Visiting
Expand Down
Loading