diff --git a/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs b/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs index 9441e2b13ad98..35eb7f3b4f273 100644 --- a/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs @@ -265,8 +265,7 @@ fn try_fold_string_from_char_code<'a>( object: &Expression<'a>, ctx: &impl ConstantEvaluationCtx<'a>, ) -> Option> { - let Expression::Identifier(ident) = object else { return None }; - if ident.name != "String" || ctx.is_global_reference(ident) != Some(true) { + if !ctx.is_global_expr("String", object) { return None; } let mut s = String::with_capacity(args.len()); @@ -350,15 +349,6 @@ fn format_radix(mut x: u32, radix: u32) -> String { result.into_iter().rev().collect() } -fn validate_global_reference<'a>( - expr: &Expression<'a>, - target: &str, - ctx: &impl ConstantEvaluationCtx<'a>, -) -> bool { - let Expression::Identifier(ident) = expr else { return false }; - ctx.is_global_reference(ident) == Some(true) && ident.name == target -} - fn validate_arguments(args: &Vec<'_, Argument<'_>>, expected_len: usize) -> bool { (args.len() == expected_len) && args.iter().all(Argument::is_expression) } @@ -369,7 +359,7 @@ fn try_fold_number_methods<'a>( name: &str, ctx: &impl ConstantEvaluationCtx<'a>, ) -> Option> { - if !validate_global_reference(object, "Number", ctx) { + if !ctx.is_global_expr("Number", object) { return None; } if args.len() != 1 { @@ -400,7 +390,7 @@ fn try_fold_roots<'a>( object: &Expression<'a>, ctx: &impl ConstantEvaluationCtx<'a>, ) -> Option> { - if !validate_global_reference(object, "Math", ctx) || !validate_arguments(args, 1) { + if !ctx.is_global_expr("Math", object) || !validate_arguments(args, 1) { return None; } let arg_val = args[0].to_expression().get_side_free_number_value(ctx)?; @@ -424,7 +414,7 @@ fn try_fold_math_unary<'a>( object: &Expression<'a>, ctx: &impl ConstantEvaluationCtx<'a>, ) -> Option> { - if !validate_global_reference(object, "Math", ctx) || !validate_arguments(args, 1) { + if !ctx.is_global_expr("Math", object) || !validate_arguments(args, 1) { return None; } let arg_val = args[0].to_expression().get_side_free_number_value(ctx)?; @@ -465,7 +455,7 @@ fn try_fold_math_variadic<'a>( object: &Expression<'a>, ctx: &impl ConstantEvaluationCtx<'a>, ) -> Option> { - if !validate_global_reference(object, "Math", ctx) { + if !ctx.is_global_expr("Math", object) { return None; } let mut numbers = std::vec::Vec::new(); diff --git a/crates/oxc_ecmascript/src/constant_evaluation/mod.rs b/crates/oxc_ecmascript/src/constant_evaluation/mod.rs index 9cc143ce242e7..33797a4229a66 100644 --- a/crates/oxc_ecmascript/src/constant_evaluation/mod.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/mod.rs @@ -122,9 +122,9 @@ impl<'a> ConstantEvaluation<'a> for IdentifierReference<'a> { _target_ty: Option, ) -> Option> { match self.name.as_str() { - "undefined" if ctx.is_global_reference(self)? => Some(ConstantValue::Undefined), - "NaN" if ctx.is_global_reference(self)? => Some(ConstantValue::Number(f64::NAN)), - "Infinity" if ctx.is_global_reference(self)? => { + "undefined" if ctx.is_global_reference(self) => Some(ConstantValue::Undefined), + "NaN" if ctx.is_global_reference(self) => Some(ConstantValue::Number(f64::NAN)), + "Infinity" if ctx.is_global_reference(self) => { Some(ConstantValue::Number(f64::INFINITY)) } _ => self @@ -346,7 +346,7 @@ fn binary_operation_evaluate_value_to<'a>( if let Expression::Identifier(right_ident) = right { let name = right_ident.name.as_str(); if matches!(name, "Object" | "Number" | "Boolean" | "String") - && ctx.is_global_reference(right_ident) == Some(true) + && ctx.is_global_reference(right_ident) { let left_ty = left.value_type(ctx); if left_ty.is_undetermined() { diff --git a/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs b/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs index c8cb58a4db0ea..35c405f9bbb66 100644 --- a/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs +++ b/crates/oxc_ecmascript/src/constant_evaluation/value_type.rs @@ -85,7 +85,7 @@ impl<'a> DetermineValueType<'a> for Expression<'a> { } } Expression::Identifier(ident) => { - if ctx.is_global_reference(ident) == Some(true) { + if ctx.is_global_reference(ident) { match ident.name.as_str() { "undefined" => ValueType::Undefined, "NaN" | "Infinity" => ValueType::Number, @@ -269,12 +269,10 @@ impl<'a> DetermineValueType<'a> for LogicalExpression<'a> { impl<'a> DetermineValueType<'a> for StaticMemberExpression<'a> { fn value_type(&self, ctx: &impl GlobalContext<'a>) -> ValueType { - if matches!(self.property.name.as_str(), "POSITIVE_INFINITY" | "NEGATIVE_INFINITY") { - if let Some(ident) = self.object.get_identifier_reference() { - if ident.name.as_str() == "Number" && ctx.is_global_reference(ident) == Some(true) { - return ValueType::Number; - } - } + if matches!(self.property.name.as_str(), "POSITIVE_INFINITY" | "NEGATIVE_INFINITY") + && ctx.is_global_expr("Number", &self.object) + { + return ValueType::Number; } ValueType::Undetermined } @@ -282,10 +280,8 @@ impl<'a> DetermineValueType<'a> for StaticMemberExpression<'a> { impl<'a> DetermineValueType<'a> for NewExpression<'a> { fn value_type(&self, ctx: &impl GlobalContext<'a>) -> ValueType { - if let Some(ident) = self.callee.get_identifier_reference() { - if ident.name.as_str() == "Date" && ctx.is_global_reference(ident) == Some(true) { - return ValueType::Object; - } + if ctx.is_global_expr("Date", &self.callee) { + return ValueType::Object; } ValueType::Undetermined } diff --git a/crates/oxc_ecmascript/src/global_context.rs b/crates/oxc_ecmascript/src/global_context.rs index 53a51989a081c..eedcdc2d8ddda 100644 --- a/crates/oxc_ecmascript/src/global_context.rs +++ b/crates/oxc_ecmascript/src/global_context.rs @@ -1,4 +1,4 @@ -use oxc_ast::ast::IdentifierReference; +use oxc_ast::ast::{Expression, IdentifierReference}; use oxc_syntax::reference::ReferenceId; use crate::constant_evaluation::ConstantValue; @@ -9,7 +9,13 @@ pub trait GlobalContext<'a>: Sized { /// - None means it is unknown. /// - Some(true) means it is a global reference. /// - Some(false) means it is not a global reference. - fn is_global_reference(&self, reference: &IdentifierReference<'a>) -> Option; + fn is_global_reference(&self, reference: &IdentifierReference<'a>) -> bool; + + fn is_global_expr(&self, name: &str, expr: &Expression<'a>) -> bool { + expr.get_identifier_reference() + .filter(|ident| ident.name == name) + .is_some_and(|ident| self.is_global_reference(ident)) + } fn get_constant_value_for_reference_id( &self, @@ -22,7 +28,7 @@ pub trait GlobalContext<'a>: Sized { pub struct WithoutGlobalReferenceInformation; impl<'a> GlobalContext<'a> for WithoutGlobalReferenceInformation { - fn is_global_reference(&self, _reference: &IdentifierReference<'a>) -> Option { - None + fn is_global_reference(&self, _reference: &IdentifierReference<'a>) -> bool { + false } } diff --git a/crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs b/crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs index 3c13107e1101c..694997b4e983a 100644 --- a/crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs +++ b/crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs @@ -81,7 +81,7 @@ impl<'a> MayHaveSideEffects<'a> for IdentifierReference<'a> { // Reading global variables may have a side effect. // NOTE: It should also return true when the reference might refer to a reference value created by a with statement // NOTE: we ignore TDZ errors - _ => ctx.unknown_global_side_effects() && ctx.is_global_reference(self) != Some(false), + _ => ctx.unknown_global_side_effects() && ctx.is_global_reference(self), } } } @@ -149,7 +149,7 @@ impl<'a> MayHaveSideEffects<'a> for BinaryExpression<'a> { // Any known global non-constructor functions can be allowed here. // But because non-constructor functions are not likely to be used, we ignore them. if is_known_global_constructor(name) - && ctx.is_global_reference(right_ident) == Some(true) + && ctx.is_global_reference(right_ident) && !self.left.value_type(ctx).is_undetermined() { return false; diff --git a/crates/oxc_ecmascript/src/to_boolean.rs b/crates/oxc_ecmascript/src/to_boolean.rs index 21d56a670d54c..f88a070df998e 100644 --- a/crates/oxc_ecmascript/src/to_boolean.rs +++ b/crates/oxc_ecmascript/src/to_boolean.rs @@ -17,8 +17,8 @@ impl<'a> ToBoolean<'a> for Expression<'a> { // 4. Return true. match self { Expression::Identifier(ident) => match ident.name.as_str() { - "NaN" | "undefined" if ctx.is_global_reference(ident) == Some(true) => Some(false), - "Infinity" if ctx.is_global_reference(ident) == Some(true) => Some(true), + "NaN" | "undefined" if ctx.is_global_reference(ident) => Some(false), + "Infinity" if ctx.is_global_reference(ident) => Some(true), _ => None, }, Expression::RegExpLiteral(_) diff --git a/crates/oxc_ecmascript/src/to_number.rs b/crates/oxc_ecmascript/src/to_number.rs index 09d6f6737c837..1e70078d339c6 100644 --- a/crates/oxc_ecmascript/src/to_number.rs +++ b/crates/oxc_ecmascript/src/to_number.rs @@ -24,10 +24,8 @@ impl<'a> ToNumber<'a> for Expression<'a> { } Expression::NullLiteral(_) => Some(0.0), Expression::Identifier(ident) => match ident.name.as_str() { - "Infinity" if ctx.is_global_reference(ident) == Some(true) => Some(f64::INFINITY), - "NaN" | "undefined" if ctx.is_global_reference(ident) == Some(true) => { - Some(f64::NAN) - } + "Infinity" if ctx.is_global_reference(ident) => Some(f64::INFINITY), + "NaN" | "undefined" if ctx.is_global_reference(ident) => Some(f64::NAN), _ => None, }, Expression::StringLiteral(lit) => { diff --git a/crates/oxc_ecmascript/src/to_string.rs b/crates/oxc_ecmascript/src/to_string.rs index 1a7970d5f519e..5e6ffdef8733e 100644 --- a/crates/oxc_ecmascript/src/to_string.rs +++ b/crates/oxc_ecmascript/src/to_string.rs @@ -78,9 +78,8 @@ impl<'a> ToJsString<'a> for TemplateLiteral<'a> { impl<'a> ToJsString<'a> for IdentifierReference<'a> { fn to_js_string(&self, ctx: &impl GlobalContext<'a>) -> Option> { let name = self.name.as_str(); - (matches!(name, "undefined" | "Infinity" | "NaN") - && ctx.is_global_reference(self) == Some(true)) - .then_some(Cow::Borrowed(name)) + (matches!(name, "undefined" | "Infinity" | "NaN") && ctx.is_global_reference(self)) + .then_some(Cow::Borrowed(name)) } } diff --git a/crates/oxc_minifier/src/ctx.rs b/crates/oxc_minifier/src/ctx.rs index a5da4db759da1..86000e86539a0 100644 --- a/crates/oxc_minifier/src/ctx.rs +++ b/crates/oxc_minifier/src/ctx.rs @@ -42,8 +42,8 @@ impl<'a, 'b> DerefMut for Ctx<'a, 'b> { } impl<'a> oxc_ecmascript::GlobalContext<'a> for Ctx<'a, '_> { - fn is_global_reference(&self, ident: &IdentifierReference<'_>) -> Option { - Some(ident.is_global_reference(self.0.scoping())) + fn is_global_reference(&self, ident: &IdentifierReference<'_>) -> bool { + ident.is_global_reference(self.0.scoping()) } fn get_constant_value_for_reference_id( diff --git a/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs b/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs index dda5d91420180..d7b4aa232daef 100644 --- a/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs +++ b/crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs @@ -26,8 +26,8 @@ impl Default for Ctx { } } impl<'a> GlobalContext<'a> for Ctx { - fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> Option { - Some(self.global_variable_names.iter().any(|name| name == ident.name.as_str())) + fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> bool { + self.global_variable_names.iter().any(|name| name == ident.name.as_str()) } } impl MayHaveSideEffectsContext<'_> for Ctx { diff --git a/crates/oxc_minifier/tests/ecmascript/mod.rs b/crates/oxc_minifier/tests/ecmascript/mod.rs index 8e0f2c6f8875b..369ccc4ee00cd 100644 --- a/crates/oxc_minifier/tests/ecmascript/mod.rs +++ b/crates/oxc_minifier/tests/ecmascript/mod.rs @@ -6,3 +6,16 @@ mod to_boolean; mod to_number; mod to_string; mod value_type; + +use oxc_ast::ast::IdentifierReference; +use oxc_ecmascript::GlobalContext; + +struct GlobalReferenceInformation { + is_undefined_shadowed: bool, +} + +impl<'a> GlobalContext<'a> for GlobalReferenceInformation { + fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> bool { + if ident.name == "undefined" { !self.is_undefined_shadowed } else { false } + } +} diff --git a/crates/oxc_minifier/tests/ecmascript/to_boolean.rs b/crates/oxc_minifier/tests/ecmascript/to_boolean.rs index 0f30f30225458..f4a21beb716ac 100644 --- a/crates/oxc_minifier/tests/ecmascript/to_boolean.rs +++ b/crates/oxc_minifier/tests/ecmascript/to_boolean.rs @@ -1,17 +1,9 @@ use oxc_allocator::Allocator; -use oxc_ast::{AstBuilder, ast::*}; -use oxc_ecmascript::{GlobalContext, ToBoolean}; +use oxc_ast::AstBuilder; +use oxc_ecmascript::ToBoolean; use oxc_span::SPAN; -struct GlobalReferenceInformation { - is_undefined_shadowed: bool, -} - -impl<'a> GlobalContext<'a> for GlobalReferenceInformation { - fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> Option { - if ident.name == "undefined" { Some(!self.is_undefined_shadowed) } else { None } - } -} +use super::GlobalReferenceInformation; #[test] fn test() { diff --git a/crates/oxc_minifier/tests/ecmascript/to_number.rs b/crates/oxc_minifier/tests/ecmascript/to_number.rs index 7c60ac331affb..35c7d60b60c64 100644 --- a/crates/oxc_minifier/tests/ecmascript/to_number.rs +++ b/crates/oxc_minifier/tests/ecmascript/to_number.rs @@ -1,17 +1,9 @@ use oxc_allocator::Allocator; use oxc_ast::{AstBuilder, ast::*}; -use oxc_ecmascript::{GlobalContext, ToNumber, WithoutGlobalReferenceInformation}; +use oxc_ecmascript::{ToNumber, WithoutGlobalReferenceInformation}; use oxc_span::SPAN; -struct GlobalReferenceInformation { - is_undefined_shadowed: bool, -} - -impl<'a> GlobalContext<'a> for GlobalReferenceInformation { - fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> Option { - if ident.name == "undefined" { Some(!self.is_undefined_shadowed) } else { None } - } -} +use super::GlobalReferenceInformation; #[test] fn test() { diff --git a/crates/oxc_minifier/tests/ecmascript/to_string.rs b/crates/oxc_minifier/tests/ecmascript/to_string.rs index c5e5f345effc3..de1e88e5355e3 100644 --- a/crates/oxc_minifier/tests/ecmascript/to_string.rs +++ b/crates/oxc_minifier/tests/ecmascript/to_string.rs @@ -1,17 +1,9 @@ use oxc_allocator::Allocator; use oxc_ast::{AstBuilder, ast::*}; -use oxc_ecmascript::{GlobalContext, ToJsString, WithoutGlobalReferenceInformation}; +use oxc_ecmascript::{ToJsString, WithoutGlobalReferenceInformation}; use oxc_span::SPAN; -struct GlobalReferenceInformation { - is_undefined_shadowed: bool, -} - -impl<'a> GlobalContext<'a> for GlobalReferenceInformation { - fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> Option { - if ident.name == "undefined" { Some(!self.is_undefined_shadowed) } else { None } - } -} +use super::GlobalReferenceInformation; #[test] fn test() { diff --git a/crates/oxc_minifier/tests/ecmascript/value_type.rs b/crates/oxc_minifier/tests/ecmascript/value_type.rs index 5ba813ff93529..db604fc49c44f 100644 --- a/crates/oxc_minifier/tests/ecmascript/value_type.rs +++ b/crates/oxc_minifier/tests/ecmascript/value_type.rs @@ -12,8 +12,8 @@ struct GlobalReferenceChecker { } impl<'a> GlobalContext<'a> for GlobalReferenceChecker { - fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> Option { - Some(self.global_variable_names.iter().any(|name| name == ident.name.as_str())) + fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> bool { + self.global_variable_names.iter().any(|name| name == ident.name.as_str()) } }