mirror of
https://github.com/oxc-project/oxc.git
synced 2026-11-04 10:29:58 +00:00
refactor(minifier): move tests to their src files (#5912)
This commit is contained in:
1 parent
cbaeea6d02
commit
943bd76679
20 files changed
+1969
-1982
No files matched your search
@@ -17,7 +17,7 @@ description.workspace = true
|
||||
workspace = true
|
||||
|
||||
[lib]
|
||||
test = false
|
||||
test = true
|
||||
doctest = false
|
||||
|
||||
[dependencies]
|
||||
|
||||
@@ -86,3 +86,34 @@ impl<'a> CollapseVariableDeclarations {
|
||||
*stmts = new_stmts;
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/CollapseVariableDeclarations.java>
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_allocator::Allocator;
|
||||
|
||||
use crate::{tester, CompressOptions};
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let allocator = Allocator::default();
|
||||
let mut pass = super::CollapseVariableDeclarations::new(CompressOptions::default());
|
||||
tester::test(&allocator, source_text, expected, &mut pass);
|
||||
}
|
||||
|
||||
fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cjs() {
|
||||
// Do not join `require` calls for cjs-module-lexer.
|
||||
test_same(
|
||||
"
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
var compilerDom = require('@vue/compiler-dom');
|
||||
var runtimeDom = require('@vue/runtime-dom');
|
||||
var shared = require('@vue/shared');
|
||||
",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -28,7 +28,7 @@ impl<'a> NodeUtil for TraverseCtx<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
pub trait CompressorPass<'a> {
|
||||
pub trait CompressorPass<'a>: Traverse<'a> {
|
||||
fn build(&mut self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>)
|
||||
where
|
||||
Self: Traverse<'a>,
|
||||
|
||||
@@ -696,3 +696,686 @@ impl<'a> PeepholeFoldConstants {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeFoldConstants.java>
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_allocator::Allocator;
|
||||
|
||||
use crate::tester;
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let allocator = Allocator::default();
|
||||
let mut pass = super::PeepholeFoldConstants::new();
|
||||
tester::test(&allocator, source_text, expected, &mut pass);
|
||||
}
|
||||
|
||||
fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined_comparison1() {
|
||||
test("undefined == undefined", "true");
|
||||
test("undefined == null", "true");
|
||||
test("undefined == void 0", "true");
|
||||
|
||||
test("undefined == 0", "false");
|
||||
test("undefined == 1", "false");
|
||||
test("undefined == 'hi'", "false");
|
||||
test("undefined == true", "false");
|
||||
test("undefined == false", "false");
|
||||
|
||||
test("undefined === undefined", "true");
|
||||
test("undefined === null", "false");
|
||||
test("undefined === void 0", "true");
|
||||
|
||||
test_same("undefined == this");
|
||||
test_same("undefined == x");
|
||||
|
||||
test("undefined != undefined", "false");
|
||||
test("undefined != null", "false");
|
||||
test("undefined != void 0", "false");
|
||||
|
||||
test("undefined != 0", "true");
|
||||
test("undefined != 1", "true");
|
||||
test("undefined != 'hi'", "true");
|
||||
test("undefined != true", "true");
|
||||
test("undefined != false", "true");
|
||||
|
||||
test("undefined !== undefined", "false");
|
||||
test("undefined !== void 0", "false");
|
||||
test("undefined !== null", "true");
|
||||
|
||||
test_same("undefined != this");
|
||||
test_same("undefined != x");
|
||||
|
||||
test("undefined < undefined", "false");
|
||||
test("undefined > undefined", "false");
|
||||
test("undefined >= undefined", "false");
|
||||
test("undefined <= undefined", "false");
|
||||
|
||||
test("0 < undefined", "false");
|
||||
test("true > undefined", "false");
|
||||
test("'hi' >= undefined", "false");
|
||||
test("null <= undefined", "false");
|
||||
|
||||
test("undefined < 0", "false");
|
||||
test("undefined > true", "false");
|
||||
test("undefined >= 'hi'", "false");
|
||||
test("undefined <= null", "false");
|
||||
|
||||
test("null == undefined", "true");
|
||||
test("0 == undefined", "false");
|
||||
test("1 == undefined", "false");
|
||||
test("'hi' == undefined", "false");
|
||||
test("true == undefined", "false");
|
||||
test("false == undefined", "false");
|
||||
test("null === undefined", "false");
|
||||
test("void 0 === undefined", "true");
|
||||
|
||||
test("undefined == NaN", "false");
|
||||
test("NaN == undefined", "false");
|
||||
test("undefined == Infinity", "false");
|
||||
test("Infinity == undefined", "false");
|
||||
test("undefined == -Infinity", "false");
|
||||
test("-Infinity == undefined", "false");
|
||||
test("({}) == undefined", "false");
|
||||
test("undefined == ({})", "false");
|
||||
test("([]) == undefined", "false");
|
||||
test("undefined == ([])", "false");
|
||||
test("(/a/g) == undefined", "false");
|
||||
test("undefined == (/a/g)", "false");
|
||||
test("(function(){}) == undefined", "false");
|
||||
test("undefined == (function(){})", "false");
|
||||
|
||||
test("undefined != NaN", "true");
|
||||
test("NaN != undefined", "true");
|
||||
test("undefined != Infinity", "true");
|
||||
test("Infinity != undefined", "true");
|
||||
test("undefined != -Infinity", "true");
|
||||
test("-Infinity != undefined", "true");
|
||||
test("({}) != undefined", "true");
|
||||
test("undefined != ({})", "true");
|
||||
test("([]) != undefined", "true");
|
||||
test("undefined != ([])", "true");
|
||||
test("(/a/g) != undefined", "true");
|
||||
test("undefined != (/a/g)", "true");
|
||||
test("(function(){}) != undefined", "true");
|
||||
test("undefined != (function(){})", "true");
|
||||
|
||||
test_same("this == undefined");
|
||||
test_same("x == undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_undefined_comparison2() {
|
||||
test("\"123\" !== void 0", "true");
|
||||
test("\"123\" === void 0", "false");
|
||||
|
||||
test("void 0 !== \"123\"", "true");
|
||||
test("void 0 === \"123\"", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_undefined_comparison3() {
|
||||
test("\"123\" !== undefined", "true");
|
||||
test("\"123\" === undefined", "false");
|
||||
|
||||
test("undefined !== \"123\"", "true");
|
||||
test("undefined === \"123\"", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_comparison1() {
|
||||
test("null == undefined", "true");
|
||||
test("null == null", "true");
|
||||
test("null == void 0", "true");
|
||||
|
||||
test("null == 0", "false");
|
||||
test("null == 1", "false");
|
||||
// test("null == 0n", "false");
|
||||
// test("null == 1n", "false");
|
||||
test("null == 'hi'", "false");
|
||||
test("null == true", "false");
|
||||
test("null == false", "false");
|
||||
|
||||
test("null === undefined", "false");
|
||||
test("null === null", "true");
|
||||
test("null === void 0", "false");
|
||||
test_same("null===x");
|
||||
|
||||
test_same("null==this");
|
||||
test_same("null==x");
|
||||
|
||||
test("null != undefined", "false");
|
||||
test("null != null", "false");
|
||||
test("null != void 0", "false");
|
||||
|
||||
test("null != 0", "true");
|
||||
test("null != 1", "true");
|
||||
// test("null != 0n", "true");
|
||||
// test("null != 1n", "true");
|
||||
test("null != 'hi'", "true");
|
||||
test("null != true", "true");
|
||||
test("null != false", "true");
|
||||
|
||||
test("null !== undefined", "true");
|
||||
test("null !== void 0", "true");
|
||||
test("null !== null", "false");
|
||||
|
||||
test_same("null!=this");
|
||||
test_same("null!=x");
|
||||
|
||||
test("null < null", "false");
|
||||
test("null > null", "false");
|
||||
test("null >= null", "true");
|
||||
test("null <= null", "true");
|
||||
|
||||
test("0 < null", "false");
|
||||
test("0 > null", "false");
|
||||
test("0 >= null", "true");
|
||||
// test("0n < null", "false");
|
||||
// test("0n > null", "false");
|
||||
// test("0n >= null", "true");
|
||||
test("true > null", "true");
|
||||
test("'hi' < null", "false");
|
||||
test("'hi' >= null", "false");
|
||||
test("null <= null", "true");
|
||||
|
||||
test("null < 0", "false");
|
||||
// test("null < 0n", "false");
|
||||
test("null > true", "false");
|
||||
test("null < 'hi'", "false");
|
||||
test("null >= 'hi'", "false");
|
||||
test("null <= null", "true");
|
||||
|
||||
test("null == null", "true");
|
||||
test("0 == null", "false");
|
||||
test("1 == null", "false");
|
||||
test("'hi' == null", "false");
|
||||
test("true == null", "false");
|
||||
test("false == null", "false");
|
||||
test("null === null", "true");
|
||||
test("void 0 === null", "false");
|
||||
|
||||
test("null == NaN", "false");
|
||||
test("NaN == null", "false");
|
||||
test("null == Infinity", "false");
|
||||
test("Infinity == null", "false");
|
||||
test("null == -Infinity", "false");
|
||||
test("-Infinity == null", "false");
|
||||
test("({}) == null", "false");
|
||||
test("null == ({})", "false");
|
||||
test("([]) == null", "false");
|
||||
test("null == ([])", "false");
|
||||
test("(/a/g) == null", "false");
|
||||
test("null == (/a/g)", "false");
|
||||
test("(function(){}) == null", "false");
|
||||
test("null == (function(){})", "false");
|
||||
|
||||
test("null != NaN", "true");
|
||||
test("NaN != null", "true");
|
||||
test("null != Infinity", "true");
|
||||
test("Infinity != null", "true");
|
||||
test("null != -Infinity", "true");
|
||||
test("-Infinity != null", "true");
|
||||
test("({}) != null", "true");
|
||||
test("null != ({})", "true");
|
||||
test("([]) != null", "true");
|
||||
test("null != ([])", "true");
|
||||
test("(/a/g) != null", "true");
|
||||
test("null != (/a/g)", "true");
|
||||
test("(function(){}) != null", "true");
|
||||
test("null != (function(){})", "true");
|
||||
|
||||
test_same("({a:f()})==null");
|
||||
test_same("null=={a:f()}");
|
||||
test_same("[f()]==null");
|
||||
test_same("null==[f()]");
|
||||
|
||||
test_same("this==null");
|
||||
test_same("x==null");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boolean_boolean_comparison() {
|
||||
test_same("!x==!y");
|
||||
test_same("!x<!y");
|
||||
test_same("!x!==!y");
|
||||
|
||||
test_same("!x==!x"); // foldable
|
||||
test_same("!x<!x"); // foldable
|
||||
test_same("!x!==!x"); // foldable
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boolean_number_comparison() {
|
||||
test_same("!x==+y");
|
||||
test_same("!x<=+y");
|
||||
test("!x !== +y", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_boolean_comparison() {
|
||||
test_same("+x==!y");
|
||||
test_same("+x<=!y");
|
||||
test("+x === !y", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boolean_string_comparison() {
|
||||
test_same("!x==''+y");
|
||||
test_same("!x<=''+y");
|
||||
test("!x !== '' + y", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_boolean_comparison() {
|
||||
test_same("''+x==!y");
|
||||
test_same("''+x<=!y");
|
||||
test("'' + x === !y", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_string_string_comparison() {
|
||||
test("'a' < 'b'", "true");
|
||||
test("'a' <= 'b'", "true");
|
||||
test("'a' > 'b'", "false");
|
||||
test("'a' >= 'b'", "false");
|
||||
test("+'a' < +'b'", "false");
|
||||
test_same("typeof a < 'a'");
|
||||
test_same("'a' >= typeof a");
|
||||
test("typeof a < typeof a", "false");
|
||||
test("typeof a >= typeof a", "true");
|
||||
test("typeof 3 > typeof 4", "false");
|
||||
test("typeof function() {} < typeof function() {}", "false");
|
||||
test("'a' == 'a'", "true");
|
||||
test("'b' != 'a'", "true");
|
||||
test_same("'undefined' == typeof a");
|
||||
test_same("typeof a != 'number'");
|
||||
test_same("'undefined' == typeof a");
|
||||
test_same("'undefined' == typeof a");
|
||||
test("typeof a == typeof a", "true");
|
||||
test("'a' === 'a'", "true");
|
||||
test("'b' !== 'a'", "true");
|
||||
test("typeof a === typeof a", "true");
|
||||
test("typeof a !== typeof a", "false");
|
||||
test_same("'' + x <= '' + y");
|
||||
test_same("'' + x != '' + y");
|
||||
test_same("'' + x === '' + y");
|
||||
|
||||
test_same("'' + x <= '' + x"); // potentially foldable
|
||||
test_same("'' + x != '' + x"); // potentially foldable
|
||||
test_same("'' + x === '' + x"); // potentially foldable
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_string_comparison() {
|
||||
test("1 < '2'", "true");
|
||||
test("2 > '1'", "true");
|
||||
test("123 > '34'", "true");
|
||||
test("NaN >= 'NaN'", "false");
|
||||
test("1 == '2'", "false");
|
||||
test("1 != '1'", "false");
|
||||
test("NaN == 'NaN'", "false");
|
||||
test("1 === '1'", "false");
|
||||
test("1 !== '1'", "true");
|
||||
test_same("+x>''+y");
|
||||
test_same("+x==''+y");
|
||||
test("+x !== '' + y", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_number_comparison() {
|
||||
test("'1' < 2", "true");
|
||||
test("'2' > 1", "true");
|
||||
test("'123' > 34", "true");
|
||||
test("'NaN' < NaN", "false");
|
||||
test("'1' == 2", "false");
|
||||
test("'1' != 1", "false");
|
||||
test("'NaN' == NaN", "false");
|
||||
test("'1' === 1", "false");
|
||||
test("'1' !== 1", "true");
|
||||
test_same("''+x<+y");
|
||||
test_same("''+x==+y");
|
||||
test("'' + x === +y", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bigint_number_comparison() {
|
||||
test("1n < 2", "true");
|
||||
test("1n > 2", "false");
|
||||
test("1n == 1", "true");
|
||||
test("1n == 2", "false");
|
||||
|
||||
// comparing with decimals is allowed
|
||||
test("1n < 1.1", "true");
|
||||
test("1n < 1.9", "true");
|
||||
test("1n < 0.9", "false");
|
||||
test("-1n < -1.1", "false");
|
||||
test("-1n < -1.9", "false");
|
||||
test("-1n < -0.9", "true");
|
||||
test("1n > 1.1", "false");
|
||||
test("1n > 0.9", "true");
|
||||
test("-1n > -1.1", "true");
|
||||
test("-1n > -0.9", "false");
|
||||
|
||||
// Don't fold unsafely large numbers because there might be floating-point error
|
||||
let max_safe_int = 9_007_199_254_740_991_i64;
|
||||
let neg_max_safe_int = -9_007_199_254_740_991_i64;
|
||||
let max_safe_float = 9_007_199_254_740_991_f64;
|
||||
let neg_max_safe_float = -9_007_199_254_740_991_f64;
|
||||
test(&format!("0n > {max_safe_int}"), "false");
|
||||
test(&format!("0n < {max_safe_int}"), "true");
|
||||
test(&format!("0n > {neg_max_safe_int}"), "true");
|
||||
test(&format!("0n < {neg_max_safe_int}"), "false");
|
||||
test(&format!("0n > {max_safe_float}"), "false");
|
||||
test(&format!("0n < {max_safe_float}"), "true");
|
||||
test(&format!("0n > {neg_max_safe_float}"), "true");
|
||||
test(&format!("0n < {neg_max_safe_float}"), "false");
|
||||
|
||||
// comparing with Infinity is allowed
|
||||
test("1n < Infinity", "true");
|
||||
test("1n > Infinity", "false");
|
||||
test("1n < -Infinity", "false");
|
||||
test("1n > -Infinity", "true");
|
||||
|
||||
// null is interpreted as 0 when comparing with bigint
|
||||
test("1n < null", "false");
|
||||
test("1n > null", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bigint_string_comparison() {
|
||||
test("1n < '2'", "true");
|
||||
test("2n > '1'", "true");
|
||||
test("123n > '34'", "true");
|
||||
test("1n == '1'", "true");
|
||||
test("1n == '2'", "false");
|
||||
test("1n != '1'", "false");
|
||||
test("1n === '1'", "false");
|
||||
test("1n !== '1'", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_string_bigint_comparison() {
|
||||
test("'1' < 2n", "true");
|
||||
test("'2' > 1n", "true");
|
||||
test("'123' > 34n", "true");
|
||||
test("'1' == 1n", "true");
|
||||
test("'1' == 2n", "false");
|
||||
test("'1' != 1n", "false");
|
||||
test("'1' === 1n", "false");
|
||||
test("'1' !== 1n", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nan_comparison() {
|
||||
test("NaN < 1", "false");
|
||||
test("NaN <= 1", "false");
|
||||
test("NaN > 1", "false");
|
||||
test("NaN >= 1", "false");
|
||||
// test("NaN < 1n", "false");
|
||||
// test("NaN <= 1n", "false");
|
||||
// test("NaN > 1n", "false");
|
||||
// test("NaN >= 1n", "false");
|
||||
|
||||
test("NaN < NaN", "false");
|
||||
test("NaN >= NaN", "false");
|
||||
test("NaN == NaN", "false");
|
||||
test("NaN === NaN", "false");
|
||||
|
||||
test("NaN < null", "false");
|
||||
test("null >= NaN", "false");
|
||||
test("NaN == null", "false");
|
||||
test("null != NaN", "true");
|
||||
test("null === NaN", "false");
|
||||
|
||||
test("NaN < undefined", "false");
|
||||
test("undefined >= NaN", "false");
|
||||
test("NaN == undefined", "false");
|
||||
test("undefined != NaN", "true");
|
||||
test("undefined === NaN", "false");
|
||||
|
||||
test_same("NaN<x");
|
||||
test_same("x>=NaN");
|
||||
test_same("NaN==x");
|
||||
test_same("x!=NaN");
|
||||
test("NaN === x", "false");
|
||||
test("x !== NaN", "true");
|
||||
test_same("NaN==foo()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn js_typeof() {
|
||||
test("x = typeof 1", "x = \"number\"");
|
||||
test("x = typeof 'foo'", "x = \"string\"");
|
||||
test("x = typeof true", "x = \"boolean\"");
|
||||
test("x = typeof false", "x = \"boolean\"");
|
||||
test("x = typeof null", "x = \"object\"");
|
||||
test("x = typeof undefined", "x = \"undefined\"");
|
||||
test("x = typeof void 0", "x = \"undefined\"");
|
||||
test("x = typeof []", "x = \"object\"");
|
||||
test("x = typeof [1]", "x = \"object\"");
|
||||
test("x = typeof [1,[]]", "x = \"object\"");
|
||||
test("x = typeof {}", "x = \"object\"");
|
||||
test("x = typeof function() {}", "x = 'function'");
|
||||
|
||||
test_same("x = typeof[1,[foo()]]");
|
||||
test_same("x = typeof{bathwater:baby()}");
|
||||
}
|
||||
|
||||
#[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()");
|
||||
|
||||
// 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_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("a=+true", "a=1");
|
||||
test("a=+10", "a=10");
|
||||
// test("a=+false", "a=0");
|
||||
test_same("a=+foo()");
|
||||
test_same("a=+f");
|
||||
// test("a=+(f?true:false)", "a=+(f?1:0)");
|
||||
test("a=+0", "a=0");
|
||||
// test("a=+Infinity", "a=Infinity");
|
||||
// test("a=+NaN", "a=NaN");
|
||||
// test("a=+-7", "a=-7");
|
||||
// test("a=+.5", "a=.5");
|
||||
|
||||
// test("a=~0xffffffff", "a=0");
|
||||
// test("a=~~0xffffffff", "a=-1");
|
||||
// test_same("a=~.5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn unary_with_big_int() {
|
||||
test("-(1n)", "-1n");
|
||||
test("- -1n", "1n");
|
||||
test("!1n", "false");
|
||||
test("~0n", "-1n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_unary_ops_string_compare() {
|
||||
test_same("a = -1");
|
||||
test("a = ~0", "a = -1");
|
||||
test("a = ~1", "a = -2");
|
||||
test("a = ~101", "a = -102");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_logical_op() {
|
||||
test("x = true && x", "x = x");
|
||||
test("x = [foo()] && x", "x = ([foo()],x)");
|
||||
|
||||
test("x = false && x", "x = false");
|
||||
test("x = true || x", "x = true");
|
||||
test("x = false || x", "x = x");
|
||||
test("x = 0 && x", "x = 0");
|
||||
test("x = 3 || x", "x = 3");
|
||||
test("x = 0n && x", "x = 0n");
|
||||
test("x = 3n || x", "x = 3n");
|
||||
test("x = false || 0", "x = 0");
|
||||
|
||||
// unfoldable, because the right-side may be the result
|
||||
test("a = x && true", "a=x && true");
|
||||
test("a = x && false", "a=x && false");
|
||||
test("a = x || 3", "a=x || 3");
|
||||
test("a = x || false", "a=x || false");
|
||||
test("a = b ? c : x || false", "a=b ? c:x || false");
|
||||
test("a = b ? x || false : c", "a=b ? x || false:c");
|
||||
test("a = b ? c : x && true", "a=b ? c:x && true");
|
||||
test("a = b ? x && true : c", "a=b ? x && true:c");
|
||||
|
||||
// folded, but not here.
|
||||
test_same("a = x || false ? b : c");
|
||||
test_same("a = x && true ? b : c");
|
||||
|
||||
test("x = foo() || true || bar()", "x = foo() || true");
|
||||
test("x = foo() || true && bar()", "x = foo() || bar()");
|
||||
test("x = foo() || false && bar()", "x = foo() || false");
|
||||
test("x = foo() && false && bar()", "x = foo() && false");
|
||||
test("x = foo() && false || bar()", "x = (foo() && false,bar())");
|
||||
test("x = foo() || false || bar()", "x = foo() || bar()");
|
||||
test("x = foo() && true && bar()", "x = foo() && bar()");
|
||||
test("x = foo() || true || bar()", "x = foo() || true");
|
||||
test("x = foo() && false && bar()", "x = foo() && false");
|
||||
test("x = foo() && 0 && bar()", "x = foo() && 0");
|
||||
test("x = foo() && 1 && bar()", "x = foo() && bar()");
|
||||
test("x = foo() || 0 || bar()", "x = foo() || bar()");
|
||||
test("x = foo() || 1 || bar()", "x = foo() || 1");
|
||||
test("x = foo() && 0n && bar()", "x = foo() && 0n");
|
||||
test("x = foo() && 1n && bar()", "x = foo() && bar()");
|
||||
test("x = foo() || 0n || bar()", "x = foo() || bar()");
|
||||
test("x = foo() || 1n || bar()", "x = foo() || 1n");
|
||||
test_same("x = foo() || bar() || baz()");
|
||||
test_same("x = foo() && bar() && baz()");
|
||||
|
||||
test("0 || b()", "b()");
|
||||
test("1 && b()", "b()");
|
||||
test("a() && (1 && b())", "a() && b()");
|
||||
test("(a() && 1) && b()", "a() && b()");
|
||||
|
||||
test("(x || '') || y;", "x || y");
|
||||
test("false || (x || '');", "x || ''");
|
||||
test("(x && 1) && y;", "x && y");
|
||||
test("true && (x && 1);", "x && 1");
|
||||
|
||||
// Really not foldable, because it would change the type of the
|
||||
// expression if foo() returns something truthy but not true.
|
||||
// Cf. FoldConstants.tryFoldAndOr().
|
||||
// An example would be if foo() is 1 (truthy) and bar() is 0 (falsey):
|
||||
// (1 && true) || 0 == true
|
||||
// 1 || 0 == 1, but true =/= 1
|
||||
test_same("x = foo() && true || bar()");
|
||||
test_same("foo() && true || bar()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_logical_op2() {
|
||||
test("x = function(){} && x", "x=x");
|
||||
test("x = true && function(){}", "x=function(){}");
|
||||
test("x = [(function(){alert(x)})()] && x", "x=([function(){alert(x)}()],x)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_nullish_coalesce() {
|
||||
// fold if left is null/undefined
|
||||
test("null ?? 1", "1");
|
||||
test("undefined ?? false", "false");
|
||||
test("(a(), null) ?? 1", "(a(), null, 1)");
|
||||
|
||||
test("x = [foo()] ?? x", "x = [foo()]");
|
||||
|
||||
// short circuit on all non nullish LHS
|
||||
test("x = false ?? x", "x = false");
|
||||
test("x = true ?? x", "x = true");
|
||||
test("x = 0 ?? x", "x = 0");
|
||||
test("x = 3 ?? x", "x = 3");
|
||||
|
||||
// unfoldable, because the right-side may be the result
|
||||
test_same("a = x ?? true");
|
||||
test_same("a = x ?? false");
|
||||
test_same("a = x ?? 3");
|
||||
test_same("a = b ? c : x ?? false");
|
||||
test_same("a = b ? x ?? false : c");
|
||||
|
||||
// folded, but not here.
|
||||
test_same("a = x ?? false ? b : c");
|
||||
test_same("a = x ?? true ? b : c");
|
||||
|
||||
test_same("x = foo() ?? true ?? bar()");
|
||||
test("x = foo() ?? (true && bar())", "x = foo() ?? bar()");
|
||||
test_same("x = (foo() || false) ?? bar()");
|
||||
|
||||
test("a() ?? (1 ?? b())", "a() ?? 1");
|
||||
test("(a() ?? 1) ?? b()", "a() ?? 1 ?? b()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_void() {
|
||||
test_same("void 0");
|
||||
test("void 1", "void 0");
|
||||
test("void x", "void 0");
|
||||
test_same("void x()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_bit_shift() {
|
||||
test("x = 1 << 0", "x=1");
|
||||
test("x = -1 << 0", "x=-1");
|
||||
test("x = 1 << 1", "x=2");
|
||||
test("x = 3 << 1", "x=6");
|
||||
test("x = 1 << 8", "x=256");
|
||||
|
||||
test("x = 1 >> 0", "x=1");
|
||||
test("x = -1 >> 0", "x=-1");
|
||||
test("x = 1 >> 1", "x=0");
|
||||
test("x = 2 >> 1", "x=1");
|
||||
test("x = 5 >> 1", "x=2");
|
||||
test("x = 127 >> 3", "x=15");
|
||||
test("x = 3 >> 1", "x=1");
|
||||
test("x = 3 >> 2", "x=0");
|
||||
test("x = 10 >> 1", "x=5");
|
||||
test("x = 10 >> 2", "x=2");
|
||||
test("x = 10 >> 5", "x=0");
|
||||
|
||||
test("x = 10 >>> 1", "x=5");
|
||||
test("x = 10 >>> 2", "x=2");
|
||||
test("x = 10 >>> 5", "x=0");
|
||||
test("x = -1 >>> 1", "x=2147483647"); // 0x7fffffff
|
||||
test("x = -1 >>> 0", "x=4294967295"); // 0xffffffff
|
||||
test("x = -2 >>> 0", "x=4294967294"); // 0xfffffffe
|
||||
test("x = 0x90000000 >>> 28", "x=9");
|
||||
|
||||
test("x = 0xffffffff << 0", "x=-1");
|
||||
test("x = 0xffffffff << 4", "x=-16");
|
||||
test("1 << 32", "1<<32");
|
||||
test("1 << -1", "1<<-1");
|
||||
test("1 >> 32", "1>>32");
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -115,3 +115,21 @@ impl<'a> PeepholeRemoveDeadCode {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// /// <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeRemoveDeadCode.java>
|
||||
// #[cfg(test)]
|
||||
// mod test {
|
||||
// use oxc_allocator::Allocator;
|
||||
|
||||
// use crate::{tester, CompressOptions};
|
||||
|
||||
// fn test(source_text: &str, expected: &str) {
|
||||
// let allocator = Allocator::default();
|
||||
// let mut pass = super::PeepholeRemoveDeadCode::new();
|
||||
// tester::test(&allocator, source_text, expected, &mut pass);
|
||||
// }
|
||||
|
||||
// fn test_same(source_text: &str) {
|
||||
// test(source_text, source_text);
|
||||
// }
|
||||
// }
|
||||
@@ -245,3 +245,45 @@ impl<'a> PeepholeSubstituteAlternateSyntax {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntax.java>
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_allocator::Allocator;
|
||||
|
||||
use crate::{tester, CompressOptions};
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let allocator = Allocator::default();
|
||||
let mut pass = super::PeepholeSubstituteAlternateSyntax::new(CompressOptions::default());
|
||||
tester::test(&allocator, source_text, expected, &mut pass);
|
||||
}
|
||||
|
||||
fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fold_return_result() {
|
||||
test("function f(){return !1;}", "function f(){return !1}");
|
||||
test("function f(){return null;}", "function f(){return null}");
|
||||
test("function f(){return void 0;}", "function f(){return}");
|
||||
test("function f(){return void foo();}", "function f(){return void foo()}");
|
||||
test("function f(){return undefined;}", "function f(){return}");
|
||||
test("function f(){if(a()){return undefined;}}", "function f(){if(a())return}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined() {
|
||||
test("var x = undefined", "var x");
|
||||
test_same("var undefined = 1;function f() {var undefined=2;var x;}");
|
||||
test("function f(undefined) {}", "function f(undefined){}");
|
||||
test("try {} catch(undefined) {}", "try{}catch(undefined){}");
|
||||
test("for (undefined in {}) {}", "for(undefined in {}){}");
|
||||
test("undefined++", "undefined++");
|
||||
test("undefined += undefined", "undefined+=void 0");
|
||||
|
||||
// shadowd
|
||||
test_same("(function(undefined) { let x = typeof undefined; })()");
|
||||
}
|
||||
}
|
||||
@@ -88,3 +88,32 @@ impl<'a> RemoveSyntax {
|
||||
ident.name == "console"
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
use oxc_allocator::Allocator;
|
||||
|
||||
use crate::{tester, CompressOptions};
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let allocator = Allocator::default();
|
||||
let mut pass = super::RemoveSyntax::new(CompressOptions::all_true());
|
||||
tester::test(&allocator, source_text, expected, &mut pass);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens() {
|
||||
test("(((x)))", "x");
|
||||
test("(((a + b))) * c", "(a + b) * c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_console() {
|
||||
test("console.log()", "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_debugger() {
|
||||
test("debugger", "");
|
||||
}
|
||||
}
|
||||
@@ -34,9 +34,14 @@ impl<'a> Compressor<'a> {
|
||||
program: &mut Program<'a>,
|
||||
) {
|
||||
let mut ctx = TraverseCtx::new(scopes, symbols, self.allocator);
|
||||
// Run separate AST passes
|
||||
// TODO: inline variables
|
||||
self.remove_syntax(program, &mut ctx);
|
||||
|
||||
if self.options.dead_code_elimination {
|
||||
self.dead_code_elimination(program, &mut ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: inline variables
|
||||
self.fold_constants(program, &mut ctx);
|
||||
self.minimize_conditions(program, &mut ctx);
|
||||
self.remove_dead_code(program, &mut ctx);
|
||||
@@ -45,39 +50,33 @@ impl<'a> Compressor<'a> {
|
||||
self.collapse(program, &mut ctx);
|
||||
}
|
||||
|
||||
fn dead_code_elimination(self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
self.fold_constants(program, ctx);
|
||||
self.minimize_conditions(program, ctx);
|
||||
self.remove_dead_code(program, ctx);
|
||||
}
|
||||
|
||||
fn remove_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.remove_syntax {
|
||||
RemoveSyntax::new(self.options).build(program, ctx);
|
||||
}
|
||||
RemoveSyntax::new(self.options).build(program, ctx);
|
||||
}
|
||||
|
||||
fn minimize_conditions(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.minimize_conditions {
|
||||
PeepholeMinimizeConditions::new().build(program, ctx);
|
||||
}
|
||||
PeepholeMinimizeConditions::new().build(program, ctx);
|
||||
}
|
||||
|
||||
fn fold_constants(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.fold_constants {
|
||||
PeepholeFoldConstants::new().with_evaluate(self.options.evaluate).build(program, ctx);
|
||||
}
|
||||
PeepholeFoldConstants::new().with_evaluate(self.options.evaluate).build(program, ctx);
|
||||
}
|
||||
|
||||
fn substitute_alternate_syntax(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.substitute_alternate_syntax {
|
||||
PeepholeSubstituteAlternateSyntax::new(self.options).build(program, ctx);
|
||||
}
|
||||
PeepholeSubstituteAlternateSyntax::new(self.options).build(program, ctx);
|
||||
}
|
||||
|
||||
fn remove_dead_code(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.remove_dead_code {
|
||||
PeepholeRemoveDeadCode::new().build(program, ctx);
|
||||
}
|
||||
PeepholeRemoveDeadCode::new().build(program, ctx);
|
||||
}
|
||||
|
||||
fn collapse(&self, program: &mut Program<'a>, ctx: &mut TraverseCtx<'a>) {
|
||||
if self.options.collapse {
|
||||
CollapseVariableDeclarations::new(self.options).build(program, ctx);
|
||||
}
|
||||
CollapseVariableDeclarations::new(self.options).build(program, ctx);
|
||||
}
|
||||
}
|
||||
@@ -11,6 +11,9 @@ mod plugins;
|
||||
mod tri;
|
||||
mod ty;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tester;
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::ast::Program;
|
||||
use oxc_mangler::Mangler;
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct CompressOptions {
|
||||
pub remove_syntax: bool,
|
||||
pub minimize_conditions: bool,
|
||||
pub substitute_alternate_syntax: bool,
|
||||
pub fold_constants: bool,
|
||||
pub remove_dead_code: bool,
|
||||
pub collapse: bool,
|
||||
pub dead_code_elimination: bool,
|
||||
|
||||
/// Various optimizations for boolean context, for example `!!a ? b : c` → `a ? b : c`.
|
||||
///
|
||||
@@ -46,19 +41,14 @@ pub struct CompressOptions {
|
||||
#[allow(clippy::derivable_impls)]
|
||||
impl Default for CompressOptions {
|
||||
fn default() -> Self {
|
||||
Self { drop_console: false, ..Self::all_true() }
|
||||
Self { dead_code_elimination: false, drop_console: false, ..Self::all_true() }
|
||||
}
|
||||
}
|
||||
|
||||
impl CompressOptions {
|
||||
pub fn all_true() -> Self {
|
||||
Self {
|
||||
remove_syntax: true,
|
||||
minimize_conditions: true,
|
||||
substitute_alternate_syntax: true,
|
||||
fold_constants: true,
|
||||
remove_dead_code: true,
|
||||
collapse: true,
|
||||
dead_code_elimination: false,
|
||||
booleans: true,
|
||||
drop_debugger: true,
|
||||
drop_console: true,
|
||||
@@ -71,12 +61,7 @@ impl CompressOptions {
|
||||
|
||||
pub fn all_false() -> Self {
|
||||
Self {
|
||||
remove_syntax: false,
|
||||
minimize_conditions: false,
|
||||
substitute_alternate_syntax: false,
|
||||
fold_constants: false,
|
||||
remove_dead_code: false,
|
||||
collapse: false,
|
||||
dead_code_elimination: false,
|
||||
booleans: false,
|
||||
drop_debugger: false,
|
||||
drop_console: false,
|
||||
@@ -88,12 +73,6 @@ impl CompressOptions {
|
||||
}
|
||||
|
||||
pub fn dead_code_elimination() -> Self {
|
||||
Self {
|
||||
remove_syntax: true,
|
||||
minimize_conditions: true,
|
||||
fold_constants: true,
|
||||
remove_dead_code: true,
|
||||
..Self::all_false()
|
||||
}
|
||||
Self { dead_code_elimination: true, ..Self::all_false() }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_codegen::{CodeGenerator, CodegenOptions};
|
||||
use oxc_parser::Parser;
|
||||
use oxc_semantic::SemanticBuilder;
|
||||
use oxc_span::SourceType;
|
||||
use oxc_traverse::TraverseCtx;
|
||||
|
||||
use crate::{ast_passes::CompressorPass, ast_passes::RemoveSyntax, CompressOptions};
|
||||
|
||||
pub fn test<'a, P: CompressorPass<'a>>(
|
||||
allocator: &'a Allocator,
|
||||
source_text: &'a str,
|
||||
expected: &'a str,
|
||||
pass: &mut P,
|
||||
) {
|
||||
let result = run(allocator, source_text, Some(pass));
|
||||
let expected = run::<P>(allocator, expected, None);
|
||||
assert_eq!(result, expected, "\nfor source\n{source_text}\nexpect\n{expected}\ngot\n{result}");
|
||||
}
|
||||
|
||||
fn run<'a, P: CompressorPass<'a>>(
|
||||
allocator: &'a Allocator,
|
||||
source_text: &'a str,
|
||||
pass: Option<&mut P>,
|
||||
) -> String {
|
||||
let source_type = SourceType::mjs();
|
||||
let mut program = Parser::new(allocator, source_text, source_type).parse().program;
|
||||
|
||||
if let Some(pass) = pass {
|
||||
let (symbols, scopes) =
|
||||
SemanticBuilder::new("").build(&program).semantic.into_symbol_table_and_scope_tree();
|
||||
let mut ctx = TraverseCtx::new(scopes, symbols, allocator);
|
||||
RemoveSyntax::new(CompressOptions::all_false()).build(&mut program, &mut ctx);
|
||||
pass.build(&mut program, &mut ctx);
|
||||
}
|
||||
|
||||
CodeGenerator::new()
|
||||
.with_options(CodegenOptions { single_quote: true, ..CodegenOptions::default() })
|
||||
.build(&program)
|
||||
.source_text
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
//! <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/CollapseVariableDeclarations.java>
|
||||
|
||||
use crate::CompressOptions;
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let options = CompressOptions::all_true();
|
||||
crate::test(source_text, expected, options);
|
||||
}
|
||||
|
||||
fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn cjs() {
|
||||
// Do not join `require` calls for cjs-module-lexer.
|
||||
test_same(
|
||||
"
|
||||
Object.defineProperty(exports, '__esModule', { value: true });
|
||||
var compilerDom = require('@vue/compiler-dom');
|
||||
var runtimeDom = require('@vue/runtime-dom');
|
||||
var shared = require('@vue/shared');
|
||||
",
|
||||
);
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
//! <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/MinimizedConditionTest.java>
|
||||
|
||||
use oxc_minifier::CompressOptions;
|
||||
|
||||
// TODO: handle negative cases
|
||||
fn test(source_text: &str, positive: &str, _negative: &str) {
|
||||
let options = CompressOptions {
|
||||
remove_syntax: true,
|
||||
minimize_conditions: true,
|
||||
..CompressOptions::all_false()
|
||||
};
|
||||
crate::test(source_text, positive, options);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn try_minimize_cond_simple() {
|
||||
test("x", "x", "x");
|
||||
test("!x", "!x", "!x");
|
||||
test("!!x", "x", "x");
|
||||
test("!(x && y)", "!x || !y", "!(x && y)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn minimize_demorgan_simple() {
|
||||
test("!(x&&y)", "!x||!y", "!(x&&y)");
|
||||
test("!(x||y)", "!x&&!y", "!(x||y)");
|
||||
test("!x||!y", "!x||!y", "!(x&&y)");
|
||||
test("!x&&!y", "!x&&!y", "!(x||y)");
|
||||
test("!(x && y && z)", "!(x && y && z)", "!(x && y && z)");
|
||||
test("(!a||!b)&&c", "(!a||!b)&&c", "!(a&&b||!c)");
|
||||
test("(!a||!b)&&(c||d)", "!(a&&b||!c&&!d)", "!(a&&b||!c&&!d)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn minimize_bug8494751() {
|
||||
test(
|
||||
"x && (y===2 || !f()) && (y===3 || !h())",
|
||||
// TODO(tbreisacher): The 'positive' option could be better:
|
||||
// "x && !((y!==2 && f()) || (y!==3 && h()))",
|
||||
"!(!x || (y!==2 && f()) || (y!==3 && h()))",
|
||||
"!(!x || (y!==2 && f()) || (y!==3 && h()))",
|
||||
);
|
||||
|
||||
test(
|
||||
"x && (y===2 || !f?.()) && (y===3 || !h?.())",
|
||||
"!(!x || (y!==2 && f?.()) || (y!==3 && h?.()))",
|
||||
"!(!x || (y!==2 && f?.()) || (y!==3 && h?.()))",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn minimize_complementable_operator() {
|
||||
test("0===c && (2===a || 1===a)", "0===c && (2===a || 1===a)", "!(0!==c || 2!==a && 1!==a)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn minimize_hook() {
|
||||
test("!(x ? y : z)", "(x ? !y : !z)", "!(x ? y : z)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn minimize_comma() {
|
||||
test("!(inc(), test())", "inc(), !test()", "!(inc(), test())");
|
||||
test("!(inc?.(), test?.())", "inc?.(), !test?.()", "!(inc?.(), test?.())");
|
||||
test("!((x,y)&&z)", "(x,!y)||!z", "!((x,y)&&z)");
|
||||
}
|
||||
@@ -1,14 +1,6 @@
|
||||
mod collapse_variable_declarations;
|
||||
mod dead_code_elimination;
|
||||
mod minimized_condition;
|
||||
mod peephole_fold_constants;
|
||||
mod peephole_minimize_conditions;
|
||||
mod peephole_substitute_alternate_syntax;
|
||||
mod remove_syntax;
|
||||
|
||||
// Oxc Integration Tests
|
||||
|
||||
use crate::CompressOptions;
|
||||
use oxc_minifier::CompressOptions;
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let options = CompressOptions::default();
|
||||
@@ -19,10 +11,38 @@ fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
// Oxc Integration Tests
|
||||
|
||||
#[test]
|
||||
fn cjs() {
|
||||
// Bail `cjs-module-lexer`.
|
||||
test_same("0 && (module.exports = { version });");
|
||||
|
||||
// Bail `cjs-module-lexer`.
|
||||
// Export is undefined when `enumerable` is "!0".
|
||||
// https://github.com/nodejs/cjs-module-lexer/issues/64
|
||||
test_same(
|
||||
"Object.defineProperty(exports, 'ConnectableObservable', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return ConnectableObservable_1.ConnectableObservable;
|
||||
}
|
||||
});",
|
||||
);
|
||||
// @babel/types/lib/index.js
|
||||
test_same(
|
||||
r#"Object.keys(_index6).forEach(function(key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
||||
if (key in exports && exports[key] === _index6[key]) return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return _index6[key];
|
||||
}
|
||||
});
|
||||
});"#,
|
||||
);
|
||||
}
|
||||
|
||||
#[test] // https://github.com/oxc-project/oxc/issues/4341
|
||||
|
||||
@@ -1,681 +0,0 @@
|
||||
//! <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeFoldConstantsTest.java>
|
||||
|
||||
use crate::CompressOptions;
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let options = CompressOptions {
|
||||
remove_syntax: true,
|
||||
fold_constants: true,
|
||||
..CompressOptions::all_false()
|
||||
};
|
||||
crate::test(source_text, expected, options);
|
||||
}
|
||||
|
||||
fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined_comparison1() {
|
||||
test("undefined == undefined", "true");
|
||||
test("undefined == null", "true");
|
||||
test("undefined == void 0", "true");
|
||||
|
||||
test("undefined == 0", "false");
|
||||
test("undefined == 1", "false");
|
||||
test("undefined == 'hi'", "false");
|
||||
test("undefined == true", "false");
|
||||
test("undefined == false", "false");
|
||||
|
||||
test("undefined === undefined", "true");
|
||||
test("undefined === null", "false");
|
||||
test("undefined === void 0", "true");
|
||||
|
||||
test_same("undefined == this");
|
||||
test_same("undefined == x");
|
||||
|
||||
test("undefined != undefined", "false");
|
||||
test("undefined != null", "false");
|
||||
test("undefined != void 0", "false");
|
||||
|
||||
test("undefined != 0", "true");
|
||||
test("undefined != 1", "true");
|
||||
test("undefined != 'hi'", "true");
|
||||
test("undefined != true", "true");
|
||||
test("undefined != false", "true");
|
||||
|
||||
test("undefined !== undefined", "false");
|
||||
test("undefined !== void 0", "false");
|
||||
test("undefined !== null", "true");
|
||||
|
||||
test_same("undefined != this");
|
||||
test_same("undefined != x");
|
||||
|
||||
test("undefined < undefined", "false");
|
||||
test("undefined > undefined", "false");
|
||||
test("undefined >= undefined", "false");
|
||||
test("undefined <= undefined", "false");
|
||||
|
||||
test("0 < undefined", "false");
|
||||
test("true > undefined", "false");
|
||||
test("'hi' >= undefined", "false");
|
||||
test("null <= undefined", "false");
|
||||
|
||||
test("undefined < 0", "false");
|
||||
test("undefined > true", "false");
|
||||
test("undefined >= 'hi'", "false");
|
||||
test("undefined <= null", "false");
|
||||
|
||||
test("null == undefined", "true");
|
||||
test("0 == undefined", "false");
|
||||
test("1 == undefined", "false");
|
||||
test("'hi' == undefined", "false");
|
||||
test("true == undefined", "false");
|
||||
test("false == undefined", "false");
|
||||
test("null === undefined", "false");
|
||||
test("void 0 === undefined", "true");
|
||||
|
||||
test("undefined == NaN", "false");
|
||||
test("NaN == undefined", "false");
|
||||
test("undefined == Infinity", "false");
|
||||
test("Infinity == undefined", "false");
|
||||
test("undefined == -Infinity", "false");
|
||||
test("-Infinity == undefined", "false");
|
||||
test("({}) == undefined", "false");
|
||||
test("undefined == ({})", "false");
|
||||
test("([]) == undefined", "false");
|
||||
test("undefined == ([])", "false");
|
||||
test("(/a/g) == undefined", "false");
|
||||
test("undefined == (/a/g)", "false");
|
||||
test("(function(){}) == undefined", "false");
|
||||
test("undefined == (function(){})", "false");
|
||||
|
||||
test("undefined != NaN", "true");
|
||||
test("NaN != undefined", "true");
|
||||
test("undefined != Infinity", "true");
|
||||
test("Infinity != undefined", "true");
|
||||
test("undefined != -Infinity", "true");
|
||||
test("-Infinity != undefined", "true");
|
||||
test("({}) != undefined", "true");
|
||||
test("undefined != ({})", "true");
|
||||
test("([]) != undefined", "true");
|
||||
test("undefined != ([])", "true");
|
||||
test("(/a/g) != undefined", "true");
|
||||
test("undefined != (/a/g)", "true");
|
||||
test("(function(){}) != undefined", "true");
|
||||
test("undefined != (function(){})", "true");
|
||||
|
||||
test_same("this == undefined");
|
||||
test_same("x == undefined");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_undefined_comparison2() {
|
||||
test("\"123\" !== void 0", "true");
|
||||
test("\"123\" === void 0", "false");
|
||||
|
||||
test("void 0 !== \"123\"", "true");
|
||||
test("void 0 === \"123\"", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_undefined_comparison3() {
|
||||
test("\"123\" !== undefined", "true");
|
||||
test("\"123\" === undefined", "false");
|
||||
|
||||
test("undefined !== \"123\"", "true");
|
||||
test("undefined === \"123\"", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_null_comparison1() {
|
||||
test("null == undefined", "true");
|
||||
test("null == null", "true");
|
||||
test("null == void 0", "true");
|
||||
|
||||
test("null == 0", "false");
|
||||
test("null == 1", "false");
|
||||
// test("null == 0n", "false");
|
||||
// test("null == 1n", "false");
|
||||
test("null == 'hi'", "false");
|
||||
test("null == true", "false");
|
||||
test("null == false", "false");
|
||||
|
||||
test("null === undefined", "false");
|
||||
test("null === null", "true");
|
||||
test("null === void 0", "false");
|
||||
test_same("null===x");
|
||||
|
||||
test_same("null==this");
|
||||
test_same("null==x");
|
||||
|
||||
test("null != undefined", "false");
|
||||
test("null != null", "false");
|
||||
test("null != void 0", "false");
|
||||
|
||||
test("null != 0", "true");
|
||||
test("null != 1", "true");
|
||||
// test("null != 0n", "true");
|
||||
// test("null != 1n", "true");
|
||||
test("null != 'hi'", "true");
|
||||
test("null != true", "true");
|
||||
test("null != false", "true");
|
||||
|
||||
test("null !== undefined", "true");
|
||||
test("null !== void 0", "true");
|
||||
test("null !== null", "false");
|
||||
|
||||
test_same("null!=this");
|
||||
test_same("null!=x");
|
||||
|
||||
test("null < null", "false");
|
||||
test("null > null", "false");
|
||||
test("null >= null", "true");
|
||||
test("null <= null", "true");
|
||||
|
||||
test("0 < null", "false");
|
||||
test("0 > null", "false");
|
||||
test("0 >= null", "true");
|
||||
// test("0n < null", "false");
|
||||
// test("0n > null", "false");
|
||||
// test("0n >= null", "true");
|
||||
test("true > null", "true");
|
||||
test("'hi' < null", "false");
|
||||
test("'hi' >= null", "false");
|
||||
test("null <= null", "true");
|
||||
|
||||
test("null < 0", "false");
|
||||
// test("null < 0n", "false");
|
||||
test("null > true", "false");
|
||||
test("null < 'hi'", "false");
|
||||
test("null >= 'hi'", "false");
|
||||
test("null <= null", "true");
|
||||
|
||||
test("null == null", "true");
|
||||
test("0 == null", "false");
|
||||
test("1 == null", "false");
|
||||
test("'hi' == null", "false");
|
||||
test("true == null", "false");
|
||||
test("false == null", "false");
|
||||
test("null === null", "true");
|
||||
test("void 0 === null", "false");
|
||||
|
||||
test("null == NaN", "false");
|
||||
test("NaN == null", "false");
|
||||
test("null == Infinity", "false");
|
||||
test("Infinity == null", "false");
|
||||
test("null == -Infinity", "false");
|
||||
test("-Infinity == null", "false");
|
||||
test("({}) == null", "false");
|
||||
test("null == ({})", "false");
|
||||
test("([]) == null", "false");
|
||||
test("null == ([])", "false");
|
||||
test("(/a/g) == null", "false");
|
||||
test("null == (/a/g)", "false");
|
||||
test("(function(){}) == null", "false");
|
||||
test("null == (function(){})", "false");
|
||||
|
||||
test("null != NaN", "true");
|
||||
test("NaN != null", "true");
|
||||
test("null != Infinity", "true");
|
||||
test("Infinity != null", "true");
|
||||
test("null != -Infinity", "true");
|
||||
test("-Infinity != null", "true");
|
||||
test("({}) != null", "true");
|
||||
test("null != ({})", "true");
|
||||
test("([]) != null", "true");
|
||||
test("null != ([])", "true");
|
||||
test("(/a/g) != null", "true");
|
||||
test("null != (/a/g)", "true");
|
||||
test("(function(){}) != null", "true");
|
||||
test("null != (function(){})", "true");
|
||||
|
||||
test_same("({a:f()})==null");
|
||||
test_same("null=={a:f()}");
|
||||
test_same("[f()]==null");
|
||||
test_same("null==[f()]");
|
||||
|
||||
test_same("this==null");
|
||||
test_same("x==null");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boolean_boolean_comparison() {
|
||||
test_same("!x==!y");
|
||||
test_same("!x<!y");
|
||||
test_same("!x!==!y");
|
||||
|
||||
test_same("!x==!x"); // foldable
|
||||
test_same("!x<!x"); // foldable
|
||||
test_same("!x!==!x"); // foldable
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boolean_number_comparison() {
|
||||
test_same("!x==+y");
|
||||
test_same("!x<=+y");
|
||||
test("!x !== +y", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_boolean_comparison() {
|
||||
test_same("+x==!y");
|
||||
test_same("+x<=!y");
|
||||
test("+x === !y", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_boolean_string_comparison() {
|
||||
test_same("!x==''+y");
|
||||
test_same("!x<=''+y");
|
||||
test("!x !== '' + y", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_boolean_comparison() {
|
||||
test_same("''+x==!y");
|
||||
test_same("''+x<=!y");
|
||||
test("'' + x === !y", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_string_string_comparison() {
|
||||
test("'a' < 'b'", "true");
|
||||
test("'a' <= 'b'", "true");
|
||||
test("'a' > 'b'", "false");
|
||||
test("'a' >= 'b'", "false");
|
||||
test("+'a' < +'b'", "false");
|
||||
test_same("typeof a < 'a'");
|
||||
test_same("'a' >= typeof a");
|
||||
test("typeof a < typeof a", "false");
|
||||
test("typeof a >= typeof a", "true");
|
||||
test("typeof 3 > typeof 4", "false");
|
||||
test("typeof function() {} < typeof function() {}", "false");
|
||||
test("'a' == 'a'", "true");
|
||||
test("'b' != 'a'", "true");
|
||||
test_same("'undefined' == typeof a");
|
||||
test_same("typeof a != 'number'");
|
||||
test_same("'undefined' == typeof a");
|
||||
test_same("'undefined' == typeof a");
|
||||
test("typeof a == typeof a", "true");
|
||||
test("'a' === 'a'", "true");
|
||||
test("'b' !== 'a'", "true");
|
||||
test("typeof a === typeof a", "true");
|
||||
test("typeof a !== typeof a", "false");
|
||||
test_same("'' + x <= '' + y");
|
||||
test_same("'' + x != '' + y");
|
||||
test_same("'' + x === '' + y");
|
||||
|
||||
test_same("'' + x <= '' + x"); // potentially foldable
|
||||
test_same("'' + x != '' + x"); // potentially foldable
|
||||
test_same("'' + x === '' + x"); // potentially foldable
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_number_string_comparison() {
|
||||
test("1 < '2'", "true");
|
||||
test("2 > '1'", "true");
|
||||
test("123 > '34'", "true");
|
||||
test("NaN >= 'NaN'", "false");
|
||||
test("1 == '2'", "false");
|
||||
test("1 != '1'", "false");
|
||||
test("NaN == 'NaN'", "false");
|
||||
test("1 === '1'", "false");
|
||||
test("1 !== '1'", "true");
|
||||
test_same("+x>''+y");
|
||||
test_same("+x==''+y");
|
||||
test("+x !== '' + y", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_string_number_comparison() {
|
||||
test("'1' < 2", "true");
|
||||
test("'2' > 1", "true");
|
||||
test("'123' > 34", "true");
|
||||
test("'NaN' < NaN", "false");
|
||||
test("'1' == 2", "false");
|
||||
test("'1' != 1", "false");
|
||||
test("'NaN' == NaN", "false");
|
||||
test("'1' === 1", "false");
|
||||
test("'1' !== 1", "true");
|
||||
test_same("''+x<+y");
|
||||
test_same("''+x==+y");
|
||||
test("'' + x === +y", "false");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bigint_number_comparison() {
|
||||
test("1n < 2", "true");
|
||||
test("1n > 2", "false");
|
||||
test("1n == 1", "true");
|
||||
test("1n == 2", "false");
|
||||
|
||||
// comparing with decimals is allowed
|
||||
test("1n < 1.1", "true");
|
||||
test("1n < 1.9", "true");
|
||||
test("1n < 0.9", "false");
|
||||
test("-1n < -1.1", "false");
|
||||
test("-1n < -1.9", "false");
|
||||
test("-1n < -0.9", "true");
|
||||
test("1n > 1.1", "false");
|
||||
test("1n > 0.9", "true");
|
||||
test("-1n > -1.1", "true");
|
||||
test("-1n > -0.9", "false");
|
||||
|
||||
// Don't fold unsafely large numbers because there might be floating-point error
|
||||
let max_safe_int = 9_007_199_254_740_991_i64;
|
||||
let neg_max_safe_int = -9_007_199_254_740_991_i64;
|
||||
let max_safe_float = 9_007_199_254_740_991_f64;
|
||||
let neg_max_safe_float = -9_007_199_254_740_991_f64;
|
||||
test(&format!("0n > {max_safe_int}"), "false");
|
||||
test(&format!("0n < {max_safe_int}"), "true");
|
||||
test(&format!("0n > {neg_max_safe_int}"), "true");
|
||||
test(&format!("0n < {neg_max_safe_int}"), "false");
|
||||
test(&format!("0n > {max_safe_float}"), "false");
|
||||
test(&format!("0n < {max_safe_float}"), "true");
|
||||
test(&format!("0n > {neg_max_safe_float}"), "true");
|
||||
test(&format!("0n < {neg_max_safe_float}"), "false");
|
||||
|
||||
// comparing with Infinity is allowed
|
||||
test("1n < Infinity", "true");
|
||||
test("1n > Infinity", "false");
|
||||
test("1n < -Infinity", "false");
|
||||
test("1n > -Infinity", "true");
|
||||
|
||||
// null is interpreted as 0 when comparing with bigint
|
||||
test("1n < null", "false");
|
||||
test("1n > null", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_bigint_string_comparison() {
|
||||
test("1n < '2'", "true");
|
||||
test("2n > '1'", "true");
|
||||
test("123n > '34'", "true");
|
||||
test("1n == '1'", "true");
|
||||
test("1n == '2'", "false");
|
||||
test("1n != '1'", "false");
|
||||
test("1n === '1'", "false");
|
||||
test("1n !== '1'", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_string_bigint_comparison() {
|
||||
test("'1' < 2n", "true");
|
||||
test("'2' > 1n", "true");
|
||||
test("'123' > 34n", "true");
|
||||
test("'1' == 1n", "true");
|
||||
test("'1' == 2n", "false");
|
||||
test("'1' != 1n", "false");
|
||||
test("'1' === 1n", "false");
|
||||
test("'1' !== 1n", "true");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_nan_comparison() {
|
||||
test("NaN < 1", "false");
|
||||
test("NaN <= 1", "false");
|
||||
test("NaN > 1", "false");
|
||||
test("NaN >= 1", "false");
|
||||
// test("NaN < 1n", "false");
|
||||
// test("NaN <= 1n", "false");
|
||||
// test("NaN > 1n", "false");
|
||||
// test("NaN >= 1n", "false");
|
||||
|
||||
test("NaN < NaN", "false");
|
||||
test("NaN >= NaN", "false");
|
||||
test("NaN == NaN", "false");
|
||||
test("NaN === NaN", "false");
|
||||
|
||||
test("NaN < null", "false");
|
||||
test("null >= NaN", "false");
|
||||
test("NaN == null", "false");
|
||||
test("null != NaN", "true");
|
||||
test("null === NaN", "false");
|
||||
|
||||
test("NaN < undefined", "false");
|
||||
test("undefined >= NaN", "false");
|
||||
test("NaN == undefined", "false");
|
||||
test("undefined != NaN", "true");
|
||||
test("undefined === NaN", "false");
|
||||
|
||||
test_same("NaN<x");
|
||||
test_same("x>=NaN");
|
||||
test_same("NaN==x");
|
||||
test_same("x!=NaN");
|
||||
test("NaN === x", "false");
|
||||
test("x !== NaN", "true");
|
||||
test_same("NaN==foo()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn js_typeof() {
|
||||
test("x = typeof 1", "x = \"number\"");
|
||||
test("x = typeof 'foo'", "x = \"string\"");
|
||||
test("x = typeof true", "x = \"boolean\"");
|
||||
test("x = typeof false", "x = \"boolean\"");
|
||||
test("x = typeof null", "x = \"object\"");
|
||||
test("x = typeof undefined", "x = \"undefined\"");
|
||||
test("x = typeof void 0", "x = \"undefined\"");
|
||||
test("x = typeof []", "x = \"object\"");
|
||||
test("x = typeof [1]", "x = \"object\"");
|
||||
test("x = typeof [1,[]]", "x = \"object\"");
|
||||
test("x = typeof {}", "x = \"object\"");
|
||||
test("x = typeof function() {}", "x = 'function'");
|
||||
|
||||
test_same("x = typeof[1,[foo()]]");
|
||||
test_same("x = typeof{bathwater:baby()}");
|
||||
}
|
||||
|
||||
#[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()");
|
||||
|
||||
// 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_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("a=+true", "a=1");
|
||||
test("a=+10", "a=10");
|
||||
// test("a=+false", "a=0");
|
||||
test_same("a=+foo()");
|
||||
test_same("a=+f");
|
||||
// test("a=+(f?true:false)", "a=+(f?1:0)");
|
||||
test("a=+0", "a=0");
|
||||
// test("a=+Infinity", "a=Infinity");
|
||||
// test("a=+NaN", "a=NaN");
|
||||
// test("a=+-7", "a=-7");
|
||||
// test("a=+.5", "a=.5");
|
||||
|
||||
// test("a=~0xffffffff", "a=0");
|
||||
// test("a=~~0xffffffff", "a=-1");
|
||||
// test_same("a=~.5", PeepholeFoldConstants.FRACTIONAL_BITWISE_OPERAND);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn unary_with_big_int() {
|
||||
test("-(1n)", "-1n");
|
||||
test("- -1n", "1n");
|
||||
test("!1n", "false");
|
||||
test("~0n", "-1n");
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[ignore]
|
||||
fn test_unary_ops_string_compare() {
|
||||
test_same("a = -1");
|
||||
test("a = ~0", "a = -1");
|
||||
test("a = ~1", "a = -2");
|
||||
test("a = ~101", "a = -102");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_logical_op() {
|
||||
test("x = true && x", "x = x");
|
||||
test("x = [foo()] && x", "x = ([foo()],x)");
|
||||
|
||||
test("x = false && x", "x = false");
|
||||
test("x = true || x", "x = true");
|
||||
test("x = false || x", "x = x");
|
||||
test("x = 0 && x", "x = 0");
|
||||
test("x = 3 || x", "x = 3");
|
||||
test("x = 0n && x", "x = 0n");
|
||||
test("x = 3n || x", "x = 3n");
|
||||
test("x = false || 0", "x = 0");
|
||||
|
||||
// unfoldable, because the right-side may be the result
|
||||
test("a = x && true", "a=x && true");
|
||||
test("a = x && false", "a=x && false");
|
||||
test("a = x || 3", "a=x || 3");
|
||||
test("a = x || false", "a=x || false");
|
||||
test("a = b ? c : x || false", "a=b ? c:x || false");
|
||||
test("a = b ? x || false : c", "a=b ? x || false:c");
|
||||
test("a = b ? c : x && true", "a=b ? c:x && true");
|
||||
test("a = b ? x && true : c", "a=b ? x && true:c");
|
||||
|
||||
// folded, but not here.
|
||||
test_same("a = x || false ? b : c");
|
||||
test_same("a = x && true ? b : c");
|
||||
|
||||
test("x = foo() || true || bar()", "x = foo() || true");
|
||||
test("x = foo() || true && bar()", "x = foo() || bar()");
|
||||
test("x = foo() || false && bar()", "x = foo() || false");
|
||||
test("x = foo() && false && bar()", "x = foo() && false");
|
||||
test("x = foo() && false || bar()", "x = (foo() && false,bar())");
|
||||
test("x = foo() || false || bar()", "x = foo() || bar()");
|
||||
test("x = foo() && true && bar()", "x = foo() && bar()");
|
||||
test("x = foo() || true || bar()", "x = foo() || true");
|
||||
test("x = foo() && false && bar()", "x = foo() && false");
|
||||
test("x = foo() && 0 && bar()", "x = foo() && 0");
|
||||
test("x = foo() && 1 && bar()", "x = foo() && bar()");
|
||||
test("x = foo() || 0 || bar()", "x = foo() || bar()");
|
||||
test("x = foo() || 1 || bar()", "x = foo() || 1");
|
||||
test("x = foo() && 0n && bar()", "x = foo() && 0n");
|
||||
test("x = foo() && 1n && bar()", "x = foo() && bar()");
|
||||
test("x = foo() || 0n || bar()", "x = foo() || bar()");
|
||||
test("x = foo() || 1n || bar()", "x = foo() || 1n");
|
||||
test_same("x = foo() || bar() || baz()");
|
||||
test_same("x = foo() && bar() && baz()");
|
||||
|
||||
test("0 || b()", "b()");
|
||||
test("1 && b()", "b()");
|
||||
test("a() && (1 && b())", "a() && b()");
|
||||
test("(a() && 1) && b()", "a() && b()");
|
||||
|
||||
test("(x || '') || y;", "x || y");
|
||||
test("false || (x || '');", "x || ''");
|
||||
test("(x && 1) && y;", "x && y");
|
||||
test("true && (x && 1);", "x && 1");
|
||||
|
||||
// Really not foldable, because it would change the type of the
|
||||
// expression if foo() returns something truthy but not true.
|
||||
// Cf. FoldConstants.tryFoldAndOr().
|
||||
// An example would be if foo() is 1 (truthy) and bar() is 0 (falsey):
|
||||
// (1 && true) || 0 == true
|
||||
// 1 || 0 == 1, but true =/= 1
|
||||
test_same("x = foo() && true || bar()");
|
||||
test_same("foo() && true || bar()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_logical_op2() {
|
||||
test("x = function(){} && x", "x=x");
|
||||
test("x = true && function(){}", "x=function(){}");
|
||||
test("x = [(function(){alert(x)})()] && x", "x=([function(){alert(x)}()],x)");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_nullish_coalesce() {
|
||||
// fold if left is null/undefined
|
||||
test("null ?? 1", "1");
|
||||
test("undefined ?? false", "false");
|
||||
test("(a(), null) ?? 1", "(a(), null, 1)");
|
||||
|
||||
test("x = [foo()] ?? x", "x = [foo()]");
|
||||
|
||||
// short circuit on all non nullish LHS
|
||||
test("x = false ?? x", "x = false");
|
||||
test("x = true ?? x", "x = true");
|
||||
test("x = 0 ?? x", "x = 0");
|
||||
test("x = 3 ?? x", "x = 3");
|
||||
|
||||
// unfoldable, because the right-side may be the result
|
||||
test_same("a = x ?? true");
|
||||
test_same("a = x ?? false");
|
||||
test_same("a = x ?? 3");
|
||||
test_same("a = b ? c : x ?? false");
|
||||
test_same("a = b ? x ?? false : c");
|
||||
|
||||
// folded, but not here.
|
||||
test_same("a = x ?? false ? b : c");
|
||||
test_same("a = x ?? true ? b : c");
|
||||
|
||||
test_same("x = foo() ?? true ?? bar()");
|
||||
test("x = foo() ?? (true && bar())", "x = foo() ?? bar()");
|
||||
test_same("x = (foo() || false) ?? bar()");
|
||||
|
||||
test("a() ?? (1 ?? b())", "a() ?? 1");
|
||||
test("(a() ?? 1) ?? b()", "a() ?? 1 ?? b()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_void() {
|
||||
test_same("void 0");
|
||||
test("void 1", "void 0");
|
||||
test("void x", "void 0");
|
||||
test_same("void x()");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_fold_bit_shift() {
|
||||
test("x = 1 << 0", "x=1");
|
||||
test("x = -1 << 0", "x=-1");
|
||||
test("x = 1 << 1", "x=2");
|
||||
test("x = 3 << 1", "x=6");
|
||||
test("x = 1 << 8", "x=256");
|
||||
|
||||
test("x = 1 >> 0", "x=1");
|
||||
test("x = -1 >> 0", "x=-1");
|
||||
test("x = 1 >> 1", "x=0");
|
||||
test("x = 2 >> 1", "x=1");
|
||||
test("x = 5 >> 1", "x=2");
|
||||
test("x = 127 >> 3", "x=15");
|
||||
test("x = 3 >> 1", "x=1");
|
||||
test("x = 3 >> 2", "x=0");
|
||||
test("x = 10 >> 1", "x=5");
|
||||
test("x = 10 >> 2", "x=2");
|
||||
test("x = 10 >> 5", "x=0");
|
||||
|
||||
test("x = 10 >>> 1", "x=5");
|
||||
test("x = 10 >>> 2", "x=2");
|
||||
test("x = 10 >>> 5", "x=0");
|
||||
test("x = -1 >>> 1", "x=2147483647"); // 0x7fffffff
|
||||
test("x = -1 >>> 0", "x=4294967295"); // 0xffffffff
|
||||
test("x = -2 >>> 0", "x=4294967294"); // 0xfffffffe
|
||||
test("x = 0x90000000 >>> 28", "x=9");
|
||||
|
||||
test("x = 0xffffffff << 0", "x=-1");
|
||||
test("x = 0xffffffff << 4", "x=-16");
|
||||
test("1 << 32", "1<<32");
|
||||
test("1 << -1", "1<<-1");
|
||||
test("1 >> 32", "1>>32");
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,72 +0,0 @@
|
||||
//! <https://github.com/google/closure-compiler/blob/master/test/com/google/javascript/jscomp/PeepholeSubstituteAlternateSyntaxTest.java>
|
||||
|
||||
use crate::CompressOptions;
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let options = CompressOptions::all_true();
|
||||
crate::test(source_text, expected, options);
|
||||
}
|
||||
|
||||
fn test_same(source_text: &str) {
|
||||
test(source_text, source_text);
|
||||
}
|
||||
|
||||
// Oxc
|
||||
|
||||
#[test]
|
||||
fn cjs() {
|
||||
// Bail `cjs-module-lexer`.
|
||||
// Export is undefined when `enumerable` is "!0".
|
||||
// https://github.com/nodejs/cjs-module-lexer/issues/64
|
||||
test_same(
|
||||
"Object.defineProperty(exports, 'ConnectableObservable', {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return ConnectableObservable_1.ConnectableObservable;
|
||||
}
|
||||
});",
|
||||
);
|
||||
// @babel/types/lib/index.js
|
||||
test_same(
|
||||
r#"Object.keys(_index6).forEach(function(key) {
|
||||
if (key === "default" || key === "__esModule") return;
|
||||
if (Object.prototype.hasOwnProperty.call(_exportNames, key)) return;
|
||||
if (key in exports && exports[key] === _index6[key]) return;
|
||||
Object.defineProperty(exports, key, {
|
||||
enumerable: true,
|
||||
get: function() {
|
||||
return _index6[key];
|
||||
}
|
||||
});
|
||||
});"#,
|
||||
);
|
||||
}
|
||||
|
||||
// Google Closure Compiler
|
||||
|
||||
#[test]
|
||||
fn fold_return_result() {
|
||||
test("function f(){return !1;}", "function f(){return !1}");
|
||||
test("function f(){return null;}", "function f(){return null}");
|
||||
test("function f(){return void 0;}", "function f(){return}");
|
||||
test("function f(){return void foo();}", "function f(){return void foo()}");
|
||||
test("function f(){return undefined;}", "function f(){return}");
|
||||
test("function f(){if(a()){return undefined;}}", "function f(){if(a())return}");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn undefined() {
|
||||
test("var x = undefined", "var x");
|
||||
test(
|
||||
"var undefined = 1;function f() {var undefined=2;var x;}",
|
||||
"var undefined=1;function f(){var undefined=2,x}",
|
||||
);
|
||||
test("function f(undefined) {}", "function f(undefined){}");
|
||||
test("try {} catch(undefined) {}", "try{}catch(undefined){}");
|
||||
test("for (undefined in {}) {}", "for(undefined in {}){}");
|
||||
test("undefined++", "undefined++");
|
||||
test("undefined += undefined", "undefined+=void 0");
|
||||
|
||||
// shadowd
|
||||
test_same("(function(undefined) { let x = typeof undefined; })()");
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
use oxc_minifier::CompressOptions;
|
||||
|
||||
fn test(source_text: &str, expected: &str) {
|
||||
let options = CompressOptions { remove_syntax: true, ..CompressOptions::all_false() };
|
||||
crate::test(source_text, expected, options);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parens() {
|
||||
test("(((x)))", "x");
|
||||
test("(((a + b))) * c", "(a + b) * c");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_console() {
|
||||
let options =
|
||||
CompressOptions { remove_syntax: true, drop_console: true, ..CompressOptions::all_false() };
|
||||
crate::test("console.log()", "", options);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn drop_debugger() {
|
||||
let options = CompressOptions {
|
||||
remove_syntax: true,
|
||||
drop_debugger: true,
|
||||
..CompressOptions::all_false()
|
||||
};
|
||||
crate::test("debugger", "", options);
|
||||
}
|
||||
@@ -12,10 +12,7 @@ pub(crate) fn test(source_text: &str, expected: &str, options: CompressOptions)
|
||||
let source_type = SourceType::default();
|
||||
let result = run(source_text, source_type, Some(options));
|
||||
let expected = run(expected, source_type, None);
|
||||
assert_eq!(
|
||||
result, expected,
|
||||
"\nfor source {source_text:?}\nexpect {expected:?}\ngot {result:?}"
|
||||
);
|
||||
assert_eq!(result, expected, "\nfor source\n{source_text}\nexpect\n{expected}\ngot\n{result}");
|
||||
}
|
||||
|
||||
fn run(source_text: &str, source_type: SourceType, options: Option<CompressOptions>) -> String {
|
||||
|
||||
Reference in new issue
Block a user