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
20 changes: 5 additions & 15 deletions crates/oxc_ecmascript/src/constant_evaluation/call_expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,7 @@ fn try_fold_string_from_char_code<'a>(
object: &Expression<'a>,
ctx: &impl ConstantEvaluationCtx<'a>,
) -> Option<ConstantValue<'a>> {
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());
Expand Down Expand Up @@ -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)
}
Expand All @@ -369,7 +359,7 @@ fn try_fold_number_methods<'a>(
name: &str,
ctx: &impl ConstantEvaluationCtx<'a>,
) -> Option<ConstantValue<'a>> {
if !validate_global_reference(object, "Number", ctx) {
if !ctx.is_global_expr("Number", object) {
return None;
}
if args.len() != 1 {
Expand Down Expand Up @@ -400,7 +390,7 @@ fn try_fold_roots<'a>(
object: &Expression<'a>,
ctx: &impl ConstantEvaluationCtx<'a>,
) -> Option<ConstantValue<'a>> {
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)?;
Expand All @@ -424,7 +414,7 @@ fn try_fold_math_unary<'a>(
object: &Expression<'a>,
ctx: &impl ConstantEvaluationCtx<'a>,
) -> Option<ConstantValue<'a>> {
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)?;
Expand Down Expand Up @@ -465,7 +455,7 @@ fn try_fold_math_variadic<'a>(
object: &Expression<'a>,
ctx: &impl ConstantEvaluationCtx<'a>,
) -> Option<ConstantValue<'a>> {
if !validate_global_reference(object, "Math", ctx) {
if !ctx.is_global_expr("Math", object) {
return None;
}
let mut numbers = std::vec::Vec::new();
Expand Down
8 changes: 4 additions & 4 deletions crates/oxc_ecmascript/src/constant_evaluation/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,9 @@ impl<'a> ConstantEvaluation<'a> for IdentifierReference<'a> {
_target_ty: Option<ValueType>,
) -> Option<ConstantValue<'a>> {
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
Expand Down Expand Up @@ -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() {
Expand Down
18 changes: 7 additions & 11 deletions crates/oxc_ecmascript/src/constant_evaluation/value_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -269,23 +269,19 @@ 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
}
}

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
}
Expand Down
14 changes: 10 additions & 4 deletions crates/oxc_ecmascript/src/global_context.rs
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<bool>;
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,
Expand All @@ -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<bool> {
None
fn is_global_reference(&self, _reference: &IdentifierReference<'a>) -> bool {
false
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
}
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_ecmascript/src/to_boolean.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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(_)
Expand Down
6 changes: 2 additions & 4 deletions crates/oxc_ecmascript/src/to_number.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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) => {
Expand Down
5 changes: 2 additions & 3 deletions crates/oxc_ecmascript/src/to_string.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<Cow<'a, str>> {
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))
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_minifier/src/ctx.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool> {
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(
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ impl Default for Ctx {
}
}
impl<'a> GlobalContext<'a> for Ctx {
fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> Option<bool> {
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 {
Expand Down
13 changes: 13 additions & 0 deletions crates/oxc_minifier/tests/ecmascript/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
}
}
14 changes: 3 additions & 11 deletions crates/oxc_minifier/tests/ecmascript/to_boolean.rs
Original file line number Diff line number Diff line change
@@ -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<bool> {
if ident.name == "undefined" { Some(!self.is_undefined_shadowed) } else { None }
}
}
use super::GlobalReferenceInformation;

#[test]
fn test() {
Expand Down
12 changes: 2 additions & 10 deletions crates/oxc_minifier/tests/ecmascript/to_number.rs
Original file line number Diff line number Diff line change
@@ -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<bool> {
if ident.name == "undefined" { Some(!self.is_undefined_shadowed) } else { None }
}
}
use super::GlobalReferenceInformation;

#[test]
fn test() {
Expand Down
12 changes: 2 additions & 10 deletions crates/oxc_minifier/tests/ecmascript/to_string.rs
Original file line number Diff line number Diff line change
@@ -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<bool> {
if ident.name == "undefined" { Some(!self.is_undefined_shadowed) } else { None }
}
}
use super::GlobalReferenceInformation;

#[test]
fn test() {
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_minifier/tests/ecmascript/value_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ struct GlobalReferenceChecker {
}

impl<'a> GlobalContext<'a> for GlobalReferenceChecker {
fn is_global_reference(&self, ident: &IdentifierReference<'a>) -> Option<bool> {
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())
}
}

Expand Down
Loading