refactor(minifier): move tests to their src files (#5912)

This commit is contained in:
Boshen committed 2024-09-20 07:56:37 +00:00
1 parent cbaeea6d02
commit 943bd76679
20 files changed
+1969 -1982

No files matched your search

+1 -1
View File
@@ -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');
",
);
}
}
+1 -1
View File
@@ -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", "");
}
}
+19 -20
View File
@@ -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);
}
}
+3
View File
@@ -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;
+5 -26
View File
@@ -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() }
}
}
+41
View File
@@ -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)");
}
+29 -9
View File
@@ -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);
}
+1 -4
View File
@@ -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 {