Skip to content

Commit b7e095f

Browse files
sapphi-redBoshen
authored andcommitted
refactor(ecmascript): pass IsGlobalReference to DetermineValueType instead of extending it
1 parent c62ed5e commit b7e095f

12 files changed

+81
-74
lines changed

crates/oxc_ecmascript/src/constant_evaluation/equality_comparison.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,15 @@
11
use oxc_ast::ast::{Expression, NumberBase};
22

3-
use super::{ConstantEvaluation, ValueType};
3+
use super::{ConstantEvaluation, DetermineValueType, ValueType};
44

55
/// <https://tc39.es/ecma262/#sec-abstract-equality-comparison>
66
pub(super) fn abstract_equality_comparison<'a>(
77
c: &impl ConstantEvaluation<'a>,
88
left_expr: &Expression<'a>,
99
right_expr: &Expression<'a>,
1010
) -> Option<bool> {
11-
let left = c.expression_value_type(left_expr);
12-
let right = c.expression_value_type(right_expr);
11+
let left = left_expr.value_type(c);
12+
let right = right_expr.value_type(c);
1313
if left != ValueType::Undetermined && right != ValueType::Undetermined {
1414
if left == right {
1515
return strict_equality_comparison(c, left_expr, right_expr);
@@ -83,8 +83,8 @@ pub(super) fn strict_equality_comparison<'a>(
8383
left_expr: &Expression<'a>,
8484
right_expr: &Expression<'a>,
8585
) -> Option<bool> {
86-
let left = c.expression_value_type(left_expr);
87-
let right = c.expression_value_type(right_expr);
86+
let left = left_expr.value_type(c);
87+
let right = right_expr.value_type(c);
8888
if !left.is_undetermined() && !right.is_undetermined() {
8989
// Strict equality can only be true for values of the same type.
9090
if left != right {

crates/oxc_ecmascript/src/constant_evaluation/mod.rs

+15-14
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,10 @@ use num_traits::{FromPrimitive, ToPrimitive, Zero};
66
use equality_comparison::{abstract_equality_comparison, strict_equality_comparison};
77
use oxc_ast::{ast::*, AstBuilder};
88

9-
use crate::{side_effects::MayHaveSideEffects, ToBigInt, ToBoolean, ToInt32, ToJsString, ToNumber};
9+
use crate::{
10+
is_global_reference::IsGlobalReference, side_effects::MayHaveSideEffects, ToBigInt, ToBoolean,
11+
ToInt32, ToJsString, ToNumber,
12+
};
1013

1114
mod equality_comparison;
1215
mod is_literal_value;
@@ -16,7 +19,7 @@ pub use is_literal_value::IsLiteralValue;
1619
pub use value::ConstantValue;
1720
pub use value_type::{DetermineValueType, ValueType};
1821

19-
pub trait ConstantEvaluation<'a>: DetermineValueType {
22+
pub trait ConstantEvaluation<'a>: IsGlobalReference {
2023
fn ast(&self) -> AstBuilder<'a>;
2124

2225
fn resolve_binding(&self, ident: &IdentifierReference<'a>) -> Option<ConstantValue<'a>> {
@@ -244,8 +247,8 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
244247
if left.may_have_side_effects(self) || right.may_have_side_effects(self) {
245248
return None;
246249
}
247-
let left_type = self.expression_value_type(left);
248-
let right_type = self.expression_value_type(right);
250+
let left_type = left.value_type(self);
251+
let right_type = right.value_type(self);
249252
if left_type.is_string() || right_type.is_string() {
250253
let lval = self.eval_expression(left)?;
251254
let rval = self.eval_expression(right)?;
@@ -329,9 +332,7 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
329332
})
330333
}
331334
BinaryOperator::BitwiseAnd | BinaryOperator::BitwiseOR | BinaryOperator::BitwiseXOR => {
332-
if self.expression_value_type(left).is_bigint()
333-
&& self.expression_value_type(right).is_bigint()
334-
{
335+
if left.value_type(self).is_bigint() && right.value_type(self).is_bigint() {
335336
let left_val = self.get_side_free_bigint_value(left)?;
336337
let right_val = self.get_side_free_bigint_value(right)?;
337338
let result_val: BigInt = match operator {
@@ -367,12 +368,12 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
367368
if matches!(name, "Object" | "Number" | "Boolean" | "String")
368369
&& self.is_global_reference(right_ident) == Some(true)
369370
{
370-
let left_ty = self.expression_value_type(left);
371+
let left_ty = left.value_type(self);
371372
if left_ty.is_undetermined() {
372373
return None;
373374
}
374375
return Some(ConstantValue::Boolean(
375-
name == "Object" && self.expression_value_type(left).is_object(),
376+
name == "Object" && left.value_type(self).is_object(),
376377
));
377378
}
378379
}
@@ -423,7 +424,7 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
423424
if expr.argument.may_have_side_effects(self) {
424425
return None;
425426
}
426-
let arg_ty = self.expression_value_type(&expr.argument);
427+
let arg_ty = expr.argument.value_type(self);
427428
let s = match arg_ty {
428429
ValueType::BigInt => "bigint",
429430
ValueType::Number => "number",
@@ -453,7 +454,7 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
453454
UnaryOperator::UnaryPlus => {
454455
self.get_side_free_number_value(&expr.argument).map(ConstantValue::Number)
455456
}
456-
UnaryOperator::UnaryNegation => match self.expression_value_type(&expr.argument) {
457+
UnaryOperator::UnaryNegation => match expr.argument.value_type(self) {
457458
ValueType::BigInt => self
458459
.get_side_free_bigint_value(&expr.argument)
459460
.map(|v| -v)
@@ -466,7 +467,7 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
466467
ValueType::Null => Some(ConstantValue::Number(-0.0)),
467468
_ => None,
468469
},
469-
UnaryOperator::BitwiseNot => match self.expression_value_type(&expr.argument) {
470+
UnaryOperator::BitwiseNot => match expr.argument.value_type(self) {
470471
ValueType::BigInt => self
471472
.get_side_free_bigint_value(&expr.argument)
472473
.map(|v| !v)
@@ -535,8 +536,8 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
535536

536537
// a. Let px be ? ToPrimitive(x, NUMBER).
537538
// b. Let py be ? ToPrimitive(y, NUMBER).
538-
let px = self.expression_value_type(x);
539-
let py = self.expression_value_type(y);
539+
let px = x.value_type(self);
540+
let py = y.value_type(self);
540541

541542
// If the operands are not primitives, `ToPrimitive` is *not* a noop.
542543
if px.is_undetermined() || px.is_object() || py.is_undetermined() || py.is_object() {

crates/oxc_ecmascript/src/constant_evaluation/value_type.rs

+41-29
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,13 @@ impl ValueType {
6161
/// Evaluate the expression and attempt to determine which ValueType it could resolve to.
6262
/// This function ignores the cases that throws an error, e.g. `foo * 0` can throw an error when `foo` is a bigint.
6363
/// To detect those cases, use [`crate::side_effects::MayHaveSideEffects`].
64-
pub trait DetermineValueType: IsGlobalReference {
65-
fn expression_value_type(&self, expr: &Expression<'_>) -> ValueType {
66-
match expr {
64+
pub trait DetermineValueType {
65+
fn value_type(&self, is_global_reference: &impl IsGlobalReference) -> ValueType;
66+
}
67+
68+
impl DetermineValueType for Expression<'_> {
69+
fn value_type(&self, is_global_reference: &impl IsGlobalReference) -> ValueType {
70+
match self {
6771
Expression::BigIntLiteral(_) => ValueType::BigInt,
6872
Expression::BooleanLiteral(_) | Expression::PrivateInExpression(_) => {
6973
ValueType::Boolean
@@ -84,7 +88,7 @@ pub trait DetermineValueType: IsGlobalReference {
8488
}
8589
}
8690
Expression::Identifier(ident) => {
87-
if self.is_global_reference(ident) == Some(true) {
91+
if is_global_reference.is_global_reference(ident) == Some(true) {
8892
match ident.name.as_str() {
8993
"undefined" => ValueType::Undefined,
9094
"NaN" | "Infinity" => ValueType::Number,
@@ -97,7 +101,7 @@ pub trait DetermineValueType: IsGlobalReference {
97101
Expression::UnaryExpression(unary_expr) => match unary_expr.operator {
98102
UnaryOperator::Void => ValueType::Undefined,
99103
UnaryOperator::UnaryNegation | UnaryOperator::BitwiseNot => {
100-
let argument_ty = self.expression_value_type(&unary_expr.argument);
104+
let argument_ty = unary_expr.argument.value_type(is_global_reference);
101105
match argument_ty {
102106
ValueType::BigInt => ValueType::BigInt,
103107
// non-object values other than BigInt are converted to number by `ToNumber`
@@ -113,24 +117,26 @@ pub trait DetermineValueType: IsGlobalReference {
113117
UnaryOperator::LogicalNot | UnaryOperator::Delete => ValueType::Boolean,
114118
UnaryOperator::Typeof => ValueType::String,
115119
},
116-
Expression::BinaryExpression(e) => self.binary_expression_value_type(e),
120+
Expression::BinaryExpression(e) => e.value_type(is_global_reference),
117121
Expression::SequenceExpression(e) => e
118122
.expressions
119123
.last()
120-
.map_or(ValueType::Undetermined, |e| self.expression_value_type(e)),
121-
Expression::AssignmentExpression(e) => self.assignment_expression_value_type(e),
122-
Expression::ConditionalExpression(e) => self.conditional_expression_value_type(e),
123-
Expression::LogicalExpression(e) => self.logical_expression_value_type(e),
124-
Expression::ParenthesizedExpression(e) => self.expression_value_type(&e.expression),
124+
.map_or(ValueType::Undetermined, |e| e.value_type(is_global_reference)),
125+
Expression::AssignmentExpression(e) => e.value_type(is_global_reference),
126+
Expression::ConditionalExpression(e) => e.value_type(is_global_reference),
127+
Expression::LogicalExpression(e) => e.value_type(is_global_reference),
128+
Expression::ParenthesizedExpression(e) => e.expression.value_type(is_global_reference),
125129
_ => ValueType::Undetermined,
126130
}
127131
}
132+
}
128133

129-
fn binary_expression_value_type(&self, e: &BinaryExpression<'_>) -> ValueType {
130-
match e.operator {
134+
impl DetermineValueType for BinaryExpression<'_> {
135+
fn value_type(&self, is_global_reference: &impl IsGlobalReference) -> ValueType {
136+
match self.operator {
131137
BinaryOperator::Addition => {
132-
let left = self.expression_value_type(&e.left);
133-
let right = self.expression_value_type(&e.right);
138+
let left = self.left.value_type(is_global_reference);
139+
let right = self.right.value_type(is_global_reference);
134140
if left == ValueType::Boolean
135141
&& matches!(right, ValueType::Undefined | ValueType::Null | ValueType::Number)
136142
{
@@ -157,8 +163,8 @@ pub trait DetermineValueType: IsGlobalReference {
157163
| BinaryOperator::BitwiseXOR
158164
| BinaryOperator::BitwiseAnd
159165
| BinaryOperator::Exponential => {
160-
let left = self.expression_value_type(&e.left);
161-
let right = self.expression_value_type(&e.right);
166+
let left = self.left.value_type(is_global_reference);
167+
let right = self.right.value_type(is_global_reference);
162168
if left.is_bigint() || right.is_bigint() {
163169
ValueType::BigInt
164170
} else if !(left.is_object() || left.is_undetermined())
@@ -180,17 +186,19 @@ pub trait DetermineValueType: IsGlobalReference {
180186
| BinaryOperator::StrictEquality
181187
| BinaryOperator::StrictInequality
182188
| BinaryOperator::LessThan
183-
| BinaryOperator::LessEqualThan
184189
| BinaryOperator::GreaterThan
190+
| BinaryOperator::LessEqualThan
185191
| BinaryOperator::GreaterEqualThan => ValueType::Boolean,
186192
}
187193
}
194+
}
188195

189-
fn assignment_expression_value_type(&self, e: &AssignmentExpression<'_>) -> ValueType {
190-
match e.operator {
191-
AssignmentOperator::Assign => self.expression_value_type(&e.right),
196+
impl DetermineValueType for AssignmentExpression<'_> {
197+
fn value_type(&self, is_global_reference: &impl IsGlobalReference) -> ValueType {
198+
match self.operator {
199+
AssignmentOperator::Assign => self.right.value_type(is_global_reference),
192200
AssignmentOperator::Addition => {
193-
let right = self.expression_value_type(&e.right);
201+
let right = self.right.value_type(is_global_reference);
194202
if right.is_string() {
195203
ValueType::String
196204
} else {
@@ -207,7 +215,7 @@ pub trait DetermineValueType: IsGlobalReference {
207215
| AssignmentOperator::BitwiseXOR
208216
| AssignmentOperator::BitwiseAnd
209217
| AssignmentOperator::Exponential => {
210-
let right = self.expression_value_type(&e.right);
218+
let right = self.right.value_type(is_global_reference);
211219
if right.is_bigint() {
212220
ValueType::BigInt
213221
} else if !(right.is_object() || right.is_undetermined()) {
@@ -222,25 +230,29 @@ pub trait DetermineValueType: IsGlobalReference {
222230
| AssignmentOperator::LogicalNullish => ValueType::Undetermined,
223231
}
224232
}
233+
}
225234

226-
fn conditional_expression_value_type(&self, e: &ConditionalExpression<'_>) -> ValueType {
227-
let left = self.expression_value_type(&e.consequent);
235+
impl DetermineValueType for ConditionalExpression<'_> {
236+
fn value_type(&self, is_global_reference: &impl IsGlobalReference) -> ValueType {
237+
let left = self.consequent.value_type(is_global_reference);
228238
if left.is_undetermined() {
229239
return ValueType::Undetermined;
230240
}
231-
let right = self.expression_value_type(&e.alternate);
241+
let right = self.alternate.value_type(is_global_reference);
232242
if left == right {
233243
return left;
234244
}
235245
ValueType::Undetermined
236246
}
247+
}
237248

238-
fn logical_expression_value_type(&self, e: &LogicalExpression<'_>) -> ValueType {
239-
let left = self.expression_value_type(&e.left);
249+
impl DetermineValueType for LogicalExpression<'_> {
250+
fn value_type(&self, is_global_reference: &impl IsGlobalReference) -> ValueType {
251+
let left = self.left.value_type(is_global_reference);
240252
if !left.is_boolean() {
241253
return ValueType::Undetermined;
242254
}
243-
let right = self.expression_value_type(&e.right);
255+
let right = self.right.value_type(is_global_reference);
244256
if left == right {
245257
return left;
246258
}

crates/oxc_minifier/src/ctx.rs

+1-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use std::ops::Deref;
22

33
use oxc_ast::{ast::*, AstBuilder};
4-
use oxc_ecmascript::constant_evaluation::{ConstantEvaluation, ConstantValue, DetermineValueType};
4+
use oxc_ecmascript::constant_evaluation::{ConstantEvaluation, ConstantValue};
55
use oxc_semantic::{IsGlobalReference, SymbolTable};
66
use oxc_traverse::TraverseCtx;
77

@@ -22,8 +22,6 @@ impl oxc_ecmascript::is_global_reference::IsGlobalReference for Ctx<'_, '_> {
2222
}
2323
}
2424

25-
impl DetermineValueType for Ctx<'_, '_> {}
26-
2725
impl<'a> ConstantEvaluation<'a> for Ctx<'a, '_> {
2826
fn ast(&self) -> AstBuilder<'a> {
2927
self.ast

crates/oxc_minifier/src/peephole/fold_constants.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ impl<'a> PeepholeOptimizations {
8383
return None;
8484
}
8585
let object = member_expr.object();
86-
let ty = ctx.expression_value_type(object);
86+
let ty = object.value_type(&ctx);
8787
(ty.is_null() || ty.is_undefined())
8888
.then(|| ctx.value_to_expr(chain_expr.span, ConstantValue::Undefined))
8989
}
@@ -161,7 +161,7 @@ impl<'a> PeepholeOptimizations {
161161
) -> Option<Expression<'a>> {
162162
debug_assert_eq!(logical_expr.operator, LogicalOperator::Coalesce);
163163
let left = &logical_expr.left;
164-
let left_val = ctx.expression_value_type(left);
164+
let left_val = left.value_type(&ctx);
165165
match left_val {
166166
ValueType::Null | ValueType::Undefined => {
167167
Some(if left.may_have_side_effects(&ctx) {
@@ -306,7 +306,7 @@ impl<'a> PeepholeOptimizations {
306306

307307
// a + 'b' + 'c' -> a + 'bc'
308308
if let Expression::BinaryExpression(left_binary_expr) = &mut e.left {
309-
if ctx.expression_value_type(&left_binary_expr.right).is_string() {
309+
if left_binary_expr.right.value_type(&ctx).is_string() {
310310
if let (Some(left_str), Some(right_str)) = (
311311
ctx.get_side_free_string_value(&left_binary_expr.right),
312312
ctx.get_side_free_string_value(&e.right),
@@ -321,12 +321,9 @@ impl<'a> PeepholeOptimizations {
321321
}
322322

323323
// remove useless `+ ""` (e.g. `typeof foo + ""` -> `typeof foo`)
324-
if e.left.is_specific_string_literal("") && ctx.expression_value_type(&e.right).is_string()
325-
{
324+
if e.left.is_specific_string_literal("") && e.right.value_type(&ctx).is_string() {
326325
return Some(ctx.ast.move_expression(&mut e.right));
327-
} else if e.right.is_specific_string_literal("")
328-
&& ctx.expression_value_type(&e.left).is_string()
329-
{
326+
} else if e.right.is_specific_string_literal("") && e.left.value_type(&ctx).is_string() {
330327
return Some(ctx.ast.move_expression(&mut e.left));
331328
}
332329

@@ -444,7 +441,7 @@ impl<'a> PeepholeOptimizations {
444441
// `typeof a !== 'b'` -> `true``
445442
if let Expression::UnaryExpression(left) = &bin_expr.left {
446443
if left.operator.is_typeof() && bin_expr.operator.is_equality() {
447-
let right_ty = ctx.expression_value_type(&bin_expr.right);
444+
let right_ty = bin_expr.right.value_type(&ctx);
448445

449446
if !right_ty.is_undetermined() && right_ty != ValueType::String {
450447
return Some(ctx.ast.expression_boolean_literal(

crates/oxc_minifier/src/peephole/minimize_conditions.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,8 @@ impl<'a> PeepholeOptimizations {
113113
if !e.operator.is_equality() {
114114
return None;
115115
}
116-
let left = ctx.expression_value_type(&e.left);
117-
let right = ctx.expression_value_type(&e.right);
116+
let left = e.left.value_type(&ctx);
117+
let right = e.right.value_type(&ctx);
118118
if left.is_undetermined() || right.is_undetermined() {
119119
return None;
120120
}

crates/oxc_minifier/src/peephole/minimize_expression_in_boolean_context.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ impl<'a> PeepholeOptimizations {
5353
Expression::BinaryExpression(e)
5454
if e.operator.is_equality()
5555
&& matches!(&e.right, Expression::NumericLiteral(lit) if lit.value == 0.0)
56-
&& ctx.expression_value_type(&e.left).is_number() =>
56+
&& e.left.value_type(&ctx).is_number() =>
5757
{
5858
let argument = ctx.ast.move_expression(&mut e.left);
5959
*expr = if matches!(

crates/oxc_minifier/src/peephole/minimize_not_expression.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ impl<'a> PeepholeOptimizations {
3232
// `!!true` -> `true`
3333
// `!!false` -> `false`
3434
Expression::UnaryExpression(e)
35-
if e.operator.is_not() && ctx.expression_value_type(&e.argument).is_boolean() =>
35+
if e.operator.is_not() && e.argument.value_type(&ctx).is_boolean() =>
3636
{
3737
Some(ctx.ast.move_expression(&mut e.argument))
3838
}

crates/oxc_minifier/src/peephole/remove_unused_expression.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -162,7 +162,7 @@ impl<'a> PeepholeOptimizations {
162162
0 => true,
163163
1 => {
164164
let Some(arg) = e.arguments[0].as_expression() else { return false };
165-
let ty = ctx.expression_value_type(arg);
165+
let ty = arg.value_type(&ctx);
166166
matches!(
167167
ty,
168168
ValueType::Null

crates/oxc_minifier/src/peephole/replace_known_methods.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -379,7 +379,7 @@ impl<'a> PeepholeOptimizations {
379379
let first_arg = first_arg.to_expression_mut(); // checked above
380380

381381
let wrap_with_unary_plus_if_needed = |expr: &mut Expression<'a>| {
382-
if ctx.expression_value_type(&*expr).is_number() {
382+
if expr.value_type(&ctx).is_number() {
383383
ctx.ast.move_expression(expr)
384384
} else {
385385
ctx.ast.expression_unary(

0 commit comments

Comments
 (0)