mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 12:19:15 +00:00
feat(minifier): improve constant evaluation (#8252)
This commit is contained in:
parent
e84f267a39
commit
4d8a08d2ac
6 changed files with 149 additions and 157 deletions
|
|
@ -1,3 +1,4 @@
|
|||
use core::f64;
|
||||
use std::{borrow::Cow, cmp::Ordering};
|
||||
|
||||
use num_bigint::BigInt;
|
||||
|
|
@ -149,6 +150,9 @@ pub trait ConstantEvaluation<'a> {
|
|||
UnaryOperator::Void => Some(f64::NAN),
|
||||
_ => None,
|
||||
},
|
||||
Expression::SequenceExpression(s) => {
|
||||
s.expressions.last().and_then(|e| self.eval_to_number(e))
|
||||
}
|
||||
expr => {
|
||||
use crate::ToNumber;
|
||||
expr.to_number()
|
||||
|
|
@ -247,39 +251,20 @@ pub trait ConstantEvaluation<'a> {
|
|||
};
|
||||
Some(ConstantValue::Number(val))
|
||||
}
|
||||
#[expect(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||
#[expect(clippy::cast_sign_loss)]
|
||||
BinaryOperator::ShiftLeft
|
||||
| BinaryOperator::ShiftRight
|
||||
| BinaryOperator::ShiftRightZeroFill => {
|
||||
let left_num = self.get_side_free_number_value(left);
|
||||
let right_num = self.get_side_free_number_value(right);
|
||||
if let (Some(left_val), Some(right_val)) = (left_num, right_num) {
|
||||
if left_val.fract() != 0.0 || right_val.fract() != 0.0 {
|
||||
return None;
|
||||
}
|
||||
// only the lower 5 bits are used when shifting, so don't do anything
|
||||
// if the shift amount is outside [0,32)
|
||||
if !(0.0..32.0).contains(&right_val) {
|
||||
return None;
|
||||
}
|
||||
let right_val_int = right_val as u32;
|
||||
let bits = left_val.to_int_32();
|
||||
|
||||
let result_val: f64 = match operator {
|
||||
BinaryOperator::ShiftLeft => f64::from(bits.wrapping_shl(right_val_int)),
|
||||
BinaryOperator::ShiftRight => f64::from(bits.wrapping_shr(right_val_int)),
|
||||
BinaryOperator::ShiftRightZeroFill => {
|
||||
// JavaScript always treats the result of >>> as unsigned.
|
||||
// We must force Rust to do the same here.
|
||||
let bits = bits as u32;
|
||||
let res = bits.wrapping_shr(right_val_int);
|
||||
f64::from(res)
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
return Some(ConstantValue::Number(result_val));
|
||||
}
|
||||
None
|
||||
let left = self.get_side_free_number_value(left)?;
|
||||
let right = self.get_side_free_number_value(right)?;
|
||||
let left = left.to_int_32();
|
||||
let right = (right.to_int_32() as u32) & 31;
|
||||
Some(ConstantValue::Number(match operator {
|
||||
BinaryOperator::ShiftLeft => f64::from(left << right),
|
||||
BinaryOperator::ShiftRight => f64::from(left >> right),
|
||||
BinaryOperator::ShiftRightZeroFill => f64::from((left as u32) >> right),
|
||||
_ => unreachable!(),
|
||||
}))
|
||||
}
|
||||
BinaryOperator::LessThan => {
|
||||
self.is_less_than(left, right, true).map(|value| match value {
|
||||
|
|
@ -401,52 +386,36 @@ pub trait ConstantEvaluation<'a> {
|
|||
};
|
||||
Some(ConstantValue::String(Cow::Borrowed(s)))
|
||||
}
|
||||
UnaryOperator::Void => {
|
||||
if (!expr.argument.is_number() || !expr.argument.is_number_0())
|
||||
&& !expr.may_have_side_effects()
|
||||
{
|
||||
return Some(ConstantValue::Undefined);
|
||||
}
|
||||
None
|
||||
}
|
||||
UnaryOperator::Void => (expr.argument.is_literal() || !expr.may_have_side_effects())
|
||||
.then_some(ConstantValue::Undefined),
|
||||
UnaryOperator::LogicalNot => {
|
||||
self.get_boolean_value(&expr.argument).map(|b| !b).map(ConstantValue::Boolean)
|
||||
}
|
||||
UnaryOperator::UnaryPlus => {
|
||||
self.eval_to_number(&expr.argument).map(ConstantValue::Number)
|
||||
}
|
||||
UnaryOperator::UnaryNegation => {
|
||||
let ty = ValueType::from(&expr.argument);
|
||||
match ty {
|
||||
ValueType::BigInt => {
|
||||
self.eval_to_big_int(&expr.argument).map(|v| -v).map(ConstantValue::BigInt)
|
||||
}
|
||||
ValueType::Number => self
|
||||
.eval_to_number(&expr.argument)
|
||||
.map(|v| if v.is_nan() { v } else { -v })
|
||||
.map(ConstantValue::Number),
|
||||
_ => None,
|
||||
UnaryOperator::UnaryNegation => match ValueType::from(&expr.argument) {
|
||||
ValueType::BigInt => {
|
||||
self.eval_to_big_int(&expr.argument).map(|v| -v).map(ConstantValue::BigInt)
|
||||
}
|
||||
}
|
||||
UnaryOperator::BitwiseNot => {
|
||||
let ty = ValueType::from(&expr.argument);
|
||||
match ty {
|
||||
ValueType::BigInt => {
|
||||
self.eval_to_big_int(&expr.argument).map(|v| !v).map(ConstantValue::BigInt)
|
||||
}
|
||||
#[expect(clippy::cast_lossless)]
|
||||
ValueType::Number => self
|
||||
.eval_to_number(&expr.argument)
|
||||
.map(|v| !v.to_int_32())
|
||||
.map(|v| v as f64)
|
||||
.map(ConstantValue::Number),
|
||||
ValueType::Undefined | ValueType::Null => Some(ConstantValue::Number(-1.0)),
|
||||
ValueType::Boolean => self
|
||||
.get_side_free_boolean_value(&expr.argument)
|
||||
.map(|v| ConstantValue::Number(if v { -2.0 } else { -1.0 })),
|
||||
_ => None,
|
||||
ValueType::Number => self
|
||||
.eval_to_number(&expr.argument)
|
||||
.map(|v| if v.is_nan() { v } else { -v })
|
||||
.map(ConstantValue::Number),
|
||||
ValueType::Undefined => Some(ConstantValue::Number(f64::NAN)),
|
||||
ValueType::Null => Some(ConstantValue::Number(-0.0)),
|
||||
_ => None,
|
||||
},
|
||||
UnaryOperator::BitwiseNot => match ValueType::from(&expr.argument) {
|
||||
ValueType::BigInt => {
|
||||
self.eval_to_big_int(&expr.argument).map(|v| !v).map(ConstantValue::BigInt)
|
||||
}
|
||||
}
|
||||
#[expect(clippy::cast_lossless)]
|
||||
_ => self
|
||||
.eval_to_number(&expr.argument)
|
||||
.map(|v| (!v.to_int_32()) as f64)
|
||||
.map(ConstantValue::Number),
|
||||
},
|
||||
UnaryOperator::Delete => None,
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ impl<'a> From<&Expression<'a>> for ValueType {
|
|||
Expression::SequenceExpression(e) => {
|
||||
e.expressions.last().map_or(ValueType::Undetermined, Self::from)
|
||||
}
|
||||
Expression::AssignmentExpression(e) => Self::from(&e.right),
|
||||
_ => Self::Undetermined,
|
||||
}
|
||||
}
|
||||
|
|
@ -115,8 +116,27 @@ impl<'a> From<&BinaryExpression<'a>> for ValueType {
|
|||
}
|
||||
Self::Undetermined
|
||||
}
|
||||
BinaryOperator::Instanceof => Self::Boolean,
|
||||
_ => Self::Undetermined,
|
||||
BinaryOperator::Subtraction
|
||||
| BinaryOperator::Multiplication
|
||||
| BinaryOperator::Division
|
||||
| BinaryOperator::Remainder
|
||||
| BinaryOperator::ShiftLeft
|
||||
| BinaryOperator::BitwiseOR
|
||||
| BinaryOperator::ShiftRight
|
||||
| BinaryOperator::BitwiseXOR
|
||||
| BinaryOperator::BitwiseAnd
|
||||
| BinaryOperator::Exponential
|
||||
| BinaryOperator::ShiftRightZeroFill => Self::Number,
|
||||
BinaryOperator::Instanceof
|
||||
| BinaryOperator::In
|
||||
| BinaryOperator::Equality
|
||||
| BinaryOperator::Inequality
|
||||
| BinaryOperator::StrictEquality
|
||||
| BinaryOperator::StrictInequality
|
||||
| BinaryOperator::LessThan
|
||||
| BinaryOperator::LessEqualThan
|
||||
| BinaryOperator::GreaterThan
|
||||
| BinaryOperator::GreaterEqualThan => Self::Boolean,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ impl<'a, 'b> PeepholeFoldConstants {
|
|||
Self { changed: false }
|
||||
}
|
||||
|
||||
#[allow(clippy::float_cmp)]
|
||||
#[expect(clippy::float_cmp)]
|
||||
fn try_fold_unary_expr(e: &UnaryExpression<'a>, ctx: Ctx<'a, 'b>) -> Option<Expression<'a>> {
|
||||
match e.operator {
|
||||
// Do not fold `void 0` back to `undefined`.
|
||||
|
|
@ -217,7 +217,7 @@ impl<'a, 'b> PeepholeFoldConstants {
|
|||
None
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
fn try_fold_binary_expr(
|
||||
e: &mut BinaryExpression<'a>,
|
||||
ctx: Ctx<'a, 'b>,
|
||||
|
|
@ -300,7 +300,7 @@ impl<'a, 'b> PeepholeFoldConstants {
|
|||
}
|
||||
|
||||
// https://github.com/evanw/esbuild/blob/v0.24.2/internal/js_ast/js_ast_helpers.go#L1128
|
||||
#[allow(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss)]
|
||||
#[must_use]
|
||||
fn approximate_printed_int_char_count(value: f64) -> usize {
|
||||
let mut count = if value.is_infinite() {
|
||||
|
|
@ -505,48 +505,37 @@ impl<'a, 'b> PeepholeFoldConstants {
|
|||
) -> Option<bool> {
|
||||
let left = ValueType::from(left_expr);
|
||||
let right = ValueType::from(right_expr);
|
||||
if left != ValueType::Undetermined && right != ValueType::Undetermined {
|
||||
if !left.is_undetermined() && !right.is_undetermined() {
|
||||
// Strict equality can only be true for values of the same type.
|
||||
if left != right {
|
||||
return Some(false);
|
||||
}
|
||||
return match left {
|
||||
ValueType::Number => {
|
||||
let left_number = ctx.get_side_free_number_value(left_expr);
|
||||
let right_number = ctx.get_side_free_number_value(right_expr);
|
||||
|
||||
if let (Some(l_num), Some(r_num)) = (left_number, right_number) {
|
||||
if l_num.is_nan() || r_num.is_nan() {
|
||||
return Some(false);
|
||||
}
|
||||
|
||||
return Some(l_num == r_num);
|
||||
let lnum = ctx.get_side_free_number_value(left_expr)?;
|
||||
let rnum = ctx.get_side_free_number_value(right_expr)?;
|
||||
if lnum.is_nan() || rnum.is_nan() {
|
||||
return Some(false);
|
||||
}
|
||||
|
||||
None
|
||||
Some(lnum == rnum)
|
||||
}
|
||||
ValueType::String => {
|
||||
let left_string = ctx.get_side_free_string_value(left_expr);
|
||||
let right_string = ctx.get_side_free_string_value(right_expr);
|
||||
if let (Some(left_string), Some(right_string)) = (left_string, right_string) {
|
||||
return Some(left_string == right_string);
|
||||
}
|
||||
None
|
||||
let left = ctx.get_side_free_string_value(left_expr)?;
|
||||
let right = ctx.get_side_free_string_value(right_expr)?;
|
||||
Some(left == right)
|
||||
}
|
||||
ValueType::Undefined | ValueType::Null => Some(true),
|
||||
ValueType::Boolean if right.is_boolean() => {
|
||||
let left = ctx.get_boolean_value(left_expr);
|
||||
let right = ctx.get_boolean_value(right_expr);
|
||||
if let (Some(left_bool), Some(right_bool)) = (left, right) {
|
||||
return Some(left_bool == right_bool);
|
||||
}
|
||||
None
|
||||
let left = ctx.get_boolean_value(left_expr)?;
|
||||
let right = ctx.get_boolean_value(right_expr)?;
|
||||
Some(left == right)
|
||||
}
|
||||
// TODO
|
||||
ValueType::BigInt
|
||||
| ValueType::Object
|
||||
| ValueType::Boolean
|
||||
| ValueType::Undetermined => None,
|
||||
ValueType::BigInt => {
|
||||
let left = ctx.get_side_free_bigint_value(left_expr)?;
|
||||
let right = ctx.get_side_free_bigint_value(right_expr)?;
|
||||
Some(left == right)
|
||||
}
|
||||
ValueType::Object | ValueType::Boolean | ValueType::Undetermined => None,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -648,6 +637,11 @@ mod test {
|
|||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_comparison() {
|
||||
test("(1, 2) !== 2", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined_comparison1() {
|
||||
test("undefined == undefined", "true");
|
||||
|
|
@ -1103,31 +1097,28 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn unary_ops() {
|
||||
// TODO: need to port
|
||||
// These cases are handled by PeepholeRemoveDeadCode in closure-compiler.
|
||||
// test_same("!foo()");
|
||||
// test_same("~foo()");
|
||||
// test_same("-foo()");
|
||||
fn test_fold_unary() {
|
||||
test_same("!foo()");
|
||||
test_same("~foo()");
|
||||
test_same("-foo()");
|
||||
|
||||
// These cases are handled here.
|
||||
test("a=!true", "a=false");
|
||||
test("a=!10", "a=false");
|
||||
test("a=!false", "a=true");
|
||||
test_same("a=!foo()");
|
||||
// test("a=-0", "a=-0.0");
|
||||
// test("a=-(0)", "a=-0.0");
|
||||
|
||||
test("a=-0", "a=-0");
|
||||
test("a=-(0)", "a=-0");
|
||||
test_same("a=-Infinity");
|
||||
test("a=-NaN", "a=NaN");
|
||||
test_same("a=-foo()");
|
||||
test("a=~~0", "a=0");
|
||||
test("a=~~10", "a=10");
|
||||
test("a=~-7", "a=6");
|
||||
test_same("a=~~foo()");
|
||||
test("-undefined", "NaN");
|
||||
test("-null", "-0");
|
||||
test("-NaN", "NaN");
|
||||
|
||||
// test("a=+true", "a=1");
|
||||
test("a=+true", "a=1");
|
||||
test("a=+10", "a=10");
|
||||
// test("a=+false", "a=0");
|
||||
test("a=+false", "a=0");
|
||||
test_same("a=+foo()");
|
||||
test_same("a=+f");
|
||||
// test("a=+(f?true:false)", "a=+(f?1:0)");
|
||||
|
|
@ -1135,15 +1126,19 @@ mod test {
|
|||
test("a=+Infinity", "a=Infinity");
|
||||
test("a=+NaN", "a=NaN");
|
||||
test("a=+-7", "a=-7");
|
||||
// test("a=+.5", "a=.5");
|
||||
test("a=+.5", "a=.5");
|
||||
|
||||
test("a=~~0", "a=0");
|
||||
test("a=~~10", "a=10");
|
||||
test("a=~-7", "a=6");
|
||||
test_same("a=~~foo()");
|
||||
test("a=~0xffffffff", "a=0");
|
||||
test("a=~~0xffffffff", "a=-1");
|
||||
// test_same("a=~.5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND);
|
||||
// test_same("a=~.5");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn unary_with_big_int() {
|
||||
fn test_fold_unary_big_int() {
|
||||
test("-(1n)", "-1n");
|
||||
test("- -1n", "1n");
|
||||
test("!1n", "false");
|
||||
|
|
@ -1453,6 +1448,8 @@ mod test {
|
|||
test("~null", "-1");
|
||||
test("~false", "-1");
|
||||
test("~true", "-2");
|
||||
test("~'1'", "-2");
|
||||
test("~'-1'", "0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
@ -1485,9 +1482,9 @@ mod test {
|
|||
|
||||
test("x = 0xffffffff << 0", "x=-1");
|
||||
test("x = 0xffffffff << 4", "x=-16");
|
||||
test("1 << 32", "1<<32");
|
||||
test("1 << 32", "1");
|
||||
test("1 << -1", "1<<-1");
|
||||
test("1 >> 32", "1>>32");
|
||||
test("1 >> 32", "1");
|
||||
|
||||
// Regression on #6161, ported from <https://github.com/tc39/test262/blob/05c45a4c430ab6fee3e0c7f0d47d8a30d8876a6d/test/language/expressions/unsigned-right-shift/S9.6_A2.2.js>.
|
||||
test("-2147483647 >>> 0", "2147483649");
|
||||
|
|
@ -1535,6 +1532,8 @@ mod test {
|
|||
// test("x = (p1 + (p2 + 'a')) + 'b'", "x = (p1 + (p2 + 'ab'))");
|
||||
// test("'a' + ('b' + p1) + 1", "'ab' + p1 + 1");
|
||||
// test("x = 'a' + ('b' + p1 + 'c')", "x = 'ab' + (p1 + 'c')");
|
||||
test("void 0 + ''", "'undefined'");
|
||||
|
||||
test_same("x = 'a' + (4 + p1 + 'a')");
|
||||
test_same("x = p1 / 3 + 4");
|
||||
test_same("foo() + 3 + 'a' + foo()");
|
||||
|
|
@ -1618,15 +1617,21 @@ mod test {
|
|||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_shift_right_zero_fill() {
|
||||
test("10 >>> 1", "5");
|
||||
test_same("-1 >>> 0");
|
||||
fn test_fold_shift_left() {
|
||||
test("1 << 3", "8");
|
||||
test("1.2345 << 0", "1");
|
||||
test_same("1 << 24");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_shift_left() {
|
||||
test("1 << 3", "8");
|
||||
test_same("1 << 24");
|
||||
fn test_fold_shift_right() {
|
||||
test("2147483647 >> -32.1", "2147483647");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_shift_right_zero_fill() {
|
||||
test("10 >>> 1", "5");
|
||||
test_same("-1 >>> 0");
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
|
|
|||
|
|
@ -506,6 +506,9 @@ impl<'a> PeepholeMinimizeConditions {
|
|||
true
|
||||
}
|
||||
|
||||
// `a instanceof b === true` -> `a instanceof b`
|
||||
// `a instanceof b === false` -> `!(a instanceof b)`
|
||||
// ^^^^^^^^^^^^^^ `ValueType::from(&e.left).is_boolean()` is `true`.
|
||||
fn try_minimize_binary(
|
||||
e: &mut BinaryExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
|
|
|
|||
|
|
@ -176,10 +176,11 @@ impl<'a> PeepholeReplaceKnownMethods {
|
|||
string_lit: &StringLiteral<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let char_at_index = call_expr.arguments.first().and_then(|arg| match arg {
|
||||
Argument::SpreadElement(_) => None,
|
||||
_ => Ctx(ctx).get_side_free_number_value(arg.to_expression()),
|
||||
})?;
|
||||
let char_at_index = match call_expr.arguments.first() {
|
||||
None => Some(0.0),
|
||||
Some(Argument::SpreadElement(_)) => None,
|
||||
Some(e) => Ctx(ctx).get_side_free_number_value(e.to_expression()),
|
||||
}?;
|
||||
let span = call_expr.span;
|
||||
// TODO: if `result` is `None`, return `NaN` instead of skipping the optimization
|
||||
let result = string_lit.value.as_str().char_code_at(Some(char_at_index))?;
|
||||
|
|
@ -226,26 +227,19 @@ impl<'a> PeepholeReplaceKnownMethods {
|
|||
Some(ctx.ast.expression_string_literal(span, result, None))
|
||||
}
|
||||
|
||||
#[expect(clippy::cast_possible_truncation, clippy::cast_sign_loss, clippy::cast_lossless)]
|
||||
fn try_fold_string_from_char_code(
|
||||
ce: &CallExpression<'a>,
|
||||
ctx: &mut TraverseCtx<'a>,
|
||||
) -> Option<Expression<'a>> {
|
||||
let ctx = Ctx(ctx);
|
||||
let args = &ce.arguments;
|
||||
if args.iter().any(|arg| !matches!(arg, Argument::NumericLiteral(_))) {
|
||||
return None;
|
||||
}
|
||||
let mut s = String::with_capacity(args.len());
|
||||
for arg in args {
|
||||
let Argument::NumericLiteral(lit) = arg else { unreachable!() };
|
||||
if lit.value.is_nan() || lit.value.is_infinite() {
|
||||
return None;
|
||||
}
|
||||
let v = lit.value.to_int_32();
|
||||
if v >= 65535 {
|
||||
return None;
|
||||
}
|
||||
let Ok(v) = u32::try_from(v) else { return None };
|
||||
let Ok(c) = char::try_from(v) else { return None };
|
||||
let expr = arg.as_expression()?;
|
||||
let v = ctx.get_side_free_number_value(expr)?;
|
||||
let v = v.to_int_32() as u16 as u32;
|
||||
let c = char::try_from(v).ok()?;
|
||||
s.push(c);
|
||||
}
|
||||
Some(ctx.ast.expression_string_literal(ce.span, s, None))
|
||||
|
|
@ -508,6 +502,7 @@ mod test {
|
|||
|
||||
#[test]
|
||||
fn test_fold_string_char_code_at() {
|
||||
fold("x = 'abcde'.charCodeAt()", "x = 97");
|
||||
fold("x = 'abcde'.charCodeAt(0)", "x = 97");
|
||||
fold("x = 'abcde'.charCodeAt(1)", "x = 98");
|
||||
fold("x = 'abcde'.charCodeAt(2)", "x = 99");
|
||||
|
|
@ -1099,17 +1094,17 @@ mod test {
|
|||
test("String.fromCharCode(120)", "'x'");
|
||||
test("String.fromCharCode(120, 121)", "'xy'");
|
||||
test_same("String.fromCharCode(55358, 56768)");
|
||||
test("String.fromCharCode(0x10000)", "String.fromCharCode(65536)");
|
||||
test("String.fromCharCode(0x10078, 0x10079)", "String.fromCharCode(0x10078, 0x10079)");
|
||||
test("String.fromCharCode(0x1_0000_FFFF)", "String.fromCharCode(4295032831)");
|
||||
test_same("String.fromCharCode(NaN)");
|
||||
test_same("String.fromCharCode(-Infinity)");
|
||||
test_same("String.fromCharCode(Infinity)");
|
||||
test_same("String.fromCharCode(null)");
|
||||
test_same("String.fromCharCode(undefined)");
|
||||
test_same("String.fromCharCode('123')");
|
||||
test("String.fromCharCode(0x10000)", "'\\0'");
|
||||
test("String.fromCharCode(0x10078, 0x10079)", "'xy'");
|
||||
test("String.fromCharCode(0x1_0000_FFFF)", "'\u{ffff}'");
|
||||
test("String.fromCharCode(NaN)", "'\\0'");
|
||||
test("String.fromCharCode(-Infinity)", "'\\0'");
|
||||
test("String.fromCharCode(Infinity)", "'\\0'");
|
||||
test("String.fromCharCode(null)", "'\\0'");
|
||||
test("String.fromCharCode(undefined)", "'\\0'");
|
||||
test("String.fromCharCode('123')", "'{'");
|
||||
test_same("String.fromCharCode(x)");
|
||||
test_same("String.fromCharCode('x')");
|
||||
test_same("String.fromCharCode('0.5')");
|
||||
test("String.fromCharCode('x')", "'\\0'");
|
||||
test("String.fromCharCode('0.5')", "'\\0'");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ Original | minified | minified | gzip | gzip | Fixture
|
|||
|
||||
1.01 MB | 460.34 kB | 458.89 kB | 126.86 kB | 126.71 kB | bundle.min.js
|
||||
|
||||
1.25 MB | 652.70 kB | 646.76 kB | 163.53 kB | 163.73 kB | three.js
|
||||
1.25 MB | 652.70 kB | 646.76 kB | 163.54 kB | 163.73 kB | three.js
|
||||
|
||||
2.14 MB | 726.21 kB | 724.14 kB | 180.20 kB | 181.07 kB | victory.js
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue