Compare commits

...

7 Commits

Author SHA1 Message Date
Alex Lam S.L
f1a77e4fc0 v3.5.5 2019-04-19 15:22:46 +08:00
Alex Lam S.L
b55a2fd531 fix corner case in functions (#3367)
fixes #3366
2019-04-19 02:55:43 +08:00
Alex Lam S.L
e8a2c0b5bf fix corner case in functions (#3365)
fixes #3364
2019-04-18 17:03:52 +08:00
Alex Lam S.L
21cd7e3f57 reduce test exports (#3361) 2019-04-17 16:19:08 +08:00
Alex Lam S.L
5172ba5f2a introduce functions (#3360)
`var f = function() {};` => `function f() {}`
2019-04-15 22:23:11 +08:00
Alex Lam S.L
a57b069409 v3.5.4 2019-04-10 02:40:42 +08:00
Alex Lam S.L
4454656c3b update dependencies (#3358)
- commander@2.20.0
- semver@6.0.0
2019-04-10 02:39:56 +08:00
10 changed files with 375 additions and 64 deletions

View File

@@ -636,6 +636,9 @@ If you're using the `X-SourceMap` header instead, you can just omit `sourceMap.u
- `expression` (default: `false`) -- Pass `true` to preserve completion values
from terminal statements without `return`, e.g. in bookmarklets.
- `functions` (default: `true`) -- convert declarations from `var`to `function`
whenever possible.
- `global_defs` (default: `{}`) -- see [conditional compilation](#conditional-compilation)
- `hoist_funs` (default: `false`) -- hoist function declarations

View File

@@ -60,6 +60,7 @@ function Compressor(options, false_by_default) {
drop_debugger : !false_by_default,
evaluate : !false_by_default,
expression : false,
functions : !false_by_default,
global_defs : false,
hoist_funs : false,
hoist_props : !false_by_default,
@@ -150,7 +151,7 @@ Compressor.prototype = new TreeTransformer;
merge(Compressor.prototype, {
option: function(key) { return this.options[key] },
exposed: function(def) {
if (def.global) for (var i = 0, len = def.orig.length; i < len; i++)
if (def.global) for (var i = 0; i < def.orig.length; i++)
if (!this.toplevel[def.orig[i] instanceof AST_SymbolDefun ? "funcs" : "vars"])
return true;
return false;
@@ -1241,7 +1242,7 @@ merge(Compressor.prototype, {
// Scan case expressions first in a switch statement
if (node instanceof AST_Switch) {
node.expression = node.expression.transform(scanner);
for (var i = 0, len = node.body.length; !abort && i < len; i++) {
for (var i = 0; !abort && i < node.body.length; i++) {
var branch = node.body[i];
if (branch instanceof AST_Case) {
if (!hit) {
@@ -1887,7 +1888,7 @@ merge(Compressor.prototype, {
}
function next_index(i) {
for (var j = i + 1, len = statements.length; j < len; j++) {
for (var j = i + 1; j < statements.length; j++) {
var stat = statements[j];
if (!(stat instanceof AST_Var && declarations_only(stat))) {
break;
@@ -1978,7 +1979,7 @@ merge(Compressor.prototype, {
function to_simple_statement(block, decls) {
if (!(block instanceof AST_BlockStatement)) return block;
var stat = null;
for (var i = 0, len = block.body.length; i < len; i++) {
for (var i = 0; i < block.body.length; i++) {
var line = block.body[i];
if (line instanceof AST_Var && declarations_only(line)) {
decls.push(line);
@@ -2118,7 +2119,7 @@ merge(Compressor.prototype, {
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
for (var i = 0, j = -1; i < statements.length; i++) {
var stat = statements[i];
var prev = statements[j];
if (stat instanceof AST_Definitions) {
@@ -2719,7 +2720,7 @@ merge(Compressor.prototype, {
def(AST_Array, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
var elements = [];
for (var i = 0, len = this.elements.length; i < len; i++) {
for (var i = 0; i < this.elements.length; i++) {
var element = this.elements[i];
var value = element._eval(compressor, cached, depth);
if (element === value) return this;
@@ -2732,7 +2733,7 @@ merge(Compressor.prototype, {
def(AST_Object, function(compressor, cached, depth) {
if (compressor.option("unsafe")) {
var val = {};
for (var i = 0, len = this.properties.length; i < len; i++) {
for (var i = 0; i < this.properties.length; i++) {
var prop = this.properties[i];
var key = prop.key;
if (key instanceof AST_Symbol) {
@@ -2927,7 +2928,7 @@ merge(Compressor.prototype, {
if (!native_fn || !native_fn[key]) return this;
}
var args = [];
for (var i = 0, len = this.args.length; i < len; i++) {
for (var i = 0; i < this.args.length; i++) {
var arg = this.args[i];
var value = arg._eval(compressor, cached, depth);
if (arg === value) return this;
@@ -3463,9 +3464,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_Definitions && scope === self) {
node.definitions.forEach(function(def) {
var node_def = def.name.definition();
if (def.name instanceof AST_SymbolVar) {
var_defs_by_id.add(node_def.id, def);
}
var_defs_by_id.add(node_def.id, def);
if (!drop_vars) {
if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true;
@@ -3572,29 +3571,40 @@ merge(Compressor.prototype, {
if (def.value && sym.id in fixed_ids && fixed_ids[sym.id] !== def) {
def.value = def.value.drop_side_effect_free(compressor);
}
if (def.name instanceof AST_SymbolVar) {
var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) {
var ref = make_node(AST_SymbolRef, def.name, def.name);
sym.references.push(ref);
var assign = make_node(AST_Assign, def, {
operator: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[sym.id] = assign;
}
side_effects.push(assign.transform(tt));
var var_defs = var_defs_by_id.get(sym.id);
if (var_defs.length > 1 && (!def.value || sym.orig.indexOf(def.name) > sym.eliminated)) {
compressor.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
if (def.value) {
var ref = make_node(AST_SymbolRef, def.name, def.name);
sym.references.push(ref);
var assign = make_node(AST_Assign, def, {
operator: "=",
left: ref,
right: def.value
});
if (fixed_ids[sym.id] === def) {
fixed_ids[sym.id] = assign;
}
remove(var_defs, def);
sym.eliminated++;
return;
side_effects.push(assign.transform(tt));
}
remove(var_defs, def);
sym.eliminated++;
return;
}
if (def.value) {
if (!def.value) {
head.push(def);
} else if (compressor.option("functions")
&& def.value === def.name.fixed_value()
&& def.value instanceof AST_Function
&& !def.value.name
&& !def.value.variables.get(def.name.name)
&& (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
compressor.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
var defun = make_node(AST_Defun, def, def.value);
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
def.name.scope.resolve().def_function(defun.name);
body.push(defun);
} else {
if (side_effects.length > 0) {
if (tail.length > 0) {
side_effects.push(def.value);
@@ -3607,8 +3617,6 @@ merge(Compressor.prototype, {
side_effects = [];
}
tail.push(def);
} else {
head.push(def);
}
} else if (sym.orig[0] instanceof AST_SymbolCatch) {
var value = def.value && def.value.drop_side_effect_free(compressor);
@@ -4669,7 +4677,7 @@ merge(Compressor.prototype, {
&& !fn.uses_arguments
&& !fn.pinned()) {
var pos = 0, last = 0;
for (var i = 0, len = self.args.length; i < len; i++) {
for (var i = 0; i < self.args.length; i++) {
var trim = i >= fn.argnames.length;
if (trim || fn.argnames[i].__unused) {
var node = self.args[i].drop_side_effect_free(compressor);
@@ -5047,7 +5055,7 @@ merge(Compressor.prototype, {
}
function can_inject_args(catches, safe_to_inject) {
for (var i = 0, len = fn.argnames.length; i < len; i++) {
for (var i = 0; i < fn.argnames.length; i++) {
var arg = fn.argnames[i];
if (arg.__unused) continue;
if (!safe_to_inject
@@ -5062,7 +5070,7 @@ merge(Compressor.prototype, {
}
function can_inject_vars(catches, safe_to_inject) {
for (var i = 0, len = fn.body.length; i < len; i++) {
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
if (!(stat instanceof AST_Var)) continue;
if (!safe_to_inject) return false;
@@ -5142,10 +5150,10 @@ merge(Compressor.prototype, {
function flatten_vars(decls, expressions) {
var pos = expressions.length;
for (var i = 0, lines = fn.body.length; i < lines; i++) {
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
if (!(stat instanceof AST_Var)) continue;
for (var j = 0, defs = stat.definitions.length; j < defs; j++) {
for (var j = 0; j < stat.definitions.length; j++) {
var var_def = stat.definitions[j];
var name = var_def.name;
var redef = name.definition().redefined();

View File

@@ -123,7 +123,7 @@ function OutputStream(options) {
});
} : function(str) {
var s = "";
for (var i = 0, len = str.length; i < len; i++) {
for (var i = 0; i < str.length; i++) {
if (is_surrogate_pair_head(str[i]) && !is_surrogate_pair_tail(str[i + 1])
|| is_surrogate_pair_tail(str[i]) && !is_surrogate_pair_head(str[i - 1])) {
s += "\\u" + str.charCodeAt(i).toString(16);

View File

@@ -164,10 +164,6 @@ function is_unicode_connector_punctuation(ch) {
return UNICODE.connector_punctuation.test(ch);
}
function is_identifier(name) {
return !RESERVED_WORDS[name] && /^[a-z_$][a-z0-9_$]*$/i.test(name);
}
function is_identifier_start(code) {
return code == 36 || code == 95 || is_letter(code);
}
@@ -272,10 +268,8 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
function find_eol() {
var text = S.text;
for (var i = S.pos, n = S.text.length; i < n; ++i) {
var ch = text[i];
if (NEWLINE_CHARS[ch])
return i;
for (var i = S.pos; i < S.text.length; ++i) {
if (NEWLINE_CHARS[text[i]]) return i;
}
return -1;
}

View File

@@ -61,8 +61,6 @@ SymbolDef.next_id = 1;
SymbolDef.prototype = {
unmangleable: function(options) {
if (!options) options = {};
return this.global && !options.toplevel
|| this.undeclared
|| !options.eval && this.scope.pinned()
@@ -296,7 +294,7 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
AST_Lambda.DEFMETHOD("resolve", return_this);
AST_Scope.DEFMETHOD("resolve", function() {
return this.parent_scope;
return this.parent_scope.resolve();
});
AST_Toplevel.DEFMETHOD("resolve", return_this);
@@ -333,7 +331,7 @@ function next_mangled_name(scope, options, def) {
} while (scope = scope.parent_scope);
});
var name;
for (var i = 0, len = holes.length; i < len; i++) {
for (var i = 0; i < holes.length; i++) {
name = base54(holes[i]);
if (names[name]) continue;
holes.splice(i, 1);
@@ -342,7 +340,7 @@ function next_mangled_name(scope, options, def) {
}
while (true) {
name = base54(++scope.cname);
if (in_use[name] || !is_identifier(name) || options.reserved.has[name]) continue;
if (in_use[name] || RESERVED_WORDS[name] || options.reserved.has[name]) continue;
if (!names[name]) break;
holes.push(scope.cname);
}
@@ -422,7 +420,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var name;
do {
name = base54(++lname);
} while (!is_identifier(name));
} while (RESERVED_WORDS[name]);
node.mangled_name = name;
return true;
}
@@ -493,7 +491,7 @@ AST_Toplevel.DEFMETHOD("expand_names", function(options) {
var name;
do {
name = base54(cname++);
} while (avoid[name] || !is_identifier(name));
} while (avoid[name] || RESERVED_WORDS[name]);
return name;
}
@@ -555,7 +553,7 @@ var base54 = (function() {
var freq = Object.create(null);
function init(chars) {
var array = [];
for (var i = 0, len = chars.length; i < len; i++) {
for (var i = 0; i < chars.length; i++) {
var ch = chars[i];
array.push(ch);
freq[ch] = -1e-2 * i;

View File

@@ -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.3",
"version": "3.5.5",
"engines": {
"node": ">=0.8.0"
},
@@ -23,12 +23,12 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.19.0",
"commander": "~2.20.0",
"source-map": "~0.6.1"
},
"devDependencies": {
"acorn": "~6.1.1",
"semver": "~5.6.0"
"semver": "~6.0.0"
},
"scripts": {
"test": "node test/run-tests.js"

View File

@@ -2703,3 +2703,312 @@ loop_inline: {
}
expect_stdout: "undefined"
}
functions: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
!function() {
var a = function() {
return "a";
};
var b = function x() {
return !!x;
};
var c = function(c) {
return c;
};
if (c(b(a()))) {
var d = function() {};
var e = function y() {
return typeof y;
};
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect: {
!function() {
function a() {
return "a";
}
var b = function x() {
return !!x;
};
var c = function(c) {
return c;
};
if (c(b(a()))) {
function d() {}
var e = function y() {
return typeof y;
};
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect_stdout: "a true 42 function function function"
}
functions_use_strict: {
options = {
functions: true,
reduce_vars: true,
unused: true,
}
input: {
"use strict";
!function() {
var a = function() {
return "a";
};
var b = function x() {
return !!x;
};
var c = function(c) {
return c;
};
if (c(b(a()))) {
var d = function() {};
var e = function y() {
return typeof y;
};
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect: {
"use strict";
!function() {
function a() {
return "a";
}
var b = function x() {
return !!x;
};
var c = function(c) {
return c;
};
if (c(b(a()))) {
var d = function() {};
var e = function y() {
return typeof y;
};
var f = function(f) {
return f;
};
console.log(a(d()), b(e()), c(f(42)), typeof d, e(), typeof f);
}
}();
}
expect_stdout: "a true 42 function function function"
}
issue_2437: {
options = {
collapse_vars: true,
conditionals: true,
functions: true,
inline: true,
join_vars: true,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
function foo() {
return bar();
}
function bar() {
if (xhrDesc) {
var req = new XMLHttpRequest();
var result = !!req.onreadystatechange;
Object.defineProperty(XMLHttpRequest.prototype, 'onreadystatechange', xhrDesc || {});
return result;
} else {
var req = new XMLHttpRequest();
var detectFunc = function(){};
req.onreadystatechange = detectFunc;
var result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
req.onreadystatechange = null;
return result;
}
}
console.log(foo());
}
expect: {
console.log(function() {
if (xhrDesc) {
var result = !!(req = new XMLHttpRequest()).onreadystatechange;
return Object.defineProperty(XMLHttpRequest.prototype, "onreadystatechange", xhrDesc || {}),
result;
}
function detectFunc() {}
var req;
(req = new XMLHttpRequest()).onreadystatechange = detectFunc;
result = req[SYMBOL_FAKE_ONREADYSTATECHANGE_1] === detectFunc;
return req.onreadystatechange = null, result;
}());
}
}
issue_2485: {
options = {
functions: true,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
var foo = function(bar) {
var n = function(a, b) {
return a + b;
};
var sumAll = function(arg) {
return arg.reduce(n, 0);
};
var runSumAll = function(arg) {
return sumAll(arg);
};
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect: {
var foo = function(bar) {
function n(a, b) {
return a + b;
}
function runSumAll(arg) {
return function(arg) {
return arg.reduce(n, 0);
}(arg);
}
bar.baz = function(arg) {
var n = runSumAll(arg);
return (n.get = 1), n;
};
return bar;
};
var bar = foo({});
console.log(bar.baz([1, 2, 3]));
}
expect_stdout: "6"
}
issue_3364: {
options = {
functions: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
mangle = {}
input: {
var s = 2, a = 100, b = 10, c = 0;
function f(p, e, r) {
try {
for (var i = 1; i-- > 0;)
var a = function(x) {
function g(y) {
y && y[a++];
}
var x = g(--s >= 0 && f(c++));
for (var j = 1; --j > 0;);
}();
} catch (e) {
try {
return;
} catch (z) {
for (var k = 1; --k > 0;) {
for (var l = 1; l > 0; --l) {
var n = function() {};
for (var k in n)
var o = (n, k);
}
}
}
}
}
var r = f();
console.log(c);
}
expect: {
var s = 2, c = 0;
(function n(r, o, a) {
try {
for (var f = 1; f-- >0;)
var t = function(r) {
(function(r) {
r && r[t++];
})(--s >= 0 && n(c++));
for (var o = 1; --o > 0;);
}();
} catch (o) {
try {
return;
} catch (r) {
for (var v = 1; --v > 0;)
for (var i = 1; i > 0;--i) {
function u() {}
for (var v in u);
}
}
}
})();
console.log(c);
}
expect_stdout: "2"
}
issue_3366: {
options = {
functions: true,
inline: true,
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f() {
function g() {
return function() {};
}
var a = g();
(function() {
this && a && console.log("PASS");
})();
}
f();
}
expect: {
(function() {
function a() {}
(function() {
this && a && console.log("PASS");
})();
})();
}
expect_stdout: "PASS"
}

View File

@@ -1,7 +1,5 @@
exports["base54"] = base54;
exports["Compressor"] = Compressor;
exports["defaults"] = defaults;
exports["is_identifier"] = is_identifier;
exports["JS_Parse_Error"] = JS_Parse_Error;
exports["mangle_properties"] = mangle_properties;
exports["minify"] = minify;
@@ -9,7 +7,6 @@ exports["OutputStream"] = OutputStream;
exports["parse"] = parse;
exports["push_uniq"] = push_uniq;
exports["reserve_quoted_keys"] = reserve_quoted_keys;
exports["SourceMap"] = SourceMap;
exports["string_template"] = string_template;
exports["tokenizer"] = tokenizer;
exports["TreeTransformer"] = TreeTransformer;

View File

@@ -3,7 +3,7 @@
"use strict";
var site = "https://browserbench.org/JetStream";
var site = "https://browserbench.org/JetStream1.1";
if (typeof phantom == "undefined") {
require("../tools/exit");
var args = process.argv.slice(2);

View File

@@ -73,8 +73,10 @@ exports.run_code = function(code, reuse) {
process.stdout.write = original_write;
if (!reuse || code.indexOf(".prototype") >= 0) {
context = null;
} else for (var key in context) {
delete context[key];
} else {
vm.runInContext(Object.keys(context).map(function(name) {
return "delete " + name;
}).join("\n"), context);
}
}
};