Skip to content

Commit c62ed5e

Browse files
sapphi-redBoshen
authored andcommitted
fix(ecmascript): to_number for shadowed undefined
1 parent 404e40c commit c62ed5e

File tree

6 files changed

+53
-11
lines changed

6 files changed

+53
-11
lines changed

crates/oxc_ecmascript/src/constant_evaluation/mod.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
188188
}
189189
expr => {
190190
use crate::ToNumber;
191-
expr.to_number()
191+
expr.to_number(self)
192192
}
193193
}
194194
}
@@ -256,8 +256,8 @@ pub trait ConstantEvaluation<'a>: DetermineValueType {
256256
if left_type.is_number() || right_type.is_number() {
257257
let lval = self.eval_expression(left)?;
258258
let rval = self.eval_expression(right)?;
259-
let lnum = lval.to_number()?;
260-
let rnum = rval.to_number()?;
259+
let lnum = lval.to_number(self)?;
260+
let rnum = rval.to_number(self)?;
261261
return Some(ConstantValue::Number(lnum + rnum));
262262
}
263263
None

crates/oxc_ecmascript/src/constant_evaluation/value.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ impl<'a> ToJsString<'a> for ConstantValue<'a> {
7676
}
7777

7878
impl<'a> ToNumber<'a> for ConstantValue<'a> {
79-
fn to_number(&self) -> Option<f64> {
79+
fn to_number(&self, _is_global_reference: &impl IsGlobalReference) -> Option<f64> {
8080
use crate::StringToNumber;
8181
match self {
8282
Self::Number(n) => Some(*n),

crates/oxc_ecmascript/src/to_number.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,16 @@
11
use oxc_ast::ast::*;
22

3+
use crate::is_global_reference::IsGlobalReference;
4+
35
/// `ToNumber`
46
///
5-
/// <https://tc39.es/ecma262/#sec-tonumber>
7+
/// <https://tc39.es/ecma262/multipage/abstract-operations.html#sec-tonumber>
68
pub trait ToNumber<'a> {
7-
fn to_number(&self) -> Option<f64>;
9+
fn to_number(&self, is_global_reference: &impl IsGlobalReference) -> Option<f64>;
810
}
911

1012
impl<'a> ToNumber<'a> for Expression<'a> {
11-
fn to_number(&self) -> Option<f64> {
13+
fn to_number(&self, is_global_reference: &impl IsGlobalReference) -> Option<f64> {
1214
match self {
1315
Expression::NumericLiteral(number_literal) => Some(number_literal.value),
1416
Expression::BooleanLiteral(bool_literal) => {
@@ -20,16 +22,22 @@ impl<'a> ToNumber<'a> for Expression<'a> {
2022
}
2123
Expression::NullLiteral(_) => Some(0.0),
2224
Expression::Identifier(ident) => match ident.name.as_str() {
23-
"Infinity" => Some(f64::INFINITY),
24-
"NaN" | "undefined" => Some(f64::NAN),
25+
"Infinity" if is_global_reference.is_global_reference(ident) == Some(true) => {
26+
Some(f64::INFINITY)
27+
}
28+
"NaN" | "undefined"
29+
if is_global_reference.is_global_reference(ident) == Some(true) =>
30+
{
31+
Some(f64::NAN)
32+
}
2533
_ => None,
2634
},
2735
Expression::StringLiteral(lit) => {
2836
use crate::StringToNumber;
2937
Some(lit.value.as_str().string_to_number())
3038
}
3139
Expression::UnaryExpression(unary) if unary.operator.is_not() => {
32-
let number = unary.argument.to_number()?;
40+
let number = unary.argument.to_number(is_global_reference)?;
3341
Some(if number == 0.0 { 1.0 } else { 0.0 })
3442
}
3543
_ => None,

crates/oxc_minifier/src/peephole/substitute_alternate_syntax.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -635,7 +635,7 @@ impl<'a> PeepholeOptimizations {
635635
span,
636636
match arg {
637637
None => 0.0,
638-
Some(arg) => arg.to_number()?,
638+
Some(arg) => arg.to_number(&ctx)?,
639639
},
640640
None,
641641
NumberBase::Decimal,

crates/oxc_minifier/tests/ecmascript/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,6 @@ mod array_join;
22
mod may_have_side_effects;
33
mod prop_name;
44
mod to_boolean;
5+
mod to_number;
56
mod to_string;
67
mod value_type;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
use oxc_allocator::Allocator;
2+
use oxc_ast::{ast::*, AstBuilder};
3+
use oxc_ecmascript::{is_global_reference::IsGlobalReference, ToNumber};
4+
use oxc_span::SPAN;
5+
6+
struct GlobalReferenceInformation {
7+
is_undefined_shadowed: bool,
8+
}
9+
10+
impl IsGlobalReference for GlobalReferenceInformation {
11+
fn is_global_reference(&self, ident: &IdentifierReference<'_>) -> Option<bool> {
12+
if ident.name == "undefined" {
13+
Some(!self.is_undefined_shadowed)
14+
} else {
15+
None
16+
}
17+
}
18+
}
19+
20+
#[test]
21+
fn test() {
22+
let allocator = Allocator::default();
23+
let ast = AstBuilder::new(&allocator);
24+
25+
let undefined = ast.expression_identifier(SPAN, "undefined");
26+
let shadowed_undefined_number =
27+
undefined.to_number(&GlobalReferenceInformation { is_undefined_shadowed: true });
28+
let global_undefined_number =
29+
undefined.to_number(&GlobalReferenceInformation { is_undefined_shadowed: false });
30+
31+
assert_eq!(shadowed_undefined_number, None);
32+
assert!(global_undefined_number.is_some_and(f64::is_nan));
33+
}

0 commit comments

Comments
 (0)