Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a82003d6ac | ||
|
|
da9f1622fc | ||
|
|
8a4c7077bb | ||
|
|
0a63f2f2b0 | ||
|
|
931ac66638 | ||
|
|
35338a100f | ||
|
|
d57b606e73 | ||
|
|
00ada04111 | ||
|
|
a31c477fea | ||
|
|
bde7418ce1 | ||
|
|
70bb304a0a | ||
|
|
9d3b1efd86 | ||
|
|
482e1baea3 | ||
|
|
e4f5ba1d29 | ||
|
|
b9053c7a25 | ||
|
|
d357a7aabc | ||
|
|
ae77ebe5a5 | ||
|
|
04439edcec | ||
|
|
a246195412 | ||
|
|
8939a36bc7 | ||
|
|
a21c348d93 | ||
|
|
1f0def10eb | ||
|
|
f87caac9d8 | ||
|
|
d538a73250 | ||
|
|
2e4fbdeb08 | ||
|
|
3bc7cc82bb | ||
|
|
45fbdbc2dc | ||
|
|
54cb678055 | ||
|
|
e88c439eac | ||
|
|
9fc8cd4076 | ||
|
|
5476cb8f05 | ||
|
|
6a30e1d6be | ||
|
|
e4881245d9 | ||
|
|
354fec8a9c | ||
|
|
11cdab745d | ||
|
|
a89d424a0b |
55
.travis.yml
55
.travis.yml
@@ -1,35 +1,46 @@
|
||||
addons:
|
||||
apt:
|
||||
sources:
|
||||
- ubuntu-toolchain-r-test
|
||||
packages:
|
||||
- libstdc++-4.9-dev
|
||||
cache:
|
||||
directories: tmp
|
||||
language: generic
|
||||
matrix:
|
||||
fast_finish: true
|
||||
sudo: false
|
||||
env:
|
||||
global:
|
||||
- UGLIFYJS_TEST_ALL=1
|
||||
matrix:
|
||||
- NODEJS_VER=node/0.10
|
||||
- NODEJS_VER=node/0.12
|
||||
- NODEJS_VER=node/4
|
||||
- NODEJS_VER=node/6
|
||||
- NODEJS_VER=node/8
|
||||
- NODEJS_VER=node/10
|
||||
- NODEJS_VER=node/latest
|
||||
- NODE=0.10 TYPE=compress
|
||||
- NODE=0.10 TYPE=mocha
|
||||
- NODE=0.10 TYPE=release/benchmark
|
||||
- NODE=0.10 TYPE=release/jetstream
|
||||
- NODE=0.12 TYPE=compress
|
||||
- NODE=0.12 TYPE=mocha
|
||||
- NODE=0.12 TYPE=release/benchmark
|
||||
- NODE=0.12 TYPE=release/jetstream
|
||||
- NODE=4 TYPE=compress
|
||||
- NODE=4 TYPE=mocha
|
||||
- NODE=4 TYPE=release/benchmark
|
||||
- NODE=4 TYPE=release/jetstream
|
||||
- NODE=6 TYPE=compress
|
||||
- NODE=6 TYPE=mocha
|
||||
- NODE=6 TYPE=release/benchmark
|
||||
- NODE=6 TYPE=release/jetstream
|
||||
- NODE=8 TYPE=compress
|
||||
- NODE=8 TYPE=mocha
|
||||
- NODE=8 TYPE=release/benchmark
|
||||
- NODE=8 TYPE=release/jetstream
|
||||
- NODE=10 TYPE=compress
|
||||
- NODE=10 TYPE=mocha
|
||||
- NODE=10 TYPE=release/benchmark
|
||||
- NODE=10 TYPE=release/jetstream
|
||||
- NODE=latest TYPE=compress
|
||||
- NODE=latest TYPE=mocha
|
||||
- NODE=latest TYPE=release/benchmark
|
||||
- NODE=latest TYPE=release/jetstream
|
||||
before_install:
|
||||
- git clone --branch v1.4.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
|
||||
- git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git ~/.nvs
|
||||
- . ~/.nvs/nvs.sh
|
||||
- nvs --version
|
||||
install:
|
||||
- nvs add $NODEJS_VER
|
||||
- nvs use $NODEJS_VER
|
||||
- nvs add node/$NODE
|
||||
- nvs use node/$NODE
|
||||
- node --version
|
||||
- npm --version --no-update-notifier
|
||||
- npm install --no-optional --no-save --no-update-notifier
|
||||
- npm install --no-audit --no-optional --no-save --no-update-notifier
|
||||
script:
|
||||
- npm test --no-update-notifier
|
||||
- node test/$TYPE
|
||||
|
||||
2
LICENSE
2
LICENSE
@@ -1,6 +1,6 @@
|
||||
UglifyJS is released under the BSD license:
|
||||
|
||||
Copyright 2012-2018 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
Copyright 2012-2019 (c) Mihai Bazon <mihai.bazon@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions
|
||||
|
||||
@@ -664,8 +664,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
|
||||
- `join_vars` (default: `true`) -- join consecutive `var` statements
|
||||
|
||||
- `keep_fargs` (default: `true`) -- Prevents the compressor from discarding unused
|
||||
function arguments. You need this for code which relies on `Function.length`.
|
||||
- `keep_fargs` (default: `strict`) -- Discard unused function arguments. Code
|
||||
which relies on `Function.length` will break if this is done indiscriminately,
|
||||
i.e. when passing `true`. Pass `false` to always retain function arguments.
|
||||
|
||||
- `keep_fnames` (default: `false`) -- Pass `true` to prevent the
|
||||
compressor from discarding function names. Useful for code relying on
|
||||
@@ -767,9 +768,6 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
|
||||
- `unused` (default: `true`) -- drop unreferenced functions and variables (simple
|
||||
direct variable assignments do not count as references unless set to `"keep_assign"`)
|
||||
|
||||
- `warnings` (default: `false`) -- display warnings when dropping unreachable
|
||||
code or unused declarations etc.
|
||||
|
||||
## Mangle options
|
||||
|
||||
- `eval` (default `false`) -- Pass `true` to mangle names visible in scopes
|
||||
|
||||
88
appveyor.yml
88
appveyor.yml
@@ -1,26 +1,74 @@
|
||||
environment:
|
||||
UGLIFYJS_TEST_ALL: 1
|
||||
matrix:
|
||||
- NODEJS_VER: node/0.10
|
||||
- NODEJS_VER: node/0.12
|
||||
- NODEJS_VER: node/4
|
||||
- NODEJS_VER: node/6
|
||||
- NODEJS_VER: node/8
|
||||
- NODEJS_VER: node/10
|
||||
- NODEJS_VER: node/latest
|
||||
install:
|
||||
- git clone --branch v1.4.2 --depth 1 https://github.com/jasongin/nvs.git %LOCALAPPDATA%\nvs
|
||||
- set PATH=%LOCALAPPDATA%\nvs;%PATH%
|
||||
- nvs --version
|
||||
- nvs add %NODEJS_VER%
|
||||
- nvs use %NODEJS_VER%
|
||||
- node --version
|
||||
- npm --version --no-update-notifier
|
||||
- npm install --no-optional --no-save --no-update-notifier
|
||||
build: off
|
||||
cache:
|
||||
- tmp
|
||||
matrix:
|
||||
fast_finish: true
|
||||
environment:
|
||||
matrix:
|
||||
- NODE: 0.10
|
||||
TYPE: compress
|
||||
- NODE: 0.10
|
||||
TYPE: mocha
|
||||
- NODE: 0.10
|
||||
TYPE: release/benchmark
|
||||
- NODE: 0.10
|
||||
TYPE: release/jetstream
|
||||
- NODE: 0.12
|
||||
TYPE: compress
|
||||
- NODE: 0.12
|
||||
TYPE: mocha
|
||||
- NODE: 0.12
|
||||
TYPE: release/benchmark
|
||||
- NODE: 0.12
|
||||
TYPE: release/jetstream
|
||||
- NODE: 4
|
||||
TYPE: compress
|
||||
- NODE: 4
|
||||
TYPE: mocha
|
||||
- NODE: 4
|
||||
TYPE: release/benchmark
|
||||
- NODE: 4
|
||||
TYPE: release/jetstream
|
||||
- NODE: 6
|
||||
TYPE: compress
|
||||
- NODE: 6
|
||||
TYPE: mocha
|
||||
- NODE: 6
|
||||
TYPE: release/benchmark
|
||||
- NODE: 6
|
||||
TYPE: release/jetstream
|
||||
- NODE: 8
|
||||
TYPE: compress
|
||||
- NODE: 8
|
||||
TYPE: mocha
|
||||
- NODE: 8
|
||||
TYPE: release/benchmark
|
||||
- NODE: 8
|
||||
TYPE: release/jetstream
|
||||
- NODE: 10
|
||||
TYPE: compress
|
||||
- NODE: 10
|
||||
TYPE: mocha
|
||||
- NODE: 10
|
||||
TYPE: release/benchmark
|
||||
- NODE: 10
|
||||
TYPE: release/jetstream
|
||||
- NODE: latest
|
||||
TYPE: compress
|
||||
- NODE: latest
|
||||
TYPE: mocha
|
||||
- NODE: latest
|
||||
TYPE: release/benchmark
|
||||
- NODE: latest
|
||||
TYPE: release/jetstream
|
||||
install:
|
||||
- git clone --branch v1.5.2 --depth 1 https://github.com/jasongin/nvs.git %LOCALAPPDATA%\nvs
|
||||
- set PATH=%LOCALAPPDATA%\nvs;%PATH%
|
||||
- nvs --version
|
||||
- nvs add node/%NODE%
|
||||
- nvs use node/%NODE%
|
||||
- node --version
|
||||
- npm --version --no-update-notifier
|
||||
- npm install --no-audit --no-optional --no-save --no-update-notifier
|
||||
test_script:
|
||||
- npm test --no-update-notifier
|
||||
- node test/%TYPE%
|
||||
|
||||
@@ -215,8 +215,10 @@ function run() {
|
||||
var ex = result.error;
|
||||
if (ex.name == "SyntaxError") {
|
||||
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
|
||||
var file = files[ex.filename];
|
||||
if (file) {
|
||||
var col = ex.col;
|
||||
var lines = files[ex.filename].split(/\r?\n/);
|
||||
var lines = file.split(/\r?\n/);
|
||||
var line = lines[ex.line - 1];
|
||||
if (!line && !col) {
|
||||
line = lines[ex.line - 2];
|
||||
@@ -232,7 +234,7 @@ function run() {
|
||||
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
|
||||
}
|
||||
}
|
||||
if (ex.defs) {
|
||||
} else if (ex.defs) {
|
||||
print_error("Supported options:");
|
||||
print_error(format_object(ex.defs));
|
||||
}
|
||||
|
||||
37
lib/ast.js
37
lib/ast.js
@@ -337,18 +337,25 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
$propdoc: {
|
||||
globals: "[Object/S] a map of name -> SymbolDef for all undeclared names",
|
||||
},
|
||||
wrap_commonjs: function(name) {
|
||||
wrap: function(name) {
|
||||
var body = this.body;
|
||||
var wrapped_tl = "(function(exports){'$ORIG';})(typeof " + name + "=='undefined'?(" + name + "={}):" + name + ");";
|
||||
wrapped_tl = parse(wrapped_tl);
|
||||
wrapped_tl = wrapped_tl.transform(new TreeTransformer(function(node) {
|
||||
return parse([
|
||||
"(function(exports){'$ORIG';})(typeof ",
|
||||
name,
|
||||
"=='undefined'?(",
|
||||
name,
|
||||
"={}):",
|
||||
name,
|
||||
");"
|
||||
].join(""), {
|
||||
filename: "wrap=" + JSON.stringify(name)
|
||||
}).transform(new TreeTransformer(function(node) {
|
||||
if (node instanceof AST_Directive && node.value == "$ORIG") {
|
||||
return MAP.splice(body);
|
||||
}
|
||||
}));
|
||||
return wrapped_tl;
|
||||
},
|
||||
wrap_enclose: function(args_values) {
|
||||
enclose: function(args_values) {
|
||||
if (typeof args_values != "string") args_values = "";
|
||||
var index = args_values.indexOf(":");
|
||||
if (index < 0) index = args_values.length;
|
||||
@@ -359,7 +366,9 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
'){"$ORIG"})(',
|
||||
args_values.slice(index + 1),
|
||||
")"
|
||||
].join("")).transform(new TreeTransformer(function(node) {
|
||||
].join(""), {
|
||||
filename: "enclose=" + JSON.stringify(args_values)
|
||||
}).transform(new TreeTransformer(function(node) {
|
||||
if (node instanceof AST_Directive && node.value == "$ORIG") {
|
||||
return MAP.splice(body);
|
||||
}
|
||||
@@ -367,7 +376,7 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
}
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments", {
|
||||
var AST_Lambda = DEFNODE("Lambda", "name argnames uses_arguments length_read", {
|
||||
$documentation: "Base class for functions",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolDeclaration?] the name of this function",
|
||||
@@ -605,6 +614,18 @@ var AST_PropAccess = DEFNODE("PropAccess", "expression property", {
|
||||
$propdoc: {
|
||||
expression: "[AST_Node] the “container” expression",
|
||||
property: "[AST_Node|string] the property to access. For AST_Dot this is always a plain string, while for AST_Sub it's an arbitrary AST_Node"
|
||||
},
|
||||
getProperty: function() {
|
||||
var p = this.property;
|
||||
if (p instanceof AST_Constant) {
|
||||
return p.getValue();
|
||||
}
|
||||
if (p instanceof AST_UnaryPrefix
|
||||
&& p.operator == "void"
|
||||
&& p.expression instanceof AST_Constant) {
|
||||
return;
|
||||
}
|
||||
return p;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
368
lib/compress.js
368
lib/compress.js
@@ -69,7 +69,7 @@ function Compressor(options, false_by_default) {
|
||||
if_return : !false_by_default,
|
||||
inline : !false_by_default,
|
||||
join_vars : !false_by_default,
|
||||
keep_fargs : true,
|
||||
keep_fargs : false_by_default || "strict",
|
||||
keep_fnames : false,
|
||||
keep_infinity : false,
|
||||
loops : !false_by_default,
|
||||
@@ -104,6 +104,17 @@ function Compressor(options, false_by_default) {
|
||||
}
|
||||
}
|
||||
if (this.options["inline"] === true) this.options["inline"] = 3;
|
||||
var keep_fargs = this.options["keep_fargs"];
|
||||
this.drop_fargs = keep_fargs == "strict" ? function(lambda, parent) {
|
||||
if (lambda.length_read) return false;
|
||||
var name = lambda.name;
|
||||
if (!name) return parent && parent.TYPE == "Call" && parent.expression === lambda;
|
||||
if (name.fixed_value() !== lambda) return false;
|
||||
var def = name.definition();
|
||||
if (def.direct_access) return false;
|
||||
var escaped = def.escaped;
|
||||
return escaped && escaped.depth != 1;
|
||||
} : keep_fargs ? return_false : return_true;
|
||||
var pure_funcs = this.options["pure_funcs"];
|
||||
if (typeof pure_funcs == "function") {
|
||||
this.pure_funcs = pure_funcs;
|
||||
@@ -118,6 +129,8 @@ function Compressor(options, false_by_default) {
|
||||
} else {
|
||||
this.pure_funcs = return_true;
|
||||
}
|
||||
var sequences = this.options["sequences"];
|
||||
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
||||
var top_retain = this.options["top_retain"];
|
||||
if (top_retain instanceof RegExp) {
|
||||
this.top_retain = function(def) {
|
||||
@@ -141,8 +154,6 @@ function Compressor(options, false_by_default) {
|
||||
funcs: toplevel,
|
||||
vars: toplevel
|
||||
};
|
||||
var sequences = this.options["sequences"];
|
||||
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
||||
}
|
||||
|
||||
Compressor.prototype = new TreeTransformer;
|
||||
@@ -272,14 +283,19 @@ merge(Compressor.prototype, {
|
||||
self.transform(tt);
|
||||
});
|
||||
|
||||
function read_property(obj, key) {
|
||||
key = get_value(key);
|
||||
function read_property(obj, node) {
|
||||
var key = node.getProperty();
|
||||
if (key instanceof AST_Node) return;
|
||||
var value;
|
||||
if (obj instanceof AST_Array) {
|
||||
var elements = obj.elements;
|
||||
if (key == "length") return make_node_from_constant(elements.length, obj);
|
||||
if (typeof key == "number" && key in elements) value = elements[key];
|
||||
} else if (obj instanceof AST_Lambda) {
|
||||
if (key == "length") {
|
||||
obj.length_read = true;
|
||||
return make_node_from_constant(obj.argnames.length, obj);
|
||||
}
|
||||
} else if (obj instanceof AST_Object) {
|
||||
key = "" + key;
|
||||
var props = obj.properties;
|
||||
@@ -326,11 +342,17 @@ merge(Compressor.prototype, {
|
||||
return is_modified(compressor, tw, obj, obj, level + 2);
|
||||
}
|
||||
if (parent instanceof AST_PropAccess && parent.expression === node) {
|
||||
var prop = read_property(value, parent.property);
|
||||
var prop = read_property(value, parent);
|
||||
return !immutable && is_modified(compressor, tw, parent, prop, level + 1);
|
||||
}
|
||||
}
|
||||
|
||||
function is_arguments(def) {
|
||||
if (def.name != "arguments") return false;
|
||||
var orig = def.orig;
|
||||
return orig.length == 1 && orig[0] instanceof AST_SymbolFunarg;
|
||||
}
|
||||
|
||||
(function(def) {
|
||||
def(AST_Node, noop);
|
||||
|
||||
@@ -428,9 +450,9 @@ merge(Compressor.prototype, {
|
||||
if (def.single_use == "m") return false;
|
||||
if (tw.safe_ids[def.id]) {
|
||||
if (def.fixed == null) {
|
||||
var orig = def.orig[0];
|
||||
if (orig instanceof AST_SymbolFunarg || orig.name == "arguments") return false;
|
||||
def.fixed = make_node(AST_Undefined, orig);
|
||||
if (is_arguments(def)) return false;
|
||||
if (def.global && def.name == "arguments") return false;
|
||||
def.fixed = make_node(AST_Undefined, def.orig);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -490,23 +512,24 @@ merge(Compressor.prototype, {
|
||||
var obj = tw.parent(level + 1);
|
||||
mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
|
||||
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
|
||||
value = read_property(value, parent.property);
|
||||
value = read_property(value, parent);
|
||||
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
|
||||
if (value) return;
|
||||
}
|
||||
if (level > 0) return;
|
||||
if (parent instanceof AST_Call && node === parent.expression) return;
|
||||
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
|
||||
if (parent instanceof AST_SimpleStatement) return;
|
||||
if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
|
||||
d.direct_access = true;
|
||||
}
|
||||
|
||||
function mark_assignment_to_arguments(node) {
|
||||
if (!(node instanceof AST_Sub)) return;
|
||||
var expr = node.expression;
|
||||
var prop = node.property;
|
||||
if (expr instanceof AST_SymbolRef && expr.name == "arguments" && prop instanceof AST_Number) {
|
||||
expr.definition().reassigned = true;
|
||||
}
|
||||
if (!(expr instanceof AST_SymbolRef)) return;
|
||||
var def = expr.definition();
|
||||
if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
|
||||
}
|
||||
|
||||
var suppressor = new TreeWalker(function(node) {
|
||||
@@ -773,7 +796,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_Unary, function(tw, descend) {
|
||||
var node = this;
|
||||
if (node.operator != "++" && node.operator != "--") return;
|
||||
if (!unary_arithmetic[node.operator]) return;
|
||||
var exp = node.expression;
|
||||
if (!(exp instanceof AST_SymbolRef)) {
|
||||
mark_assignment_to_arguments(exp);
|
||||
@@ -869,9 +892,13 @@ merge(Compressor.prototype, {
|
||||
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) {
|
||||
@@ -880,7 +907,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
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;
|
||||
}
|
||||
@@ -1195,7 +1222,7 @@ merge(Compressor.prototype, {
|
||||
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
|
||||
@@ -1266,6 +1293,7 @@ merge(Compressor.prototype, {
|
||||
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
|
||||
}
|
||||
if (node instanceof AST_Debugger) return true;
|
||||
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
|
||||
if (node instanceof AST_IterationStatement) return !(node instanceof AST_For);
|
||||
if (node instanceof AST_LoopControl) return true;
|
||||
if (node instanceof AST_Try) return true;
|
||||
@@ -1419,7 +1447,7 @@ merge(Compressor.prototype, {
|
||||
extract_candidates(expr.expression);
|
||||
expr.body.forEach(extract_candidates);
|
||||
} else if (expr instanceof AST_Unary) {
|
||||
if (expr.operator == "++" || expr.operator == "--") {
|
||||
if (unary_arithmetic[expr.operator]) {
|
||||
candidates.push(hit_stack.slice());
|
||||
} else {
|
||||
extract_candidates(expr.expression);
|
||||
@@ -1489,9 +1517,9 @@ merge(Compressor.prototype, {
|
||||
function mangleable_var(var_def) {
|
||||
var value = var_def.value;
|
||||
if (!(value instanceof AST_SymbolRef)) return;
|
||||
if (value.name == "arguments") return;
|
||||
var def = value.definition();
|
||||
if (def.undeclared) return;
|
||||
if (is_arguments(def)) return;
|
||||
return value_def = def;
|
||||
}
|
||||
|
||||
@@ -2217,18 +2245,6 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
}
|
||||
|
||||
function get_value(key) {
|
||||
if (key instanceof AST_Constant) {
|
||||
return key.getValue();
|
||||
}
|
||||
if (key instanceof AST_UnaryPrefix
|
||||
&& key.operator == "void"
|
||||
&& key.expression instanceof AST_Constant) {
|
||||
return;
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
function is_undefined(node, compressor) {
|
||||
return node.is_undefined
|
||||
|| node instanceof AST_Undefined
|
||||
@@ -2267,8 +2283,7 @@ merge(Compressor.prototype, {
|
||||
// returns true if this node may be null, undefined or contain `AST_Accessor`
|
||||
(function(def) {
|
||||
AST_Node.DEFMETHOD("may_throw_on_access", function(compressor) {
|
||||
return !compressor.option("pure_getters")
|
||||
|| this._dot_throw(compressor);
|
||||
return !compressor.option("pure_getters") || this._dot_throw(compressor);
|
||||
});
|
||||
function is_strict(compressor) {
|
||||
return /strict/.test(compressor.option("pure_getters"));
|
||||
@@ -2276,7 +2291,15 @@ merge(Compressor.prototype, {
|
||||
def(AST_Node, is_strict);
|
||||
def(AST_Array, return_false);
|
||||
def(AST_Assign, function(compressor) {
|
||||
return this.operator == "=" && this.right._dot_throw(compressor);
|
||||
if (this.operator != "=") return false;
|
||||
var rhs = this.right;
|
||||
if (!rhs._dot_throw(compressor)) return false;
|
||||
var sym = this.left;
|
||||
if (!(sym instanceof AST_SymbolRef)) return true;
|
||||
if (rhs instanceof AST_Binary && rhs.operator == "||" && sym.name == rhs.left.name) {
|
||||
return rhs.right._dot_throw(compressor);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
def(AST_Binary, function(compressor) {
|
||||
return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
|
||||
@@ -2307,6 +2330,7 @@ merge(Compressor.prototype, {
|
||||
if (!is_strict(compressor)) return false;
|
||||
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
|
||||
if (this.is_immutable()) return false;
|
||||
if (is_arguments(this.definition())) return false;
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed) return true;
|
||||
this._dot_throw = return_true;
|
||||
@@ -2334,7 +2358,7 @@ merge(Compressor.prototype, {
|
||||
case "&&":
|
||||
return this.left.is_defined(compressor) && this.right.is_defined(compressor);
|
||||
case "||":
|
||||
return this.left.is_defined(compressor) || this.right.is_defined(compressor);
|
||||
return this.left.is_truthy() || this.right.is_defined(compressor);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
@@ -2354,7 +2378,7 @@ merge(Compressor.prototype, {
|
||||
if (this.is_immutable()) return true;
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed) return false;
|
||||
this.is_defined = return_true;
|
||||
this.is_defined = return_false;
|
||||
var result = fixed.is_defined(compressor);
|
||||
delete this.is_defined;
|
||||
return result;
|
||||
@@ -2549,6 +2573,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
var lazy_op = makePredicate("&& ||");
|
||||
var unary_arithmetic = makePredicate("++ --");
|
||||
var unary_side_effects = makePredicate("delete ++ --");
|
||||
|
||||
function is_lhs(node, parent) {
|
||||
@@ -3375,16 +3400,28 @@ merge(Compressor.prototype, {
|
||||
def(AST_Lambda, function(scope) {
|
||||
var self = this;
|
||||
var result = true;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
var inner_scopes = [];
|
||||
self.walk(new TreeWalker(function(node, descend) {
|
||||
if (!result) return true;
|
||||
if (node instanceof AST_Catch) {
|
||||
inner_scopes.push(node.argname.scope);
|
||||
descend();
|
||||
inner_scopes.pop();
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Scope && node !== self) {
|
||||
inner_scopes.push(node);
|
||||
descend();
|
||||
inner_scopes.pop();
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
if (self.inlined) {
|
||||
result = false;
|
||||
return true;
|
||||
}
|
||||
var def = node.definition();
|
||||
if (member(def, self.enclosed)
|
||||
&& !self.variables.has(def.name)) {
|
||||
if (!self.variables.has(def.name) && !member(def.scope, inner_scopes)) {
|
||||
if (scope) {
|
||||
var scope_def = scope.find_variable(node);
|
||||
if (def.undeclared ? !scope_def : scope_def === def) {
|
||||
@@ -3601,7 +3638,7 @@ merge(Compressor.prototype, {
|
||||
var value = null;
|
||||
if (node instanceof AST_Assign) {
|
||||
if (!in_use || node.left === sym && def.id in fixed_ids && fixed_ids[def.id] !== node) {
|
||||
value = node.right;
|
||||
value = get_rhs(node);
|
||||
}
|
||||
} else if (!in_use) {
|
||||
value = make_node(AST_Number, node, {
|
||||
@@ -3621,7 +3658,7 @@ merge(Compressor.prototype, {
|
||||
node.name = null;
|
||||
}
|
||||
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
|
||||
var trim = !compressor.option("keep_fargs");
|
||||
var trim = compressor.drop_fargs(node, parent);
|
||||
for (var a = node.argnames, i = a.length; --i >= 0;) {
|
||||
var sym = a[i];
|
||||
if (!(sym.definition().id in in_use_ids)) {
|
||||
@@ -3682,6 +3719,7 @@ merge(Compressor.prototype, {
|
||||
} else if (compressor.option("functions")
|
||||
&& def.value === def.name.fixed_value()
|
||||
&& def.value instanceof AST_Function
|
||||
&& !(def.value.name && def.value.name.definition().assignments)
|
||||
&& can_rename(def.value, def.name.name)
|
||||
&& (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
|
||||
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
||||
@@ -3811,6 +3849,7 @@ merge(Compressor.prototype, {
|
||||
};
|
||||
}
|
||||
});
|
||||
tt.push(compressor.parent());
|
||||
self.transform(tt);
|
||||
|
||||
function verify_safe_usage(def, read, modified) {
|
||||
@@ -3824,6 +3863,15 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
function get_rhs(assign) {
|
||||
var rhs = assign.right;
|
||||
if (!assign.write_only) return rhs;
|
||||
if (!(rhs instanceof AST_Binary && lazy_op[rhs.operator])) return rhs;
|
||||
var sym = assign.left;
|
||||
if (!(sym instanceof AST_SymbolRef) || sym.name != rhs.left.name) return rhs;
|
||||
return rhs.right.has_side_effects(compressor) ? rhs : rhs.right;
|
||||
}
|
||||
|
||||
function scan_ref_scoped(node, descend) {
|
||||
var node_def, props = [], sym = assign_as_unused(node, props);
|
||||
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
|
||||
@@ -3831,9 +3879,10 @@ merge(Compressor.prototype, {
|
||||
prop.walk(tw);
|
||||
});
|
||||
if (node instanceof AST_Assign) {
|
||||
node.right.walk(tw);
|
||||
var right = get_rhs(node);
|
||||
right.walk(tw);
|
||||
if (node.left === sym) {
|
||||
if (!node_def.chained && sym.fixed_value(true) === node.right) {
|
||||
if (!node_def.chained && sym.fixed_value(true) === right) {
|
||||
fixed_ids[node_def.id] = node;
|
||||
}
|
||||
if (!node.write_only) {
|
||||
@@ -4019,10 +4068,11 @@ merge(Compressor.prototype, {
|
||||
var top_retain = self instanceof AST_Toplevel && compressor.top_retain || return_false;
|
||||
var defs_by_id = Object.create(null);
|
||||
self.transform(new TreeTransformer(function(node, descend) {
|
||||
if (node instanceof AST_Assign
|
||||
&& node.operator == "="
|
||||
&& node.write_only
|
||||
&& can_hoist(node.left, node.right, 1)) {
|
||||
if (node instanceof AST_Assign) {
|
||||
if (node.operator != "=") return;
|
||||
if (!node.write_only) return;
|
||||
if (node.left.scope !== self) return;
|
||||
if (!can_hoist(node.left, node.right, 1)) return;
|
||||
descend(node, this);
|
||||
var defs = new Dictionary();
|
||||
var assignments = [];
|
||||
@@ -4051,7 +4101,9 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
return make_sequence(node, assignments);
|
||||
}
|
||||
if (node instanceof AST_VarDef && can_hoist(node.name, node.value, 0)) {
|
||||
if (node instanceof AST_Scope) return node === self ? undefined : node;
|
||||
if (node instanceof AST_VarDef) {
|
||||
if (!can_hoist(node.name, node.value, 0)) return;
|
||||
descend(node, this);
|
||||
var defs = new Dictionary();
|
||||
var var_defs = [];
|
||||
@@ -4064,32 +4116,6 @@ merge(Compressor.prototype, {
|
||||
defs_by_id[node.name.definition().id] = defs;
|
||||
return MAP.splice(var_defs);
|
||||
}
|
||||
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
|
||||
var defs = defs_by_id[node.expression.definition().id];
|
||||
if (defs) {
|
||||
var def = defs.get(get_value(node.property));
|
||||
var sym = make_node(AST_SymbolRef, node, {
|
||||
name: def.name,
|
||||
scope: node.expression.scope,
|
||||
thedef: def
|
||||
});
|
||||
sym.reference({});
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
|
||||
function can_hoist(sym, right, count) {
|
||||
if (sym.scope !== self) return;
|
||||
var def = sym.definition();
|
||||
if (def.assignments != count) return;
|
||||
if (def.direct_access) return;
|
||||
if (def.escaped.depth == 1) return;
|
||||
if (def.references.length == count) return;
|
||||
if (def.single_use) return;
|
||||
if (top_retain(def)) return;
|
||||
if (sym.fixed_value() !== right) return;
|
||||
return right instanceof AST_Object;
|
||||
}
|
||||
|
||||
function make_sym(sym, key) {
|
||||
var new_var = make_node(AST_SymbolVar, sym, {
|
||||
@@ -4102,6 +4128,43 @@ merge(Compressor.prototype, {
|
||||
return new_var;
|
||||
}
|
||||
}));
|
||||
self.transform(new TreeTransformer(function(node, descend) {
|
||||
if (node instanceof AST_PropAccess) {
|
||||
if (!(node.expression instanceof AST_SymbolRef)) return;
|
||||
var defs = defs_by_id[node.expression.definition().id];
|
||||
if (!defs) return;
|
||||
var def = defs.get(node.getProperty());
|
||||
var sym = make_node(AST_SymbolRef, node, {
|
||||
name: def.name,
|
||||
scope: node.expression.scope,
|
||||
thedef: def
|
||||
});
|
||||
sym.reference({});
|
||||
return sym;
|
||||
}
|
||||
if (node instanceof AST_Unary) {
|
||||
if (unary_side_effects[node.operator]) return;
|
||||
if (!(node.expression instanceof AST_SymbolRef)) return;
|
||||
if (!(node.expression.definition().id in defs_by_id)) return;
|
||||
var opt = node.clone();
|
||||
opt.expression = make_node(AST_Object, node, {
|
||||
properties: []
|
||||
});
|
||||
return opt;
|
||||
}
|
||||
}));
|
||||
|
||||
function can_hoist(sym, right, count) {
|
||||
var def = sym.definition();
|
||||
if (def.assignments != count) return;
|
||||
if (def.direct_access) return;
|
||||
if (def.escaped.depth == 1) return;
|
||||
if (def.references.length == count) return;
|
||||
if (def.single_use) return;
|
||||
if (top_retain(def)) return;
|
||||
if (sym.fixed_value() !== right) return;
|
||||
return right instanceof AST_Object;
|
||||
}
|
||||
});
|
||||
|
||||
// drop_side_effect_free()
|
||||
@@ -4134,12 +4197,14 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_Assign, function(compressor) {
|
||||
var left = this.left;
|
||||
if (left.has_side_effects(compressor)
|
||||
|| compressor.has_directive("use strict")
|
||||
&& left instanceof AST_PropAccess
|
||||
&& left.expression.is_constant()) {
|
||||
return this;
|
||||
if (left instanceof AST_PropAccess) {
|
||||
var expr = left.expression;
|
||||
if (expr instanceof AST_Assign && !expr.may_throw_on_access(compressor)) {
|
||||
expr.write_only = true;
|
||||
}
|
||||
if (compressor.has_directive("use strict") && expr.is_constant()) return this;
|
||||
}
|
||||
if (left.has_side_effects(compressor)) return this;
|
||||
this.write_only = true;
|
||||
if (root_expr(left).is_constant_expression(compressor.find_parent(AST_Scope))) {
|
||||
return this.right.drop_side_effect_free(compressor);
|
||||
@@ -4214,8 +4279,9 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_Constant, return_null);
|
||||
def(AST_Dot, function(compressor, first_in_statement) {
|
||||
if (this.expression.may_throw_on_access(compressor)) return this;
|
||||
return this.expression.drop_side_effect_free(compressor, first_in_statement);
|
||||
var expr = this.expression;
|
||||
if (expr.may_throw_on_access(compressor)) return this;
|
||||
return expr.drop_side_effect_free(compressor, first_in_statement);
|
||||
});
|
||||
def(AST_Function, function(compressor) {
|
||||
return this.name && compressor.option("ie8") ? this : null;
|
||||
@@ -4405,8 +4471,9 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_For, function(self, compressor) {
|
||||
if (!compressor.option("loops")) return self;
|
||||
if (compressor.option("side_effects") && self.init) {
|
||||
self.init = self.init.drop_side_effect_free(compressor);
|
||||
if (compressor.option("side_effects")) {
|
||||
if (self.init) self.init = self.init.drop_side_effect_free(compressor);
|
||||
if (self.step) self.step = self.step.drop_side_effect_free(compressor);
|
||||
}
|
||||
if (self.condition) {
|
||||
var cond = self.condition.evaluate(compressor);
|
||||
@@ -5170,25 +5237,26 @@ merge(Compressor.prototype, {
|
||||
return return_value(stat);
|
||||
}
|
||||
|
||||
function var_exists(catches, name) {
|
||||
return catches[name] || identifier_atom[name] || scope.var_names()[name];
|
||||
function var_exists(defined, name) {
|
||||
return defined[name] || identifier_atom[name] || scope.var_names()[name];
|
||||
}
|
||||
|
||||
function can_inject_args(catches, safe_to_inject) {
|
||||
function can_inject_args(catches, used, safe_to_inject) {
|
||||
for (var i = 0; i < fn.argnames.length; i++) {
|
||||
var arg = fn.argnames[i];
|
||||
if (arg.__unused) continue;
|
||||
if (!safe_to_inject || var_exists(catches, arg.name)) return false;
|
||||
used[arg.name] = true;
|
||||
if (in_loop) in_loop.push(arg.definition());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function can_inject_vars(catches, safe_to_inject) {
|
||||
function can_inject_vars(catches, used, safe_to_inject) {
|
||||
for (var i = 0; i < fn.body.length; i++) {
|
||||
var stat = fn.body[i];
|
||||
if (stat instanceof AST_Defun) {
|
||||
if (!safe_to_inject || var_exists(catches, stat.name.name)) return false;
|
||||
if (!safe_to_inject || var_exists(used, stat.name.name)) return false;
|
||||
continue;
|
||||
}
|
||||
if (!(stat instanceof AST_Var)) continue;
|
||||
@@ -5217,8 +5285,9 @@ merge(Compressor.prototype, {
|
||||
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
|
||||
&& (exp !== fn || fn.parent_scope === compressor.find_parent(AST_Scope));
|
||||
var inline = compressor.option("inline");
|
||||
if (!can_inject_vars(catches, inline >= 3 && safe_to_inject)) return false;
|
||||
if (!can_inject_args(catches, inline >= 2 && safe_to_inject)) return false;
|
||||
var used = Object.create(catches);
|
||||
if (!can_inject_args(catches, used, inline >= 2 && safe_to_inject)) return false;
|
||||
if (!can_inject_vars(catches, used, inline >= 3 && safe_to_inject)) return false;
|
||||
return !in_loop || in_loop.length == 0 || !is_reachable(fn, in_loop);
|
||||
}
|
||||
|
||||
@@ -5394,7 +5463,8 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_UnaryPrefix, function(self, compressor) {
|
||||
var e = self.expression;
|
||||
if (self.operator == "delete"
|
||||
if (compressor.option("evaluate")
|
||||
&& self.operator == "delete"
|
||||
&& !(e instanceof AST_SymbolRef
|
||||
|| e instanceof AST_PropAccess
|
||||
|| is_identifier_atom(e))) {
|
||||
@@ -5525,20 +5595,29 @@ merge(Compressor.prototype, {
|
||||
self.right = tmp;
|
||||
}
|
||||
}
|
||||
if (commutativeOperators[self.operator]) {
|
||||
if (self.right.is_constant()
|
||||
&& !self.left.is_constant()) {
|
||||
if (commutativeOperators[self.operator] && self.right.is_constant() && !self.left.is_constant()) {
|
||||
// if right is a constant, whatever side effects the
|
||||
// left side might have could not influence the
|
||||
// result. hence, force switch.
|
||||
|
||||
if (!(self.left instanceof AST_Binary
|
||||
&& PRECEDENCE[self.left.operator] >= PRECEDENCE[self.operator])) {
|
||||
reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
self = self.lift_sequences(compressor);
|
||||
if (compressor.option("assignments") && lazy_op[self.operator]) {
|
||||
var assign = self.right;
|
||||
// a || (a = x) => a = a || x
|
||||
// a && (a = x) => a = a && x
|
||||
if (self.left instanceof AST_SymbolRef
|
||||
&& assign instanceof AST_Assign
|
||||
&& assign.operator == "="
|
||||
&& self.left.equivalent_to(assign.left)) {
|
||||
self.right = assign.right;
|
||||
assign.right = self;
|
||||
return assign;
|
||||
}
|
||||
}
|
||||
if (compressor.option("comparisons")) switch (self.operator) {
|
||||
case "===":
|
||||
case "!==":
|
||||
@@ -5936,11 +6015,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
if (compressor.option("unsafe")
|
||||
&& self.right instanceof AST_Call
|
||||
&& self.right.expression instanceof AST_Dot
|
||||
&& indexFns[self.right.expression.property]) {
|
||||
if (compressor.option("unsafe")) {
|
||||
var indexRight = is_indexFn(self.right);
|
||||
if (compressor.option("booleans")
|
||||
&& indexRight
|
||||
&& (self.operator == "==" || self.operator == "!=")
|
||||
&& self.left instanceof AST_Number
|
||||
&& self.left.getValue() == 0
|
||||
@@ -5950,18 +6028,26 @@ merge(Compressor.prototype, {
|
||||
expression: self.right
|
||||
}) : self.right).optimize(compressor);
|
||||
}
|
||||
var indexLeft = is_indexFn(self.left);
|
||||
if (compressor.option("comparisons") && is_indexOf_match_pattern()) {
|
||||
var node = make_node(AST_UnaryPrefix, self, {
|
||||
operator: "!",
|
||||
expression: make_node(AST_UnaryPrefix, self, {
|
||||
operator: "~",
|
||||
expression: self.right
|
||||
expression: indexLeft ? self.left : self.right
|
||||
})
|
||||
});
|
||||
if (self.operator == "!=" || self.operator == "<=") node = make_node(AST_UnaryPrefix, self, {
|
||||
switch (self.operator) {
|
||||
case "<":
|
||||
if (indexLeft) break;
|
||||
case "<=":
|
||||
case "!=":
|
||||
node = make_node(AST_UnaryPrefix, self, {
|
||||
operator: "!",
|
||||
expression: node
|
||||
});
|
||||
break;
|
||||
}
|
||||
return node.optimize(compressor);
|
||||
}
|
||||
}
|
||||
@@ -5999,17 +6085,26 @@ merge(Compressor.prototype, {
|
||||
return node.evaluate(compressor);
|
||||
}
|
||||
|
||||
function is_indexFn(node) {
|
||||
return node instanceof AST_Call
|
||||
&& node.expression instanceof AST_Dot
|
||||
&& indexFns[node.expression.property];
|
||||
}
|
||||
|
||||
function is_indexOf_match_pattern() {
|
||||
switch (self.operator) {
|
||||
case ">":
|
||||
case "<=":
|
||||
// 0 > array.indexOf(string) => !~array.indexOf(string)
|
||||
// 0 <= array.indexOf(string) => !!~array.indexOf(string)
|
||||
return self.left instanceof AST_Number && self.left.getValue() == 0;
|
||||
return indexRight && self.left instanceof AST_Number && self.left.getValue() == 0;
|
||||
case "<":
|
||||
// array.indexOf(string) < 0 => !~array.indexOf(string)
|
||||
if (indexLeft && self.right instanceof AST_Number && self.right.getValue() == 0) return true;
|
||||
// -1 < array.indexOf(string) => !!~array.indexOf(string)
|
||||
case "==":
|
||||
case "!=":
|
||||
// -1 == array.indexOf(string) => !~array.indexOf(string)
|
||||
// -1 != array.indexOf(string) => !!~array.indexOf(string)
|
||||
if (!indexRight) return false;
|
||||
return self.left instanceof AST_Number && self.left.getValue() == -1
|
||||
|| self.left instanceof AST_UnaryPrefix && self.left.operator == "-"
|
||||
&& self.left.expression instanceof AST_Number && self.left.expression.getValue() == 1;
|
||||
@@ -6102,7 +6197,6 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
} else {
|
||||
value = fixed.optimize(compressor);
|
||||
if (value === fixed) value = fixed.clone(true);
|
||||
}
|
||||
def.replaced++;
|
||||
return value;
|
||||
@@ -6254,6 +6348,7 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("dead_code")
|
||||
&& self.left instanceof AST_SymbolRef
|
||||
&& (def = self.left.definition()).scope === compressor.find_parent(AST_Lambda)) {
|
||||
if (self.left.is_immutable()) return strip_assignment();
|
||||
var level = 0, node, parent = self;
|
||||
do {
|
||||
node = parent;
|
||||
@@ -6261,16 +6356,12 @@ merge(Compressor.prototype, {
|
||||
if (parent instanceof AST_Exit) {
|
||||
if (in_try(level, parent)) break;
|
||||
if (is_reachable(def.scope, [ def ])) break;
|
||||
if (self.operator == "=") return self.right.optimize(compressor);
|
||||
def.fixed = false;
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: self.operator.slice(0, -1),
|
||||
left: self.left,
|
||||
right: self.right
|
||||
}).optimize(compressor);
|
||||
return strip_assignment();
|
||||
}
|
||||
} while (parent instanceof AST_Binary && parent.right === node
|
||||
|| parent instanceof AST_Sequence && parent.tail_node() === node);
|
||||
|| parent instanceof AST_Sequence && parent.tail_node() === node
|
||||
|| parent instanceof AST_UnaryPrefix);
|
||||
}
|
||||
self = self.lift_sequences(compressor);
|
||||
if (!compressor.option("assignments")) return self;
|
||||
@@ -6318,6 +6409,14 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function strip_assignment() {
|
||||
return (self.operator != "=" ? make_node(AST_Binary, self, {
|
||||
operator: self.operator.slice(0, -1),
|
||||
left: self.left,
|
||||
right: self.right
|
||||
}) : maintain_this_binding(compressor, compressor.parent(), self, self.right)).optimize(compressor);
|
||||
}
|
||||
});
|
||||
|
||||
OPT(AST_Conditional, function(self, compressor) {
|
||||
@@ -6629,24 +6728,30 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
}
|
||||
var fn;
|
||||
var parent = compressor.parent();
|
||||
var def, fn, fn_parent;
|
||||
if (compressor.option("arguments")
|
||||
&& expr instanceof AST_SymbolRef
|
||||
&& expr.name == "arguments"
|
||||
&& expr.definition().orig.length == 1
|
||||
&& is_arguments(def = expr.definition())
|
||||
&& prop instanceof AST_Number
|
||||
&& (fn = expr.scope) === compressor.find_parent(AST_Lambda)) {
|
||||
&& (fn = expr.scope) === find_lambda()) {
|
||||
var index = prop.getValue();
|
||||
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
|
||||
if (!def.deleted) def.deleted = [];
|
||||
def.deleted[index] = true;
|
||||
}
|
||||
var argname = fn.argnames[index];
|
||||
if (argname && compressor.has_directive("use strict")) {
|
||||
var def = argname.definition();
|
||||
if (def.deleted && def.deleted[index]) {
|
||||
argname = null;
|
||||
} else if (argname && compressor.has_directive("use strict")) {
|
||||
var arg_def = argname.definition();
|
||||
if (!compressor.option("reduce_vars")
|
||||
|| expr.definition().reassigned
|
||||
|| def.assignments
|
||||
|| def.orig.length > 1) {
|
||||
|| def.reassigned
|
||||
|| arg_def.assignments
|
||||
|| arg_def.orig.length > 1) {
|
||||
argname = null;
|
||||
}
|
||||
} else if (!argname && !compressor.option("keep_fargs") && index < fn.argnames.length + 5) {
|
||||
} else if (!argname && index < fn.argnames.length + 5 && compressor.drop_fargs(fn, fn_parent)) {
|
||||
while (index >= fn.argnames.length) {
|
||||
argname = make_node(AST_SymbolFunarg, fn, {
|
||||
name: fn.make_var_name("argument_" + fn.argnames.length),
|
||||
@@ -6659,13 +6764,14 @@ merge(Compressor.prototype, {
|
||||
if (argname && find_if(function(node) {
|
||||
return node.name === argname.name;
|
||||
}, fn.argnames) === argname) {
|
||||
def.reassigned = false;
|
||||
var sym = make_node(AST_SymbolRef, self, argname);
|
||||
sym.reference({});
|
||||
delete argname.__unused;
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
if (is_lhs(compressor.self(), compressor.parent())) return self;
|
||||
if (is_lhs(compressor.self(), parent)) return self;
|
||||
if (key !== prop) {
|
||||
var sub = self.flatten_object(property, compressor);
|
||||
if (sub) {
|
||||
@@ -6714,6 +6820,16 @@ merge(Compressor.prototype, {
|
||||
return best_of(compressor, ev, self);
|
||||
}
|
||||
return self;
|
||||
|
||||
function find_lambda() {
|
||||
var i = 0, p;
|
||||
while (p = compressor.parent(i++)) {
|
||||
if (p instanceof AST_Lambda) {
|
||||
fn_parent = compressor.parent(i);
|
||||
return p;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
AST_Scope.DEFMETHOD("contains_this", function() {
|
||||
|
||||
@@ -8,7 +8,7 @@ var to_base64 = typeof btoa == "undefined" ? function(str) {
|
||||
} : btoa;
|
||||
|
||||
function read_source_map(name, code) {
|
||||
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(.*)/.exec(code);
|
||||
var match = /\n\/\/# sourceMappingURL=data:application\/json(;.*?)?;base64,(\S+)\s*$/.exec(code);
|
||||
if (!match) {
|
||||
AST_Node.warn("inline source map not found: " + name);
|
||||
return null;
|
||||
@@ -151,12 +151,13 @@ function minify(files, options) {
|
||||
if (quoted_props) {
|
||||
reserve_quoted_keys(toplevel, quoted_props);
|
||||
}
|
||||
if (options.wrap) {
|
||||
toplevel = toplevel.wrap_commonjs(options.wrap);
|
||||
}
|
||||
if (options.enclose) {
|
||||
toplevel = toplevel.wrap_enclose(options.enclose);
|
||||
}
|
||||
[ "enclose", "wrap" ].forEach(function(action) {
|
||||
var option = options[action];
|
||||
if (!option) return;
|
||||
var orig = toplevel.print_to_string().slice(0, -1);
|
||||
toplevel = toplevel[action](option);
|
||||
files[toplevel.start.file] = toplevel.print_to_string().replace(orig, "");
|
||||
});
|
||||
if (timings) timings.rename = Date.now();
|
||||
if (options.rename) {
|
||||
toplevel.figure_out_scope(options.mangle);
|
||||
@@ -204,10 +205,14 @@ function minify(files, options) {
|
||||
result.code = stream.get();
|
||||
if (options.sourceMap) {
|
||||
result.map = options.output.source_map.toString();
|
||||
if (options.sourceMap.url == "inline") {
|
||||
var url = options.sourceMap.url;
|
||||
if (url) {
|
||||
result.code = result.code.replace(/\n\/\/# sourceMappingURL=\S+\s*$/, "");
|
||||
if (url == "inline") {
|
||||
result.code += "\n//# sourceMappingURL=data:application/json;charset=utf-8;base64," + to_base64(result.map);
|
||||
} else if (options.sourceMap.url) {
|
||||
result.code += "\n//# sourceMappingURL=" + options.sourceMap.url;
|
||||
} else {
|
||||
result.code += "\n//# sourceMappingURL=" + url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -136,8 +136,7 @@ function OutputStream(options) {
|
||||
|
||||
function make_string(str, quote) {
|
||||
var dq = 0, sq = 0;
|
||||
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g,
|
||||
function(s, i) {
|
||||
str = str.replace(/[\\\b\f\n\r\v\t\x22\x27\u2028\u2029\0\ufeff]/g, function(s, i) {
|
||||
switch (s) {
|
||||
case '"': ++dq; return '"';
|
||||
case "'": ++sq; return "'";
|
||||
@@ -451,16 +450,11 @@ function OutputStream(options) {
|
||||
|
||||
function prepend_comments(node) {
|
||||
var self = this;
|
||||
var start = node.start;
|
||||
if (!start) return;
|
||||
if (start.comments_before && start.comments_before._dumped === self) return;
|
||||
var comments = start.comments_before;
|
||||
if (!comments) {
|
||||
comments = start.comments_before = [];
|
||||
}
|
||||
comments._dumped = self;
|
||||
var scan = node instanceof AST_Exit && node.value;
|
||||
var comments = dump(node);
|
||||
if (!comments) return;
|
||||
|
||||
if (node instanceof AST_Exit && node.value) {
|
||||
if (scan) {
|
||||
var tw = new TreeWalker(function(node) {
|
||||
var parent = tw.parent();
|
||||
if (parent instanceof AST_Exit
|
||||
@@ -471,11 +465,8 @@ function OutputStream(options) {
|
||||
|| parent instanceof AST_Sequence && parent.expressions[0] === node
|
||||
|| parent instanceof AST_Sub && parent.expression === node
|
||||
|| parent instanceof AST_UnaryPostfix) {
|
||||
var text = node.start.comments_before;
|
||||
if (text && text._dumped !== self) {
|
||||
text._dumped = self;
|
||||
comments = comments.concat(text);
|
||||
}
|
||||
var before = dump(node);
|
||||
if (before) comments = comments.concat(before);
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
@@ -518,13 +509,29 @@ function OutputStream(options) {
|
||||
}
|
||||
});
|
||||
if (!last_nlb) {
|
||||
if (start.nlb) {
|
||||
if (node.start.nlb) {
|
||||
print("\n");
|
||||
indent();
|
||||
} else {
|
||||
space();
|
||||
}
|
||||
}
|
||||
|
||||
function dump(node) {
|
||||
var token = node.start;
|
||||
if (!token) {
|
||||
if (!scan) return;
|
||||
node.start = token = new AST_Token();
|
||||
}
|
||||
var comments = token.comments_before;
|
||||
if (!comments) {
|
||||
if (!scan) return;
|
||||
token.comments_before = comments = [];
|
||||
}
|
||||
if (comments._dumped === self) return;
|
||||
comments._dumped = self;
|
||||
return comments;
|
||||
}
|
||||
}
|
||||
|
||||
function append_comments(node, tail) {
|
||||
@@ -591,7 +598,6 @@ function OutputStream(options) {
|
||||
}
|
||||
print(encoded);
|
||||
},
|
||||
encode_string : encode_string,
|
||||
next_indent : next_indent,
|
||||
with_indent : with_indent,
|
||||
with_block : with_block,
|
||||
@@ -1375,8 +1381,27 @@ function OutputStream(options) {
|
||||
if (regexp.raw_source) {
|
||||
str = "/" + regexp.raw_source + str.slice(str.lastIndexOf("/"));
|
||||
}
|
||||
str = output.to_utf8(str);
|
||||
output.print(str);
|
||||
output.print(output.to_utf8(str).replace(/\\(?:\0(?![0-9])|[^\0])/g, function(seq) {
|
||||
switch (seq[1]) {
|
||||
case "\n": return "\\n";
|
||||
case "\r": return "\\r";
|
||||
case "\t": return "\t";
|
||||
case "\b": return "\b";
|
||||
case "\f": return "\f";
|
||||
case "\0": return "\0";
|
||||
case "\x0B": return "\v";
|
||||
case "\u2028": return "\\u2028";
|
||||
case "\u2029": return "\\u2029";
|
||||
default: return seq;
|
||||
}
|
||||
}).replace(/[\n\r\u2028\u2029]/g, function(c) {
|
||||
switch (c) {
|
||||
case "\n": return "\\n";
|
||||
case "\r": return "\\r";
|
||||
case "\u2028": return "\\u2028";
|
||||
case "\u2029": return "\\u2029";
|
||||
}
|
||||
}));
|
||||
var p = output.parent();
|
||||
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
||||
output.print(" ");
|
||||
|
||||
10
package.json
10
package.json
@@ -3,7 +3,7 @@
|
||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||
"license": "BSD-2-Clause",
|
||||
"version": "3.5.10",
|
||||
"version": "3.6.1",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
@@ -23,15 +23,15 @@
|
||||
"LICENSE"
|
||||
],
|
||||
"dependencies": {
|
||||
"commander": "~2.20.0",
|
||||
"commander": "2.20.0",
|
||||
"source-map": "~0.6.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"acorn": "~6.1.1",
|
||||
"semver": "~6.0.0"
|
||||
"acorn": "~7.1.0",
|
||||
"semver": "~6.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "node test/run-tests.js"
|
||||
"test": "node test/compress.js && node test/mocha.js"
|
||||
},
|
||||
"keywords": [
|
||||
"cli",
|
||||
|
||||
450
test/compress.js
Normal file
450
test/compress.js
Normal file
@@ -0,0 +1,450 @@
|
||||
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 file = process.argv[2];
|
||||
var dir = path.resolve(path.dirname(module.filename), "compress");
|
||||
if (file) {
|
||||
var minify_options = require("./ufuzz.json").map(JSON.stringify);
|
||||
log("--- {file}", { file: file });
|
||||
var tests = parse_test(path.resolve(dir, file));
|
||||
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);
|
||||
}
|
||||
})();
|
||||
}
|
||||
|
||||
function evaluate(code) {
|
||||
if (code instanceof U.AST_Node) code = make_code(code, { beautify: true });
|
||||
return new Function("return(" + code + ")")();
|
||||
}
|
||||
|
||||
function log() {
|
||||
console.log("%s", tmpl.apply(null, arguments));
|
||||
}
|
||||
|
||||
function make_code(ast, options) {
|
||||
var stream = U.OutputStream(options);
|
||||
ast.print(stream);
|
||||
return stream.get();
|
||||
}
|
||||
|
||||
function parse_test(file) {
|
||||
var script = fs.readFileSync(file, "utf8");
|
||||
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348
|
||||
try {
|
||||
var ast = U.parse(script, {
|
||||
filename: file
|
||||
});
|
||||
} catch (e) {
|
||||
console.error("Caught error while parsing tests in " + file);
|
||||
console.error(e);
|
||||
process.exit(1);
|
||||
}
|
||||
var tests = Object.create(null);
|
||||
var tw = new U.TreeWalker(function(node, descend) {
|
||||
if (node instanceof U.AST_LabeledStatement
|
||||
&& tw.parent() instanceof U.AST_Toplevel) {
|
||||
var name = node.label.name;
|
||||
if (name in tests) {
|
||||
throw new Error('Duplicated test name "' + name + '" in ' + file);
|
||||
}
|
||||
tests[name] = get_one_test(name, node.body);
|
||||
return true;
|
||||
}
|
||||
if (!(node instanceof U.AST_Toplevel)) croak(node);
|
||||
});
|
||||
ast.walk(tw);
|
||||
return tests;
|
||||
|
||||
function croak(node) {
|
||||
throw new Error(tmpl("Can't understand test file {file} [{line},{col}]\n{code}", {
|
||||
file: file,
|
||||
line: node.start.line,
|
||||
col: node.start.col,
|
||||
code: make_code(node, { beautify: false })
|
||||
}));
|
||||
}
|
||||
|
||||
function read_string(stat) {
|
||||
if (stat.TYPE == "SimpleStatement") {
|
||||
var body = stat.body;
|
||||
switch(body.TYPE) {
|
||||
case "String":
|
||||
return body.value;
|
||||
case "Array":
|
||||
return body.elements.map(function(element) {
|
||||
if (element.TYPE !== "String")
|
||||
throw new Error("Should be array of strings");
|
||||
return element.value;
|
||||
}).join("\n");
|
||||
}
|
||||
}
|
||||
throw new Error("Should be string or array of strings");
|
||||
}
|
||||
|
||||
function get_one_test(name, block) {
|
||||
var test = { name: name, options: {} };
|
||||
var tw = new U.TreeWalker(function(node, descend) {
|
||||
if (node instanceof U.AST_Assign) {
|
||||
if (!(node.left instanceof U.AST_SymbolRef)) {
|
||||
croak(node);
|
||||
}
|
||||
var name = node.left.name;
|
||||
test[name] = evaluate(node.right);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof U.AST_LabeledStatement) {
|
||||
var label = node.label;
|
||||
assert.ok([
|
||||
"input",
|
||||
"expect",
|
||||
"expect_exact",
|
||||
"expect_warnings",
|
||||
"expect_stdout",
|
||||
"node_version",
|
||||
].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", {
|
||||
name: label.name,
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
var stat = node.body;
|
||||
if (label.name == "expect_exact" || label.name == "node_version") {
|
||||
test[label.name] = read_string(stat);
|
||||
} else if (label.name == "expect_stdout") {
|
||||
var body = stat.body;
|
||||
if (body instanceof U.AST_Boolean) {
|
||||
test[label.name] = body.value;
|
||||
} else if (body instanceof U.AST_Call) {
|
||||
var ctor = global[body.expression.name];
|
||||
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
test[label.name] = ctor.apply(null, body.args.map(function(node) {
|
||||
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
return node.value;
|
||||
}));
|
||||
} else {
|
||||
test[label.name] = read_string(stat) + "\n";
|
||||
}
|
||||
} else {
|
||||
test[label.name] = stat;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
block.walk(tw);
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
// Try to reminify original input with standard options
|
||||
// to see if it matches 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) [
|
||||
"keep_fargs",
|
||||
"keep_fnames",
|
||||
].forEach(function(name) {
|
||||
if (name in orig_options) {
|
||||
options.compress[name] = orig_options[name];
|
||||
}
|
||||
});
|
||||
var options_formatted = JSON.stringify(options, null, 4);
|
||||
var result = U.minify(input_code, options);
|
||||
if (result.error) {
|
||||
log([
|
||||
"!!! failed input reminify",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"---OPTIONS---",
|
||||
"{options}",
|
||||
"--ERROR---",
|
||||
"{error}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
options: options_formatted,
|
||||
error: result.error,
|
||||
});
|
||||
return false;
|
||||
} else {
|
||||
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(expected, actual)) {
|
||||
log([
|
||||
"!!! failed running reminified input",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"---OPTIONS---",
|
||||
"{options}",
|
||||
"---OUTPUT---",
|
||||
"{output}",
|
||||
"---EXPECTED {expected_type}---",
|
||||
"{expected}",
|
||||
"---ACTUAL {actual_type}---",
|
||||
"{actual}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
options: options_formatted,
|
||||
output: result.code,
|
||||
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, toplevel) {
|
||||
var result = sandbox.run_code(code, toplevel);
|
||||
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
|
||||
}
|
||||
|
||||
function test_case(test) {
|
||||
log(" Running test [{name}]", { name: test.name });
|
||||
var output_options = test.beautify || {};
|
||||
var expect;
|
||||
if (test.expect) {
|
||||
expect = make_code(to_toplevel(test.expect, test.mangle), output_options);
|
||||
} else {
|
||||
expect = test.expect_exact;
|
||||
}
|
||||
var input = to_toplevel(test.input, test.mangle);
|
||||
var input_code = make_code(input);
|
||||
var input_formatted = make_code(test.input, {
|
||||
beautify: true,
|
||||
comments: "all",
|
||||
keep_quoted_props: true,
|
||||
quote_style: 3,
|
||||
});
|
||||
try {
|
||||
U.parse(input_code);
|
||||
} catch (ex) {
|
||||
log([
|
||||
"!!! Cannot parse input",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"--PARSE ERROR--",
|
||||
"{error}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
error: ex,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
var warnings_emitted = [];
|
||||
if (test.expect_warnings) {
|
||||
var expected_warnings = make_code(test.expect_warnings, {
|
||||
beautify: false,
|
||||
quote_style: 2, // force double quote to match JSON
|
||||
});
|
||||
U.AST_Node.log_function(function(text) {
|
||||
warnings_emitted.push(text);
|
||||
}, /"INFO: /.test(expected_warnings));
|
||||
}
|
||||
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
|
||||
var quoted_props = test.mangle.properties.reserved;
|
||||
if (!Array.isArray(quoted_props)) quoted_props = [];
|
||||
test.mangle.properties.reserved = quoted_props;
|
||||
U.reserve_quoted_keys(input, quoted_props);
|
||||
}
|
||||
if (test.rename) {
|
||||
input.figure_out_scope(test.mangle);
|
||||
input.expand_names(test.mangle);
|
||||
}
|
||||
var cmp = new U.Compressor(test.options, true);
|
||||
var output = cmp.compress(input);
|
||||
output.figure_out_scope(test.mangle);
|
||||
if (test.mangle) {
|
||||
output.compute_char_frequency(test.mangle);
|
||||
output.mangle_names(test.mangle);
|
||||
if (test.mangle.properties) {
|
||||
output = U.mangle_properties(output, test.mangle.properties);
|
||||
}
|
||||
}
|
||||
output = make_code(output, output_options);
|
||||
if (expect != output) {
|
||||
log([
|
||||
"!!! failed",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"---OUTPUT---",
|
||||
"{output}",
|
||||
"---EXPECTED---",
|
||||
"{expected}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
output: output,
|
||||
expected: expect
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// expect == output
|
||||
try {
|
||||
U.parse(output);
|
||||
} catch (ex) {
|
||||
log([
|
||||
"!!! Test matched expected result but cannot parse output",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"---OUTPUT---",
|
||||
"{output}",
|
||||
"--REPARSE ERROR--",
|
||||
"{error}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
output: output,
|
||||
error: ex,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (test.expect_warnings) {
|
||||
warnings_emitted = warnings_emitted.map(function(input) {
|
||||
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
|
||||
});
|
||||
var actual_warnings = JSON.stringify(warnings_emitted);
|
||||
if (expected_warnings != actual_warnings) {
|
||||
log([
|
||||
"!!! failed",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"---EXPECTED WARNINGS---",
|
||||
"{expected_warnings}",
|
||||
"---ACTUAL WARNINGS---",
|
||||
"{actual_warnings}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
expected_warnings: expected_warnings,
|
||||
actual_warnings: actual_warnings,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
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 = actual;
|
||||
}
|
||||
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
|
||||
log([
|
||||
"!!! Invalid input or expected stdout",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"---EXPECTED {expected_type}---",
|
||||
"{expected}",
|
||||
"---ACTUAL {actual_type}---",
|
||||
"{actual}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
||||
expected: test.expect_stdout,
|
||||
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
|
||||
actual: actual,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
actual = run_code(output, toplevel);
|
||||
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
|
||||
log([
|
||||
"!!! failed",
|
||||
"---INPUT---",
|
||||
"{input}",
|
||||
"---EXPECTED {expected_type}---",
|
||||
"{expected}",
|
||||
"---ACTUAL {actual_type}---",
|
||||
"{actual}",
|
||||
"",
|
||||
"",
|
||||
].join("\n"), {
|
||||
input: input_formatted,
|
||||
expected_type: typeof test.expect_stdout == "string" ? "STDOUT" : "ERROR",
|
||||
expected: test.expect_stdout,
|
||||
actual_type: typeof actual == "string" ? "STDOUT" : "ERROR",
|
||||
actual: actual,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (!reminify(test.options, input_code, input_formatted, stdout)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function tmpl() {
|
||||
return U.string_template.apply(null, arguments);
|
||||
}
|
||||
|
||||
function to_toplevel(input, mangle_options) {
|
||||
if (!(input instanceof U.AST_BlockStatement)) throw new Error("Unsupported input syntax");
|
||||
var directive = true;
|
||||
var offset = input.start.line;
|
||||
var tokens = [];
|
||||
var toplevel = new U.AST_Toplevel(input.transform(new U.TreeTransformer(function(node) {
|
||||
if (U.push_uniq(tokens, node.start)) node.start.line -= offset;
|
||||
if (!directive || node === input) return;
|
||||
if (node instanceof U.AST_SimpleStatement && node.body instanceof U.AST_String) {
|
||||
return new U.AST_Directive(node.body);
|
||||
} else {
|
||||
directive = false;
|
||||
}
|
||||
})));
|
||||
toplevel.figure_out_scope(mangle_options);
|
||||
return toplevel;
|
||||
}
|
||||
@@ -405,6 +405,52 @@ issue_3273_global_strict_reduce_vars: {
|
||||
]
|
||||
}
|
||||
|
||||
issue_3273_keep_fargs_false: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: false,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
"use strict";
|
||||
arguments[0]++;
|
||||
console.log(arguments[0]);
|
||||
})(0);
|
||||
}
|
||||
expect: {
|
||||
(function(argument_0) {
|
||||
"use strict";
|
||||
argument_0++;
|
||||
console.log(argument_0);
|
||||
})(0);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_3273_keep_fargs_strict: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
"use strict";
|
||||
arguments[0]++;
|
||||
console.log(arguments[0]);
|
||||
})(0);
|
||||
}
|
||||
expect: {
|
||||
(function(argument_0) {
|
||||
"use strict";
|
||||
argument_0++;
|
||||
console.log(argument_0);
|
||||
})(0);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_3282_1: {
|
||||
options = {
|
||||
arguments: true,
|
||||
@@ -576,3 +622,157 @@ issue_3282_2_passes: {
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_3420_1: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
return function() {
|
||||
return arguments[0];
|
||||
};
|
||||
}().length);
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return function() {
|
||||
return arguments[0];
|
||||
};
|
||||
}().length);
|
||||
}
|
||||
expect_stdout: "0"
|
||||
}
|
||||
|
||||
issue_3420_2: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
}
|
||||
input: {
|
||||
var foo = function() {
|
||||
delete arguments[0];
|
||||
};
|
||||
foo();
|
||||
}
|
||||
expect: {
|
||||
var foo = function() {
|
||||
delete arguments[0];
|
||||
};
|
||||
foo();
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_3420_3: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
var foo = function() {
|
||||
delete arguments[0];
|
||||
};
|
||||
foo();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
var foo = function() {
|
||||
delete arguments[0];
|
||||
};
|
||||
foo();
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_3420_4: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
}
|
||||
input: {
|
||||
!function() {
|
||||
console.log(arguments[0]);
|
||||
delete arguments[0];
|
||||
console.log(arguments[0]);
|
||||
}(42);
|
||||
}
|
||||
expect: {
|
||||
!function(argument_0) {
|
||||
console.log(argument_0);
|
||||
delete arguments[0];
|
||||
console.log(arguments[0]);
|
||||
}(42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"42",
|
||||
"undefined",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3420_5: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
!function() {
|
||||
console.log(arguments[0]);
|
||||
delete arguments[0];
|
||||
console.log(arguments[0]);
|
||||
}(42);
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
!function(argument_0) {
|
||||
console.log(argument_0);
|
||||
delete arguments[0];
|
||||
console.log(arguments[0]);
|
||||
}(42);
|
||||
}
|
||||
expect_stdout: [
|
||||
"42",
|
||||
"undefined",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3420_6: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
return delete arguments[0];
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return delete arguments[0];
|
||||
}());
|
||||
}
|
||||
expect_stdout: "true"
|
||||
}
|
||||
|
||||
issue_3420_7: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: "strict",
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
console.log(function() {
|
||||
return delete arguments[0];
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
console.log(function() {
|
||||
return delete arguments[0];
|
||||
}());
|
||||
}
|
||||
expect_stdout: "true"
|
||||
}
|
||||
|
||||
@@ -311,3 +311,65 @@ issue_3375: {
|
||||
}
|
||||
expect_stdout: "string"
|
||||
}
|
||||
|
||||
issue_3427: {
|
||||
options = {
|
||||
assignments: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
a || (a = {});
|
||||
})();
|
||||
}
|
||||
expect: {}
|
||||
}
|
||||
|
||||
issue_3429_1: {
|
||||
options = {
|
||||
assignments: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = "PASS";
|
||||
(function(b) {
|
||||
b && (b = a = "FAIL");
|
||||
})();
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a = "PASS";
|
||||
(function(b) {
|
||||
b = b && (a = "FAIL");
|
||||
})();
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3429_2: {
|
||||
options = {
|
||||
assignments: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a;
|
||||
(function(b) {
|
||||
b || (b = a = "FAIL");
|
||||
})(42);
|
||||
console.log(a);
|
||||
}
|
||||
expect: {
|
||||
var a;
|
||||
(function(b) {
|
||||
b = b || (a = "FAIL");
|
||||
})(42);
|
||||
console.log(a);
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
@@ -3497,10 +3497,10 @@ issue_2437_1: {
|
||||
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
|
||||
result;
|
||||
}
|
||||
var req, detectFunc = function(){};
|
||||
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
|
||||
return req.onreadystatechange = null, result;
|
||||
var req = new XMLHttpRequest(), detectFunc = function(){};
|
||||
return req.onreadystatechange = detectFunc,
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
|
||||
req.onreadystatechange = null, result;
|
||||
}());
|
||||
}
|
||||
}
|
||||
@@ -3545,8 +3545,8 @@ issue_2437_2: {
|
||||
if (xhrDesc)
|
||||
return (req = new XMLHttpRequest()).onreadystatechange,
|
||||
Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {});
|
||||
var req;
|
||||
(req = new XMLHttpRequest).onreadystatechange = function(){},
|
||||
var req = new XMLHttpRequest();
|
||||
req.onreadystatechange = function(){},
|
||||
req[SYMBOL_FAKE_ONREADYSTATECHANGE_1],
|
||||
req.onreadystatechange = null;
|
||||
}();
|
||||
@@ -6178,3 +6178,62 @@ assign_undeclared: {
|
||||
"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
|
||||
}
|
||||
|
||||
issue_3439_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function(a) {
|
||||
function a() {}
|
||||
return a;
|
||||
}(42));
|
||||
}
|
||||
expect: {
|
||||
console.log(typeof function(a) {
|
||||
function a() {}
|
||||
return a;
|
||||
}(42));
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_3439_2: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function() {
|
||||
var a = 42;
|
||||
function a() {}
|
||||
return a;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(typeof function() {
|
||||
return 42;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "number"
|
||||
}
|
||||
|
||||
@@ -373,10 +373,70 @@ unsafe_indexOf: {
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
if (Object.keys({ foo: 42 }).indexOf("foo") >= 0) console.log("PASS");
|
||||
var a = Object.keys({ foo: 42 });
|
||||
if (a.indexOf("bar") < 0) console.log("PASS");
|
||||
if (0 > a.indexOf("bar")) console.log("PASS");
|
||||
if (a.indexOf("foo") >= 0) console.log("PASS");
|
||||
if (0 <= a.indexOf("foo")) console.log("PASS");
|
||||
if (a.indexOf("foo") > -1) console.log("PASS");
|
||||
if (-1 < a.indexOf("foo")) console.log("PASS");
|
||||
if (a.indexOf("bar") == -1) console.log("PASS");
|
||||
if (-1 == a.indexOf("bar")) console.log("PASS");
|
||||
if (a.indexOf("bar") === -1) console.log("PASS");
|
||||
if (-1 === a.indexOf("bar")) console.log("PASS");
|
||||
if (a.indexOf("foo") != -1) console.log("PASS");
|
||||
if (-1 != a.indexOf("foo")) console.log("PASS");
|
||||
if (a.indexOf("foo") !== -1) console.log("PASS");
|
||||
if (-1 !== a.indexOf("foo")) console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
if (~Object.keys({ foo: 42 }).indexOf("foo")) console.log("PASS");
|
||||
var a = Object.keys({ foo: 42 });
|
||||
if (!~a.indexOf("bar")) console.log("PASS");
|
||||
if (!~a.indexOf("bar")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
if (!~a.indexOf("bar")) console.log("PASS");
|
||||
if (!~a.indexOf("bar")) console.log("PASS");
|
||||
if (!~a.indexOf("bar")) console.log("PASS");
|
||||
if (!~a.indexOf("bar")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
if (~a.indexOf("foo")) console.log("PASS");
|
||||
}
|
||||
expect_stdout: [
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
"PASS",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3413: {
|
||||
options = {
|
||||
comparisons: true,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
var b;
|
||||
void 0 !== ("" < b || void 0) || console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
var b;
|
||||
void 0 !== ("" < b || void 0) || console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -960,3 +960,56 @@ unsafe_string_replace: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3402: {
|
||||
options = {
|
||||
dead_code: true,
|
||||
evaluate: true,
|
||||
functions: true,
|
||||
passes: 2,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
typeofs: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var f = function f() {
|
||||
f = 42;
|
||||
console.log(typeof f);
|
||||
};
|
||||
"function" == typeof f && f();
|
||||
"function" == typeof f && f();
|
||||
console.log(typeof f);
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
console.log(typeof f);
|
||||
}
|
||||
f();
|
||||
f();
|
||||
console.log(typeof f);
|
||||
}
|
||||
expect_stdout: [
|
||||
"function",
|
||||
"function",
|
||||
"function",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3406: {
|
||||
options = {
|
||||
dead_code: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function f(a) {
|
||||
return delete (f = a);
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function f(a) {
|
||||
return delete (0, a);
|
||||
}());
|
||||
}
|
||||
expect_stdout: "true"
|
||||
}
|
||||
|
||||
@@ -797,6 +797,7 @@ assign_chain: {
|
||||
issue_1583: {
|
||||
options = {
|
||||
keep_fargs: true,
|
||||
passes: 2,
|
||||
reduce_funcs: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
@@ -1004,7 +1005,7 @@ issue_1715_4: {
|
||||
delete_assign_1: {
|
||||
options = {
|
||||
booleans: true,
|
||||
side_effects: true,
|
||||
evaluate: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
@@ -1023,7 +1024,7 @@ delete_assign_1: {
|
||||
console.log((1 / 0, !0));
|
||||
console.log((1 / 0, !0));
|
||||
console.log((NaN, !0));
|
||||
console.log((0 / 0, !0));
|
||||
console.log((NaN, !0));
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
@@ -1031,8 +1032,8 @@ delete_assign_1: {
|
||||
delete_assign_2: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
keep_infinity: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
@@ -1051,7 +1052,7 @@ delete_assign_2: {
|
||||
console.log((Infinity, !0));
|
||||
console.log((1 / 0, !0));
|
||||
console.log((NaN, !0));
|
||||
console.log((0 / 0, !0));
|
||||
console.log((NaN, !0));
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
@@ -1144,6 +1145,7 @@ var_catch_toplevel: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
negate_iife: true,
|
||||
passes: 2,
|
||||
reduce_funcs: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
@@ -2026,3 +2028,37 @@ issue_3375: {
|
||||
}
|
||||
expect_stdout: "0 0"
|
||||
}
|
||||
|
||||
issue_3427_1: {
|
||||
options = {
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
a = a || {};
|
||||
})();
|
||||
}
|
||||
expect: {}
|
||||
}
|
||||
|
||||
issue_3427_2: {
|
||||
options = {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var s = "PASS";
|
||||
console.log(s = s || "FAIL");
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var s = "PASS";
|
||||
console.log(s = s || "FAIL");
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -2860,10 +2860,10 @@ issue_2437: {
|
||||
result;
|
||||
}
|
||||
function detectFunc() {}
|
||||
var req;
|
||||
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
|
||||
return req.onreadystatechange = null, result;
|
||||
var req = new XMLHttpRequest();
|
||||
return req.onreadystatechange = detectFunc,
|
||||
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc,
|
||||
req.onreadystatechange = null, result;
|
||||
}());
|
||||
}
|
||||
}
|
||||
@@ -3065,3 +3065,134 @@ class_iife: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3400: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
reduce_funcs: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(f) {
|
||||
console.log(f()()[0].p);
|
||||
})(function() {
|
||||
function g() {
|
||||
function h(u) {
|
||||
var o = {
|
||||
p: u
|
||||
};
|
||||
return console.log(o[g]), o;
|
||||
}
|
||||
function e() {
|
||||
return [ 42 ].map(function(v) {
|
||||
return h(v);
|
||||
});
|
||||
}
|
||||
return e();
|
||||
}
|
||||
return g;
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
void console.log(function g() {
|
||||
function e() {
|
||||
return [42].map(function(v) {
|
||||
return o = {
|
||||
p: v
|
||||
}, console.log(o[g]) , o;
|
||||
var o;
|
||||
});
|
||||
}
|
||||
return e();
|
||||
}()[0].p);
|
||||
}
|
||||
expect_stdout: [
|
||||
"undefined",
|
||||
"42",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3402: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
functions: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
typeofs: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var f = function f() {
|
||||
f = 42;
|
||||
console.log(typeof f);
|
||||
};
|
||||
"function" == typeof f && f();
|
||||
"function" == typeof f && f();
|
||||
console.log(typeof f);
|
||||
}
|
||||
expect: {
|
||||
var f = function f() {
|
||||
f = 42;
|
||||
console.log(typeof f);
|
||||
};
|
||||
f();
|
||||
f();
|
||||
console.log(typeof f);
|
||||
}
|
||||
expect_stdout: [
|
||||
"function",
|
||||
"function",
|
||||
"function",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3439: {
|
||||
options = {
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function() {
|
||||
return function(a) {
|
||||
function a() {}
|
||||
return a;
|
||||
}(42);
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(typeof function(a) {
|
||||
function a() {}
|
||||
return a;
|
||||
}(42));
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_3444: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(h) {
|
||||
return f;
|
||||
function f() {
|
||||
g();
|
||||
}
|
||||
function g() {
|
||||
h("PASS");
|
||||
}
|
||||
})(console.log)();
|
||||
}
|
||||
expect: {
|
||||
(function(h) {
|
||||
return function() {
|
||||
void h("PASS");
|
||||
};
|
||||
})(console.log)();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -862,3 +862,55 @@ issue_3071_3: {
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
issue_3411: {
|
||||
options = {
|
||||
hoist_props: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var c = 1;
|
||||
!function f() {
|
||||
var o = {
|
||||
p: --c && f()
|
||||
};
|
||||
+o || console.log("PASS");
|
||||
}();
|
||||
}
|
||||
expect: {
|
||||
var c = 1;
|
||||
!function f() {
|
||||
var o_p = --c && f();
|
||||
+{} || console.log("PASS");
|
||||
}();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3440: {
|
||||
options = {
|
||||
hoist_props: true,
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
function f() {
|
||||
console.log(o.p);
|
||||
}
|
||||
var o = {
|
||||
p: "PASS",
|
||||
};
|
||||
return f;
|
||||
})()();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var o_p = "PASS";
|
||||
return function() {
|
||||
console.log(o_p);
|
||||
};
|
||||
})()();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -366,7 +366,7 @@ mangle_catch_redef_3: {
|
||||
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: {
|
||||
@@ -389,10 +389,10 @@ 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,
|
||||
@@ -412,7 +412,7 @@ mangle_catch_redef_ie8_3: {
|
||||
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: {
|
||||
@@ -435,5 +435,5 @@ 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
|
||||
}
|
||||
|
||||
1157
test/compress/keep_fargs.js
Normal file
1157
test/compress/keep_fargs.js
Normal file
File diff suppressed because it is too large
Load Diff
@@ -673,3 +673,19 @@ issue_3371: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
step: {
|
||||
options = {
|
||||
loops: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
for (var i = 0; i < 42; "foo", i++, "bar");
|
||||
console.log(i);
|
||||
}
|
||||
expect: {
|
||||
for (var i = 0; i < 42; i++);
|
||||
console.log(i);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
}
|
||||
|
||||
@@ -1161,3 +1161,49 @@ collapse_rhs_lhs: {
|
||||
}
|
||||
expect_stdout: "1 3"
|
||||
}
|
||||
|
||||
drop_arguments: {
|
||||
options = {
|
||||
pure_getters: "strict",
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
arguments.slice = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
arguments[42];
|
||||
arguments.length;
|
||||
arguments.slice();
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
arguments.slice = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
arguments.slice();
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3427: {
|
||||
options = {
|
||||
assignments: true,
|
||||
collapse_vars: true,
|
||||
inline: true,
|
||||
pure_getters: "strict",
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a;
|
||||
(function(b) {
|
||||
b.p = 42;
|
||||
})(a || (a = {}));
|
||||
}
|
||||
expect: {}
|
||||
}
|
||||
|
||||
@@ -35,3 +35,140 @@ regexp_2: {
|
||||
}
|
||||
expect_stdout: '["PASS","pass"]'
|
||||
}
|
||||
|
||||
issue_3434_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
beautify = {
|
||||
beautify: true,
|
||||
}
|
||||
input: {
|
||||
var o = {
|
||||
"\n": RegExp("\n"),
|
||||
"\r": RegExp("\r"),
|
||||
"\t": RegExp("\t"),
|
||||
"\b": RegExp("\b"),
|
||||
"\f": RegExp("\f"),
|
||||
"\0": RegExp("\0"),
|
||||
"\x0B": RegExp("\x0B"),
|
||||
"\u2028": RegExp("\u2028"),
|
||||
"\u2029": RegExp("\u2029"),
|
||||
};
|
||||
for (var c in o)
|
||||
console.log(o[c].test("\\"), o[c].test(c));
|
||||
}
|
||||
expect_exact: [
|
||||
"var o = {",
|
||||
' "\\n": /\\n/,',
|
||||
' "\\r": /\\r/,',
|
||||
' "\\t": /\t/,',
|
||||
' "\\b": /\b/,',
|
||||
' "\\f": /\f/,',
|
||||
' "\\0": /\0/,',
|
||||
' "\\v": /\v/,',
|
||||
' "\\u2028": /\\u2028/,',
|
||||
' "\\u2029": /\\u2029/',
|
||||
"};",
|
||||
"",
|
||||
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
|
||||
]
|
||||
expect_stdout: [
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3434_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
beautify = {
|
||||
beautify: true,
|
||||
}
|
||||
input: {
|
||||
var o = {
|
||||
"\n": RegExp("\\\n"),
|
||||
"\r": RegExp("\\\r"),
|
||||
"\t": RegExp("\\\t"),
|
||||
"\b": RegExp("\\\b"),
|
||||
"\f": RegExp("\\\f"),
|
||||
"\0": RegExp("\\\0"),
|
||||
"\x0B": RegExp("\\\x0B"),
|
||||
"\u2028": RegExp("\\\u2028"),
|
||||
"\u2029": RegExp("\\\u2029"),
|
||||
};
|
||||
for (var c in o)
|
||||
console.log(o[c].test("\\"), o[c].test(c));
|
||||
}
|
||||
expect_exact: [
|
||||
"var o = {",
|
||||
' "\\n": /\\n/,',
|
||||
' "\\r": /\\r/,',
|
||||
' "\\t": /\t/,',
|
||||
' "\\b": /\b/,',
|
||||
' "\\f": /\f/,',
|
||||
' "\\0": /\0/,',
|
||||
' "\\v": /\v/,',
|
||||
' "\\u2028": /\\u2028/,',
|
||||
' "\\u2029": /\\u2029/',
|
||||
"};",
|
||||
"",
|
||||
'for (var c in o) console.log(o[c].test("\\\\"), o[c].test(c));',
|
||||
]
|
||||
expect_stdout: [
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
"false true",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3434_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
RegExp("\n");
|
||||
RegExp("\r");
|
||||
RegExp("\\n");
|
||||
RegExp("\\\n");
|
||||
RegExp("\\\\n");
|
||||
RegExp("\\\\\n");
|
||||
RegExp("\\\\\\n");
|
||||
RegExp("\\\\\\\n");
|
||||
RegExp("\u2028");
|
||||
RegExp("\u2029");
|
||||
RegExp("\n\r\u2028\u2029");
|
||||
RegExp("\\\nfo\n[\n]o\\bbb");
|
||||
}
|
||||
expect: {
|
||||
/\n/;
|
||||
/\r/;
|
||||
/\n/;
|
||||
/\n/;
|
||||
/\\n/;
|
||||
/\\\n/;
|
||||
/\\\n/;
|
||||
/\\\n/;
|
||||
/\u2028/;
|
||||
/\u2029/;
|
||||
/\n\r\u2028\u2029/;
|
||||
/\nfo\n[\n]o\bbb/;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,3 +12,71 @@ console_log: {
|
||||
"% %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"
|
||||
}
|
||||
|
||||
log_global: {
|
||||
input: {
|
||||
console.log(function() {
|
||||
return this;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return this;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "[object global]"
|
||||
}
|
||||
|
||||
@@ -490,6 +490,7 @@ issue_1758: {
|
||||
delete_seq_1: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
@@ -514,6 +515,7 @@ delete_seq_1: {
|
||||
delete_seq_2: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
@@ -538,6 +540,7 @@ delete_seq_2: {
|
||||
delete_seq_3: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
keep_infinity: true,
|
||||
side_effects: true,
|
||||
}
|
||||
@@ -563,6 +566,7 @@ delete_seq_3: {
|
||||
delete_seq_4: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
@@ -590,6 +594,7 @@ delete_seq_4: {
|
||||
delete_seq_5: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
keep_infinity: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
@@ -618,6 +623,7 @@ delete_seq_5: {
|
||||
delete_seq_6: {
|
||||
options = {
|
||||
booleans: true,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
|
||||
6
test/input/issue-3294/input.js
Normal file
6
test/input/issue-3294/input.js
Normal file
@@ -0,0 +1,6 @@
|
||||
var Foo = function Foo(){console.log(1+2);}; new Foo();
|
||||
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,DeadBeef
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjpudWxsLCJzb3VyY2VzIjpbInN0ZGluIl0sInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLElBQU0sR0FBRyxHQUFDLEFBQUUsWUFBVyxFQUFFLENBQUMsT0FBTyxDQUFDLEdBQUcsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFDLENBQUMsQ0FBQyxDQUFBLEFBQUUsQ0FBQyxJQUFJLEdBQUcsRUFBRSxDQUFDOyJ9
|
||||
|
||||
|
||||
2
test/input/issue-3294/output.js
Normal file
2
test/input/issue-3294/output.js
Normal file
@@ -0,0 +1,2 @@
|
||||
new function(){console.log(3)};
|
||||
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIl0sIm5hbWVzIjpbImNvbnNvbGUiLCJsb2ciXSwibWFwcGluZ3MiOiJBQUErQyxJQUFyQyxXQUFnQkEsUUFBUUMsSUFBSSIsInNvdXJjZXNDb250ZW50IjpbImNsYXNzIEZvbyB7IGNvbnN0cnVjdG9yKCl7Y29uc29sZS5sb2coMSsyKTt9IH0gbmV3IEZvbygpO1xuIl19
|
||||
@@ -62,8 +62,17 @@ if (typeof phantom == "undefined") {
|
||||
if (debug) {
|
||||
console.log("http://localhost:" + port + "/");
|
||||
} else {
|
||||
child_process.exec("npm install phantomjs-prebuilt@2.1.14 --no-save", function(error) {
|
||||
if (error) throw error;
|
||||
child_process.spawn(process.platform == "win32" ? "npm.cmd" : "npm", [
|
||||
"install",
|
||||
"phantomjs-prebuilt@2.1.14",
|
||||
"--no-audit",
|
||||
"--no-optional",
|
||||
"--no-save",
|
||||
"--no-update-notifier",
|
||||
], {
|
||||
stdio: [ "ignore", 1, 2 ]
|
||||
}).on("exit", function(code) {
|
||||
if (code) throw new Error("npm install failed!");
|
||||
var program = require("phantomjs-prebuilt").exec(process.argv[1], port);
|
||||
program.stdout.pipe(process.stdout);
|
||||
program.stderr.pipe(process.stderr);
|
||||
|
||||
@@ -114,13 +114,12 @@ describe("bin/uglifyjs", function() {
|
||||
|
||||
var child = exec(command, function(err, stdout) {
|
||||
if (err) throw err;
|
||||
|
||||
assert.strictEqual(stdout, read("test/input/pr-3040/expect.js"));
|
||||
assert.strictEqual(stdout, read("test/input/issue-3040/expect.js"));
|
||||
done();
|
||||
});
|
||||
setTimeout(function() {
|
||||
fs.writeFileSync(mapFile, read("test/input/pr-3040/input.js.map"));
|
||||
child.stdin.end(read("test/input/pr-3040/input.js"));
|
||||
fs.writeFileSync(mapFile, read("test/input/issue-3040/input.js.map"));
|
||||
child.stdin.end(read("test/input/issue-3040/input.js"));
|
||||
}, 1000);
|
||||
});
|
||||
it("Should work with --keep-fnames (mangle only)", function(done) {
|
||||
@@ -713,7 +712,7 @@ describe("bin/uglifyjs", function() {
|
||||
var command = uglifyjscmd + " test/input/enclose/input.js --enclose window,undefined:window --wrap exports";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, '(function(window,undefined){(function(exports){function enclose(){console.log("test enclose")}enclose()})(typeof exports=="undefined"?exports={}:exports)})(window);\n');
|
||||
assert.strictEqual(stdout, '(function(exports){(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window)})(typeof exports=="undefined"?exports={}:exports);\n');
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -260,6 +260,60 @@ describe("comments", function() {
|
||||
].join("\n"));
|
||||
});
|
||||
|
||||
it("Should handle programmatic AST insertions gracefully", function() {
|
||||
var ast = UglifyJS.parse([
|
||||
"function f() {",
|
||||
" //foo",
|
||||
" bar;",
|
||||
" return;",
|
||||
"}",
|
||||
].join("\n"));
|
||||
ast.body[0].body[0] = new UglifyJS.AST_Throw({value: ast.body[0].body[0].body});
|
||||
ast.body[0].body[1].value = new UglifyJS.AST_Number({value: 42});
|
||||
assert.strictEqual(ast.print_to_string({comments: "all"}), [
|
||||
"function f(){",
|
||||
"//foo",
|
||||
"throw bar;return 42}",
|
||||
].join("\n"));
|
||||
});
|
||||
|
||||
it("Should not duplicate sourceMappingURL", function() {
|
||||
var code = [
|
||||
"//# sourceMappingURL=input.map",
|
||||
"print(42);",
|
||||
"//# sourceMappingURL=input.map",
|
||||
"",
|
||||
"//# sourceMappingURL=input.map",
|
||||
"//# sourceMappingURL=input.map",
|
||||
"",
|
||||
].join("\n");
|
||||
|
||||
var result = UglifyJS.minify(code, {
|
||||
output: { comments: true },
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code, [
|
||||
"//# sourceMappingURL=input.map",
|
||||
"print(42);",
|
||||
"//# sourceMappingURL=input.map",
|
||||
"//# sourceMappingURL=input.map",
|
||||
"//# sourceMappingURL=input.map",
|
||||
].join("\n"));
|
||||
|
||||
result = UglifyJS.minify(code, {
|
||||
output: { comments: true },
|
||||
sourceMap: { url: "output.map" },
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code, [
|
||||
"//# sourceMappingURL=input.map",
|
||||
"print(42);",
|
||||
"//# sourceMappingURL=input.map",
|
||||
"//# sourceMappingURL=input.map",
|
||||
"//# sourceMappingURL=output.map",
|
||||
].join("\n"));
|
||||
});
|
||||
|
||||
describe("comment before constant", function() {
|
||||
var js = 'function f() { /*c1*/ var /*c2*/ foo = /*c3*/ false; return foo; }';
|
||||
|
||||
|
||||
@@ -365,7 +365,7 @@ describe("minify", function() {
|
||||
wrap: 'exports',
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code, '(function(window,undefined){(function(exports){function enclose(){console.log("test enclose")}enclose()})(typeof exports=="undefined"?exports={}:exports)})(window);');
|
||||
assert.strictEqual(result.code, '(function(exports){(function(window,undefined){function enclose(){console.log("test enclose")}enclose()})(window)})(typeof exports=="undefined"?exports={}:exports);');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
var assert = require("assert");
|
||||
var semver = require("semver");
|
||||
var spawn = require("child_process").spawn;
|
||||
|
||||
if (!process.env.UGLIFYJS_TEST_ALL) return;
|
||||
|
||||
function run(command, args, done) {
|
||||
spawn(command, args, {
|
||||
stdio: [ "ignore", 1, 2 ]
|
||||
}).on("exit", function(code) {
|
||||
assert.strictEqual(code, 0);
|
||||
done();
|
||||
});
|
||||
}
|
||||
|
||||
describe("test/benchmark.js", function() {
|
||||
this.timeout(10 * 60 * 1000);
|
||||
[
|
||||
"-b",
|
||||
"-b braces",
|
||||
"-m",
|
||||
"-mc passes=3",
|
||||
"-mc passes=3,toplevel",
|
||||
"-mc passes=3,unsafe",
|
||||
"-mc keep_fargs=false,passes=3",
|
||||
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
|
||||
].forEach(function(options) {
|
||||
it("Should pass with options " + options, function(done) {
|
||||
var args = options.split(/ /);
|
||||
args.unshift("test/benchmark.js");
|
||||
run(process.argv[0], args, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
if (semver.satisfies(process.version, "0.12")) return;
|
||||
describe("test/jetstream.js", function() {
|
||||
this.timeout(20 * 60 * 1000);
|
||||
[
|
||||
"-mc",
|
||||
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
|
||||
].forEach(function(options) {
|
||||
it("Should pass with options " + options, function(done) {
|
||||
var args = options.split(/ /);
|
||||
args.unshift("test/jetstream.js");
|
||||
args.push("-b", "beautify=false,webkit");
|
||||
run(process.argv[0], args, done);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -169,6 +169,18 @@ describe("sourcemaps", function() {
|
||||
map = JSON.parse(result.map);
|
||||
assert.ok(!("sourcesContent" in map));
|
||||
});
|
||||
it("Should parse the correct sourceMappingURL", function() {
|
||||
var result = UglifyJS.minify(read("./test/input/issue-3294/input.js"), {
|
||||
compress: { toplevel: true },
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
includeSources: true,
|
||||
url: "inline"
|
||||
}
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-3294/output.js", "utf8"));
|
||||
});
|
||||
});
|
||||
|
||||
describe("sourceMapInline", function() {
|
||||
|
||||
14
test/release/benchmark.js
Normal file
14
test/release/benchmark.js
Normal file
@@ -0,0 +1,14 @@
|
||||
require("./run")([
|
||||
"-b",
|
||||
"-b braces",
|
||||
"-m",
|
||||
"-mc passes=3",
|
||||
"-mc passes=3,toplevel",
|
||||
"-mc passes=3,unsafe",
|
||||
"-mc keep_fargs=false,passes=3",
|
||||
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
|
||||
].map(function(options) {
|
||||
var args = options.split(/ /);
|
||||
args.unshift("test/benchmark.js");
|
||||
return args;
|
||||
}));
|
||||
9
test/release/jetstream.js
Normal file
9
test/release/jetstream.js
Normal file
@@ -0,0 +1,9 @@
|
||||
require("./run")([
|
||||
"-mc",
|
||||
"-mc keep_fargs=false,passes=3,pure_getters,unsafe,unsafe_comps,unsafe_math,unsafe_proto",
|
||||
].map(function(options) {
|
||||
var args = options.split(/ /);
|
||||
args.unshift("test/jetstream.js");
|
||||
args.push("-b", "beautify=false,webkit");
|
||||
return args;
|
||||
}));
|
||||
16
test/release/run.js
Normal file
16
test/release/run.js
Normal file
@@ -0,0 +1,16 @@
|
||||
var child_process = require("child_process");
|
||||
|
||||
module.exports = function(tasks) {
|
||||
(function next() {
|
||||
if (!tasks.length) return;
|
||||
var args = tasks.shift();
|
||||
console.log();
|
||||
console.log("\u001B[36m$> " + args.join(" ") + "\u001B[39m");
|
||||
child_process.spawn(process.argv[0], args, {
|
||||
stdio: [ "ignore", 1, 2 ]
|
||||
}).on("exit", function(code) {
|
||||
if (code) process.exit(code);
|
||||
next();
|
||||
});
|
||||
})();
|
||||
};
|
||||
@@ -1,361 +0,0 @@
|
||||
#! /usr/bin/env node
|
||||
|
||||
var U = require("./node");
|
||||
var path = require("path");
|
||||
var fs = require("fs");
|
||||
var assert = require("assert");
|
||||
var sandbox = require("./sandbox");
|
||||
var semver = require("semver");
|
||||
|
||||
var tests_dir = path.dirname(module.filename);
|
||||
var failures = 0;
|
||||
var failed_files = {};
|
||||
var minify_options = require("./ufuzz.json").map(JSON.stringify);
|
||||
|
||||
run_compress_tests();
|
||||
if (failures) {
|
||||
console.error("\n!!! Failed " + failures + " test cases.");
|
||||
console.error("!!! " + Object.keys(failed_files).join(", "));
|
||||
process.exit(1);
|
||||
}
|
||||
console.log();
|
||||
require("./mocha.js");
|
||||
|
||||
/* -----[ utils ]----- */
|
||||
|
||||
function evaluate(code) {
|
||||
if (code instanceof U.AST_Node) code = make_code(code, { beautify: true });
|
||||
return new Function("return(" + code + ")")();
|
||||
}
|
||||
|
||||
function log() {
|
||||
console.log("%s", tmpl.apply(null, arguments));
|
||||
}
|
||||
|
||||
function make_code(ast, options) {
|
||||
var stream = U.OutputStream(options);
|
||||
ast.print(stream);
|
||||
return stream.get();
|
||||
}
|
||||
|
||||
function parse_test(file) {
|
||||
var script = fs.readFileSync(file, "utf8");
|
||||
// TODO try/catch can be removed after fixing https://github.com/mishoo/UglifyJS2/issues/348
|
||||
try {
|
||||
var ast = U.parse(script, {
|
||||
filename: file
|
||||
});
|
||||
} catch (e) {
|
||||
console.log("Caught error while parsing tests in " + file + "\n");
|
||||
console.log(e);
|
||||
throw e;
|
||||
}
|
||||
var tests = {};
|
||||
var tw = new U.TreeWalker(function(node, descend) {
|
||||
if (node instanceof U.AST_LabeledStatement
|
||||
&& tw.parent() instanceof U.AST_Toplevel) {
|
||||
var name = node.label.name;
|
||||
if (name in tests) {
|
||||
throw new Error('Duplicated test name "' + name + '" in ' + file);
|
||||
}
|
||||
tests[name] = get_one_test(name, node.body);
|
||||
return true;
|
||||
}
|
||||
if (!(node instanceof U.AST_Toplevel)) croak(node);
|
||||
});
|
||||
ast.walk(tw);
|
||||
return tests;
|
||||
|
||||
function croak(node) {
|
||||
throw new Error(tmpl("Can't understand test file {file} [{line},{col}]\n{code}", {
|
||||
file: file,
|
||||
line: node.start.line,
|
||||
col: node.start.col,
|
||||
code: make_code(node, { beautify: false })
|
||||
}));
|
||||
}
|
||||
|
||||
function read_string(stat) {
|
||||
if (stat.TYPE == "SimpleStatement") {
|
||||
var body = stat.body;
|
||||
switch(body.TYPE) {
|
||||
case "String":
|
||||
return body.value;
|
||||
case "Array":
|
||||
return body.elements.map(function(element) {
|
||||
if (element.TYPE !== "String")
|
||||
throw new Error("Should be array of strings");
|
||||
return element.value;
|
||||
}).join("\n");
|
||||
}
|
||||
}
|
||||
throw new Error("Should be string or array of strings");
|
||||
}
|
||||
|
||||
function get_one_test(name, block) {
|
||||
var test = { name: name, options: {} };
|
||||
var tw = new U.TreeWalker(function(node, descend) {
|
||||
if (node instanceof U.AST_Assign) {
|
||||
if (!(node.left instanceof U.AST_SymbolRef)) {
|
||||
croak(node);
|
||||
}
|
||||
var name = node.left.name;
|
||||
test[name] = evaluate(node.right);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof U.AST_LabeledStatement) {
|
||||
var label = node.label;
|
||||
assert.ok([
|
||||
"input",
|
||||
"expect",
|
||||
"expect_exact",
|
||||
"expect_warnings",
|
||||
"expect_stdout",
|
||||
"node_version",
|
||||
].indexOf(label.name) >= 0, tmpl("Unsupported label {name} [{line},{col}]", {
|
||||
name: label.name,
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
var stat = node.body;
|
||||
if (label.name == "expect_exact" || label.name == "node_version") {
|
||||
test[label.name] = read_string(stat);
|
||||
} else if (label.name == "expect_stdout") {
|
||||
var body = stat.body;
|
||||
if (body instanceof U.AST_Boolean) {
|
||||
test[label.name] = body.value;
|
||||
} else if (body instanceof U.AST_Call) {
|
||||
var ctor = global[body.expression.name];
|
||||
assert.ok(ctor === Error || ctor.prototype instanceof Error, tmpl("Unsupported expect_stdout format [{line},{col}]", {
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
test[label.name] = ctor.apply(null, body.args.map(function(node) {
|
||||
assert.ok(node instanceof U.AST_Constant, tmpl("Unsupported expect_stdout format [{line},{col}]", {
|
||||
line: label.start.line,
|
||||
col: label.start.col
|
||||
}));
|
||||
return node.value;
|
||||
}));
|
||||
} else {
|
||||
test[label.name] = read_string(stat) + "\n";
|
||||
}
|
||||
} else {
|
||||
test[label.name] = stat;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
block.walk(tw);
|
||||
return test;
|
||||
}
|
||||
}
|
||||
|
||||
// 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) {
|
||||
for (var i = 0; i < minify_options.length; i++) {
|
||||
var options = JSON.parse(minify_options[i]);
|
||||
if (options.compress) [
|
||||
"keep_fargs",
|
||||
"keep_fnames",
|
||||
].forEach(function(name) {
|
||||
if (name in orig_options) {
|
||||
options.compress[name] = orig_options[name];
|
||||
}
|
||||
});
|
||||
var options_formatted = JSON.stringify(options, null, 4);
|
||||
var result = U.minify(input_code, options);
|
||||
if (result.error) {
|
||||
log("!!! failed input reminify\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n--ERROR---\n{error}\n\n", {
|
||||
input: input_formatted,
|
||||
options: options_formatted,
|
||||
error: result.error,
|
||||
});
|
||||
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;
|
||||
}
|
||||
if (!sandbox.same_stdout(expect_stdout, stdout)) {
|
||||
log("!!! failed running reminified input\n---INPUT---\n{input}\n---OPTIONS---\n{options}\n---OUTPUT---\n{output}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||
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,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
function run_code(code) {
|
||||
var result = sandbox.run_code(code, true);
|
||||
return typeof result == "string" ? result.replace(/\u001b\[\d+m/g, "") : result;
|
||||
}
|
||||
|
||||
function run_compress_tests() {
|
||||
var dir = path.resolve(tests_dir, "compress");
|
||||
fs.readdirSync(dir).filter(function(name) {
|
||||
return /\.js$/i.test(name);
|
||||
}).forEach(function(file) {
|
||||
log("--- {file}", { file: file });
|
||||
function test_case(test) {
|
||||
log(" Running test [{name}]", { name: test.name });
|
||||
var output_options = test.beautify || {};
|
||||
var expect;
|
||||
if (test.expect) {
|
||||
expect = make_code(to_toplevel(test.expect, test.mangle), output_options);
|
||||
} else {
|
||||
expect = test.expect_exact;
|
||||
}
|
||||
var input = to_toplevel(test.input, test.mangle);
|
||||
var input_code = make_code(input);
|
||||
var input_formatted = make_code(test.input, {
|
||||
beautify: true,
|
||||
quote_style: 3,
|
||||
keep_quoted_props: true
|
||||
});
|
||||
try {
|
||||
U.parse(input_code);
|
||||
} catch (ex) {
|
||||
log("!!! Cannot parse input\n---INPUT---\n{input}\n--PARSE ERROR--\n{error}\n\n", {
|
||||
input: input_formatted,
|
||||
error: ex,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
var warnings_emitted = [];
|
||||
if (test.expect_warnings) {
|
||||
var expected_warnings = make_code(test.expect_warnings, {
|
||||
beautify: false,
|
||||
quote_style: 2, // force double quote to match JSON
|
||||
});
|
||||
U.AST_Node.log_function(function(text) {
|
||||
warnings_emitted.push(text);
|
||||
}, /"INFO: /.test(expected_warnings));
|
||||
}
|
||||
if (test.mangle && test.mangle.properties && test.mangle.properties.keep_quoted) {
|
||||
var quoted_props = test.mangle.properties.reserved;
|
||||
if (!Array.isArray(quoted_props)) quoted_props = [];
|
||||
test.mangle.properties.reserved = quoted_props;
|
||||
U.reserve_quoted_keys(input, quoted_props);
|
||||
}
|
||||
if (test.rename) {
|
||||
input.figure_out_scope(test.mangle);
|
||||
input.expand_names(test.mangle);
|
||||
}
|
||||
var cmp = new U.Compressor(test.options, true);
|
||||
var output = cmp.compress(input);
|
||||
output.figure_out_scope(test.mangle);
|
||||
if (test.mangle) {
|
||||
output.compute_char_frequency(test.mangle);
|
||||
output.mangle_names(test.mangle);
|
||||
if (test.mangle.properties) {
|
||||
output = U.mangle_properties(output, test.mangle.properties);
|
||||
}
|
||||
}
|
||||
output = make_code(output, output_options);
|
||||
if (expect != output) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n---EXPECTED---\n{expected}\n\n", {
|
||||
input: input_formatted,
|
||||
output: output,
|
||||
expected: expect
|
||||
});
|
||||
return false;
|
||||
}
|
||||
// expect == output
|
||||
try {
|
||||
U.parse(output);
|
||||
} catch (ex) {
|
||||
log("!!! Test matched expected result but cannot parse output\n---INPUT---\n{input}\n---OUTPUT---\n{output}\n--REPARSE ERROR--\n{error}\n\n", {
|
||||
input: input_formatted,
|
||||
output: output,
|
||||
error: ex,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (test.expect_warnings) {
|
||||
warnings_emitted = warnings_emitted.map(function(input) {
|
||||
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
|
||||
});
|
||||
var actual_warnings = JSON.stringify(warnings_emitted);
|
||||
if (expected_warnings != actual_warnings) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED WARNINGS---\n{expected_warnings}\n---ACTUAL WARNINGS---\n{actual_warnings}\n\n", {
|
||||
input: input_formatted,
|
||||
expected_warnings: expected_warnings,
|
||||
actual_warnings: actual_warnings,
|
||||
});
|
||||
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 === true) {
|
||||
test.expect_stdout = stdout;
|
||||
}
|
||||
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
|
||||
log("!!! Invalid input or expected stdout\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||
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,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
stdout = run_code(output);
|
||||
if (!sandbox.same_stdout(test.expect_stdout, stdout)) {
|
||||
log("!!! failed\n---INPUT---\n{input}\n---EXPECTED {expected_type}---\n{expected}\n---ACTUAL {actual_type}---\n{actual}\n\n", {
|
||||
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,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
if (!reminify(test.options, input_code, input_formatted, test.expect_stdout)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
var tests = parse_test(path.resolve(dir, file));
|
||||
for (var i in tests) if (tests.hasOwnProperty(i)) {
|
||||
if (!test_case(tests[i])) {
|
||||
failures++;
|
||||
failed_files[file] = 1;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function tmpl() {
|
||||
return U.string_template.apply(null, arguments);
|
||||
}
|
||||
|
||||
function to_toplevel(input, mangle_options) {
|
||||
if (!(input instanceof U.AST_BlockStatement)) throw new Error("Unsupported input syntax");
|
||||
var directive = true;
|
||||
var offset = input.start.line;
|
||||
var tokens = [];
|
||||
var toplevel = new U.AST_Toplevel(input.transform(new U.TreeTransformer(function(node) {
|
||||
if (U.push_uniq(tokens, node.start)) node.start.line -= offset;
|
||||
if (!directive || node === input) return;
|
||||
if (node instanceof U.AST_SimpleStatement && node.body instanceof U.AST_String) {
|
||||
return new U.AST_Directive(node.body);
|
||||
} else {
|
||||
directive = false;
|
||||
}
|
||||
})));
|
||||
toplevel.figure_out_scope(mangle_options);
|
||||
return toplevel;
|
||||
}
|
||||
@@ -1,29 +1,7 @@
|
||||
var semver = require("semver");
|
||||
var vm = require("vm");
|
||||
|
||||
function safe_log(arg, level) {
|
||||
if (arg) switch (typeof arg) {
|
||||
case "function":
|
||||
return arg.toString();
|
||||
case "object":
|
||||
if (/Error$/.test(arg.name)) return arg.toString();
|
||||
arg.constructor.toString();
|
||||
if (level--) for (var key in arg) {
|
||||
var desc = Object.getOwnPropertyDescriptor(arg, key);
|
||||
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
function log(msg) {
|
||||
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
|
||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||
return safe_log(arg, 3);
|
||||
}));
|
||||
}
|
||||
|
||||
var func_toString = new vm.Script([
|
||||
var setupContext = new vm.Script([
|
||||
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {",
|
||||
" f.toString = Function.prototype.toString;",
|
||||
"});",
|
||||
@@ -44,40 +22,51 @@ var func_toString = new vm.Script([
|
||||
' return "function(){}";',
|
||||
" };",
|
||||
"}();",
|
||||
"this;",
|
||||
]).join("\n"));
|
||||
|
||||
function createContext() {
|
||||
var ctx = vm.createContext(Object.defineProperty({}, "console", { value: { log: log } }));
|
||||
func_toString.runInContext(ctx);
|
||||
var global = setupContext.runInContext(ctx);
|
||||
return ctx;
|
||||
|
||||
function safe_log(arg, level) {
|
||||
if (arg) switch (typeof arg) {
|
||||
case "function":
|
||||
return arg.toString();
|
||||
case "object":
|
||||
if (arg === global) return "[object global]";
|
||||
if (/Error$/.test(arg.name)) return arg.toString();
|
||||
arg.constructor.toString();
|
||||
if (level--) for (var key in arg) {
|
||||
var desc = Object.getOwnPropertyDescriptor(arg, key);
|
||||
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
|
||||
}
|
||||
}
|
||||
return arg;
|
||||
}
|
||||
|
||||
var context;
|
||||
exports.run_code = function(code, reuse) {
|
||||
function log(msg) {
|
||||
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
|
||||
return console.log.apply(console, [].map.call(arguments, function(arg) {
|
||||
return safe_log(arg, 3);
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -960,16 +960,16 @@ if (require.main !== module) {
|
||||
}
|
||||
|
||||
function println(msg) {
|
||||
if (typeof msg != "undefined") process.stdout.write(msg);
|
||||
if (typeof msg != "undefined") process.stdout.write(typeof msg == "string" ? msg : msg.stack);
|
||||
process.stdout.write("\n");
|
||||
}
|
||||
|
||||
function errorln(msg) {
|
||||
if (typeof msg != "undefined") process.stderr.write(msg);
|
||||
if (typeof msg != "undefined") process.stderr.write(typeof msg == "string" ? msg : msg.stack);
|
||||
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,
|
||||
@@ -980,8 +980,8 @@ function try_beautify(code, result, printfn) {
|
||||
});
|
||||
if (beautified.error) {
|
||||
printfn("// !!! beautify failed !!!");
|
||||
printfn(beautified.error.stack);
|
||||
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code), result)) {
|
||||
printfn(beautified.error);
|
||||
} else if (sandbox.same_stdout(sandbox.run_code(beautified.code, toplevel), result)) {
|
||||
printfn("// (beautified)");
|
||||
printfn(beautified.code);
|
||||
return;
|
||||
@@ -1007,9 +1007,9 @@ function log_suspects(minify_options, component) {
|
||||
var result = UglifyJS.minify(original_code, m);
|
||||
if (result.error) {
|
||||
errorln("Error testing options." + component + "." + name);
|
||||
errorln(result.error.stack);
|
||||
errorln(result.error);
|
||||
} else {
|
||||
var r = sandbox.run_code(result.code);
|
||||
var r = sandbox.run_code(result.code, m.toplevel);
|
||||
return sandbox.same_stdout(original_result, r);
|
||||
}
|
||||
}
|
||||
@@ -1029,9 +1029,9 @@ function log_rename(options) {
|
||||
var result = UglifyJS.minify(original_code, m);
|
||||
if (result.error) {
|
||||
errorln("Error testing options.rename");
|
||||
errorln(result.error.stack);
|
||||
errorln(result.error);
|
||||
} 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");
|
||||
@@ -1045,31 +1045,31 @@ function log(options) {
|
||||
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(original_result);
|
||||
errorln("uglified result:");
|
||||
errorln(typeof uglify_result == "string" ? uglify_result : uglify_result.stack);
|
||||
errorln(uglify_result);
|
||||
} else {
|
||||
errorln("// !!! uglify failed !!!");
|
||||
errorln(uglify_code.stack);
|
||||
if (typeof original_result != "string") {
|
||||
errorln(uglify_code);
|
||||
if (errored) {
|
||||
errorln();
|
||||
errorln();
|
||||
errorln("original stacktrace:");
|
||||
errorln(original_result.stack);
|
||||
errorln(original_result);
|
||||
}
|
||||
}
|
||||
errorln("minify(options):");
|
||||
options = JSON.parse(options);
|
||||
errorln(JSON.stringify(options, null, 2));
|
||||
errorln();
|
||||
if (!ok && typeof uglify_code == "string") {
|
||||
@@ -1084,34 +1084,38 @@ var fallback_options = [ JSON.stringify({
|
||||
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:");
|
||||
println(original_result.stack);
|
||||
println(original_result);
|
||||
println();
|
||||
}
|
||||
if (!ok && isFinite(num_iterations)) {
|
||||
|
||||
@@ -2993,6 +2993,7 @@
|
||||
"caption",
|
||||
"caption-side",
|
||||
"captionSide",
|
||||
"capture",
|
||||
"captureEvents",
|
||||
"captureStackTrace",
|
||||
"captureStream",
|
||||
@@ -4957,6 +4958,7 @@
|
||||
"oncandidatewindowupdate",
|
||||
"oncanplay",
|
||||
"oncanplaythrough",
|
||||
"once",
|
||||
"oncellchange",
|
||||
"onchange",
|
||||
"onchargingchange",
|
||||
@@ -5343,6 +5345,7 @@
|
||||
"parseInt",
|
||||
"part",
|
||||
"participants",
|
||||
"passive",
|
||||
"password",
|
||||
"pasteHTML",
|
||||
"path",
|
||||
|
||||
@@ -1,21 +1,19 @@
|
||||
var fs = require("fs");
|
||||
|
||||
exports.FILES = [
|
||||
"../lib/utils.js",
|
||||
"../lib/ast.js",
|
||||
"../lib/parse.js",
|
||||
"../lib/transform.js",
|
||||
"../lib/scope.js",
|
||||
"../lib/output.js",
|
||||
"../lib/compress.js",
|
||||
"../lib/sourcemap.js",
|
||||
"../lib/mozilla-ast.js",
|
||||
"../lib/propmangle.js",
|
||||
"../lib/minify.js",
|
||||
"./exports.js",
|
||||
].map(function(file) {
|
||||
return require.resolve(file);
|
||||
});
|
||||
require.resolve("../lib/utils.js"),
|
||||
require.resolve("../lib/ast.js"),
|
||||
require.resolve("../lib/parse.js"),
|
||||
require.resolve("../lib/transform.js"),
|
||||
require.resolve("../lib/scope.js"),
|
||||
require.resolve("../lib/output.js"),
|
||||
require.resolve("../lib/compress.js"),
|
||||
require.resolve("../lib/sourcemap.js"),
|
||||
require.resolve("../lib/mozilla-ast.js"),
|
||||
require.resolve("../lib/propmangle.js"),
|
||||
require.resolve("../lib/minify.js"),
|
||||
require.resolve("./exports.js"),
|
||||
];
|
||||
|
||||
new Function("MOZ_SourceMap", "exports", function() {
|
||||
var code = exports.FILES.map(function(file) {
|
||||
|
||||
Reference in New Issue
Block a user