if (tw.safe_ids[def.id]) {
if (def.fixed == null) {
if (is_arguments(def)) return false;
+ if (def.global && def.name == "arguments") return false;
def.fixed = make_node(AST_Undefined, def.orig);
}
return true;
return orig.length == 1 && orig[0] instanceof AST_SymbolLambda;
});
- function is_lhs_read_only(lhs) {
+ function is_lhs_read_only(lhs, compressor) {
if (lhs instanceof AST_This) return true;
- if (lhs instanceof AST_SymbolRef) return lhs.definition().orig[0] instanceof AST_SymbolLambda;
+ if (lhs instanceof AST_SymbolRef) {
+ var def = lhs.definition();
+ return def.orig[0] instanceof AST_SymbolLambda
+ || compressor.exposed(def) && identifier_atom[def.name];
+ }
if (lhs instanceof AST_PropAccess) {
lhs = lhs.expression;
if (lhs instanceof AST_SymbolRef) {
}
if (!lhs) return true;
if (lhs.is_constant()) return true;
- return is_lhs_read_only(lhs);
+ return is_lhs_read_only(lhs, compressor);
}
return false;
}
var stop_if_hit = null;
var lhs = get_lhs(candidate);
var side_effects = lhs && lhs.has_side_effects(compressor);
- var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs);
+ var scan_lhs = lhs && !side_effects && !is_lhs_read_only(lhs, compressor);
var scan_rhs = foldable(get_rhs(candidate));
if (!scan_lhs && !scan_rhs) continue;
// Locate symbols which may execute code outside of scanning range
-#! /usr/bin/env node
-
var assert = require("assert");
+var child_process = require("child_process");
var fs = require("fs");
var path = require("path");
var sandbox = require("./sandbox");
var semver = require("semver");
var U = require("./node");
-var failures = 0;
-var failed_files = Object.create(null);
-var minify_options = require("./ufuzz.json").map(JSON.stringify);
+var file = process.argv[2];
var dir = path.resolve(path.dirname(module.filename), "compress");
-fs.readdirSync(dir).filter(function(name) {
- return /\.js$/i.test(name);
-}).forEach(function(file) {
+if (file) {
+ var minify_options = require("./ufuzz.json").map(JSON.stringify);
log("--- {file}", { file: file });
var tests = parse_test(path.resolve(dir, file));
- for (var i in tests) if (!test_case(tests[i])) {
- failures++;
- failed_files[file] = 1;
- }
-});
-if (failures) {
- console.error();
- console.error("!!! Failed " + failures + " test case(s).");
- console.error("!!! " + Object.keys(failed_files).join(", "));
- process.exit(1);
+ process.exit(Object.keys(tests).filter(function(name) {
+ return !test_case(tests[name]);
+ }).length);
+} else {
+ var files = fs.readdirSync(dir).filter(function(name) {
+ return /\.js$/i.test(name);
+ });
+ var failures = 0;
+ var failed_files = Object.create(null);
+ (function next() {
+ var file = files.shift();
+ if (file) {
+ child_process.spawn(process.argv[0], [ process.argv[1], file ], {
+ stdio: [ "ignore", 1, 2 ]
+ }).on("exit", function(code) {
+ if (code) {
+ failures += code;
+ failed_files[file] = code;
+ }
+ next();
+ });
+ } else if (failures) {
+ console.error();
+ console.error("!!! Failed " + failures + " test case(s).");
+ console.error("!!! " + Object.keys(failed_files).join(", "));
+ process.exit(1);
+ }
+ })();
}
-/* -----[ utils ]----- */
-
function evaluate(code) {
if (code instanceof U.AST_Node) code = make_code(code, { beautify: true });
return new Function("return(" + code + ")")();
// Try to reminify original input with standard options
// to see if it matches expect_stdout.
-function reminify(orig_options, input_code, input_formatted, expect_stdout) {
+function reminify(orig_options, input_code, input_formatted, stdout) {
for (var i = 0; i < minify_options.length; i++) {
var options = JSON.parse(minify_options[i]);
if (options.compress) [
});
return false;
} else {
- var stdout = run_code(result.code);
- if (typeof expect_stdout != "string" && typeof stdout != "string" && expect_stdout.name == stdout.name) {
- stdout = expect_stdout;
+ var expected = stdout[options.toplevel ? 1 : 0];
+ var actual = run_code(result.code, options.toplevel);
+ if (typeof expected != "string" && typeof actual != "string" && expected.name == actual.name) {
+ actual = expected;
}
- if (!sandbox.same_stdout(expect_stdout, stdout)) {
+ if (!sandbox.same_stdout(expected, actual)) {
log([
"!!! failed running reminified input",
"---INPUT---",
input: input_formatted,
options: options_formatted,
output: result.code,
- expected_type: typeof expect_stdout == "string" ? "STDOUT" : "ERROR",
- expected: expect_stdout,
- actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
- actual: stdout,
+ expected_type: typeof expected == "string" ? "STDOUT" : "ERROR",
+ expected: expected,
+ actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
+ actual: actual,
});
return false;
}
return true;
}
-function run_code(code) {
- var result = sandbox.run_code(code, true);
+function run_code(code, toplevel) {
+ var result = sandbox.run_code(code, toplevel);
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
}
return false;
}
}
- if (test.expect_stdout
- && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
- var stdout = run_code(input_code);
+ if (test.expect_stdout && (!test.node_version || semver.satisfies(process.version, test.node_version))) {
+ var stdout = [ run_code(input_code), run_code(input_code, true) ];
+ var toplevel = test.options.toplevel;
+ var actual = stdout[toplevel ? 1 : 0];
if (test.expect_stdout === true) {
- test.expect_stdout = stdout;
+ test.expect_stdout = actual;
}
- if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
+ if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! Invalid input or expected stdout",
"---INPUT---",
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
- actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
- actual: stdout,
+ actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
+ actual: actual,
});
return false;
}
- stdout = run_code(output);
- if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
+ actual = run_code(output, toplevel);
+ if (!sandbox.same_stdout(test.expect_stdout, actual)) {
log([
"!!! failed",
"---INPUT---",
input: input_formatted,
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
expected: test.expect_stdout,
- actual_type: typeof stdout == "string" ? "STDOUT" : "ERROR",
- actual: stdout,
+ actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
+ actual: actual,
});
return false;
}
- if (!reminify(test.options, input_code, input_formatted, test.expect_stdout)) {
+ if (!reminify(test.options, input_code, input_formatted, stdout)) {
return false;
}
}
"object",
]
}
+
+Infinity_assignment: {
+ options = {
+ collapse_vars: true,
+ pure_getters: "strict",
+ unsafe: true,
+ }
+ input: {
+ var Infinity;
+ Infinity = 42;
+ console.log(Infinity);
+ }
+ expect: {
+ var Infinity;
+ Infinity = 42;
+ console.log(Infinity);
+ }
+ expect_stdout: true
+}
console.log(o);
}
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
- expect_stdout: "PASS"
+ expect_stdout: true
}
mangle_catch_redef_3_toplevel: {
console.log(o);
}
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
- expect_stdout: "PASS"
+ expect_stdout: true
}
-mangle_catch_redef_ie8_3: {
+mangle_catch_redef_3_ie8: {
mangle = {
ie8: true,
toplevel: false,
console.log(o);
}
expect_exact: 'var o="PASS";try{throw 0}catch(o){(function(){function c(){o="FAIL"}c(),c()})()}console.log(o);'
- expect_stdout: "PASS"
+ expect_stdout: true
}
mangle_catch_redef_3_ie8_toplevel: {
console.log(o);
}
expect_exact: 'var c="PASS";try{throw 0}catch(c){(function(){function o(){c="FAIL"}o(),o()})()}console.log(c);'
- expect_stdout: "PASS"
+ expect_stdout: true
}
"% %s",
]
}
+
+typeof_arguments: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var arguments;
+ console.log((typeof arguments).length);
+ }
+ expect: {
+ var arguments;
+ console.log((typeof arguments).length);
+ }
+ expect_stdout: "6"
+}
+
+typeof_arguments_assigned: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var arguments = void 0;
+ console.log((typeof arguments).length);
+ }
+ expect: {
+ console.log("undefined".length);
+ }
+ expect_stdout: "9"
+}
+
+toplevel_Infinity_NaN_undefined: {
+ options = {
+ evaluate: true,
+ reduce_vars: true,
+ toplevel: true,
+ unused: true,
+ }
+ input: {
+ var Infinity = "foo";
+ var NaN = 42;
+ var undefined = null;
+ console.log(Infinity, NaN, undefined);
+ }
+ expect: {
+ console.log("foo", 42, null);
+ }
+ expect_stdout: "foo 42 null"
+}
return ctx;
}
-var context;
-exports.run_code = function(code, reuse) {
+exports.run_code = function(code, toplevel) {
var stdout = "";
var original_write = process.stdout.write;
process.stdout.write = function(chunk) {
stdout += chunk;
};
try {
- if (!reuse || !context) context = createContext();
- vm.runInContext([
- "!function() {",
- code,
- "}();",
- ].join("\n"), context, { timeout: 5000 });
+ vm.runInContext(toplevel ? "(function(){" + code + "})()" : code, createContext(), { timeout: 5000 });
return stdout;
} catch (ex) {
return ex;
} finally {
process.stdout.write = original_write;
- if (!reuse || code.indexOf(".prototype") >= 0) {
- context = null;
- } else {
- vm.runInContext(Object.keys(context).map(function(name) {
- return "delete " + name;
- }).join("\n"), context);
- }
}
};
process.stderr.write("\n");
}
-function try_beautify(code, result, printfn) {
+function try_beautify(code, toplevel, result, printfn) {
var beautified = UglifyJS.minify(code, {
compress: false,
mangle: false,
if (beautified.error) {
printfn("// !!! beautify failed !!!");
printfn(beautified.error.stack);
- } else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
+ } else if (sandbox.same_stdout(sandbox.run_code(beautified.code, toplevel), result)) {
printfn("// (beautified)");
printfn(beautified.code);
return;
errorln("Error testing options." + component + "." + name);
errorln(result.error.stack);
} else {
- var r = sandbox.run_code(result.code);
+ var r = sandbox.run_code(result.code, m.toplevel);
return sandbox.same_stdout(original_result, r);
}
}
errorln("Error testing options.rename");
errorln(result.error.stack);
} else {
- var r = sandbox.run_code(result.code);
+ var r = sandbox.run_code(result.code, m.toplevel);
if (sandbox.same_stdout(original_result, r)) {
errorln("Suspicious options:");
errorln(" rename");
errorln("//=============================================================");
if (!ok) errorln("// !!!!!! Failed... round " + round);
errorln("// original code");
- try_beautify(original_code, original_result, errorln);
+ try_beautify(original_code, false, original_result, errorln);
errorln();
errorln();
errorln("//-------------------------------------------------------------");
+ options = JSON.parse(options);
if (typeof uglify_code == "string") {
errorln("// uglified code");
- try_beautify(uglify_code, uglify_result, errorln);
+ try_beautify(uglify_code, options.toplevel, uglify_result, errorln);
errorln();
errorln();
errorln("original result:");
- errorln(typeof original_result == "string" ? original_result : original_result.stack);
+ errorln(errored ? original_result.stack : original_result);
errorln("uglified result:");
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
} else {
errorln("// !!! uglify failed !!!");
errorln(uglify_code.stack);
- if (typeof original_result != "string") {
+ if (errored) {
errorln();
errorln();
errorln("original stacktrace:");
}
}
errorln("minify(options):");
- options = JSON.parse(options);
errorln(JSON.stringify(options, null, 2));
errorln();
if (!ok && typeof uglify_code == "string") {
mangle: false
}) ];
var minify_options = require("./ufuzz.json").map(JSON.stringify);
-var original_code, original_result;
+var original_code, original_result, errored;
var uglify_code, uglify_result, ok;
for (var round = 1; round <= num_iterations; round++) {
process.stdout.write(round + " of " + num_iterations + "\r");
original_code = createTopLevelCode();
- original_result = sandbox.run_code(original_code);
- (typeof original_result != "string" ? fallback_options : minify_options).forEach(function(options) {
- uglify_code = UglifyJS.minify(original_code, JSON.parse(options));
+ var orig_result = [ sandbox.run_code(original_code) ];
+ errored = typeof orig_result[0] != "string";
+ if (!errored) orig_result.push(sandbox.run_code(original_code, true));
+ (errored ? fallback_options : minify_options).forEach(function(options) {
+ var o = JSON.parse(options);
+ uglify_code = UglifyJS.minify(original_code, o);
+ original_result = orig_result[o.toplevel ? 1 : 0];
if (!uglify_code.error) {
uglify_code = uglify_code.code;
- uglify_result = sandbox.run_code(uglify_code);
+ uglify_result = sandbox.run_code(uglify_code, o.toplevel);
ok = sandbox.same_stdout(original_result, uglify_result);
} else {
uglify_code = uglify_code.error;
- if (typeof original_result != "string") {
+ if (errored) {
ok = uglify_code.name == original_result.name;
}
}
if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
- else if (typeof original_result != "string") {
+ else if (errored) {
println("//=============================================================");
println("// original code");
- try_beautify(original_code, original_result, println);
+ try_beautify(original_code, o.toplevel, original_result, println);
println();
println();
println("original result:");