Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e4881245d9 | ||
|
|
354fec8a9c | ||
|
|
11cdab745d | ||
|
|
a89d424a0b | ||
|
|
429d2b56b7 | ||
|
|
2ea96549c5 | ||
|
|
fba008e298 | ||
|
|
c37a8e927e | ||
|
|
413bbe0480 | ||
|
|
34075fc4c4 | ||
|
|
e5436ca566 | ||
|
|
cfde686eab | ||
|
|
a206964c0a | ||
|
|
c56d89f804 | ||
|
|
c215706350 | ||
|
|
d3b93ec682 | ||
|
|
6fe20dbe33 | ||
|
|
7ccdf3337b | ||
|
|
dafed54764 | ||
|
|
a84beafd1b | ||
|
|
f01cc1e413 |
@@ -767,9 +767,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
|
||||
|
||||
71
bin/uglifyjs
71
bin/uglifyjs
@@ -63,7 +63,7 @@ if (program.configFile) {
|
||||
}
|
||||
}
|
||||
if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
|
||||
fatal("ERROR: cannot write source map to STDOUT");
|
||||
fatal("cannot write source map to STDOUT");
|
||||
}
|
||||
[
|
||||
"compress",
|
||||
@@ -78,6 +78,15 @@ if (!program.output && program.sourceMap && program.sourceMap.url != "inline") {
|
||||
options[name] = program[name];
|
||||
}
|
||||
});
|
||||
if (program.verbose) {
|
||||
options.warnings = "verbose";
|
||||
} else if (program.warn) {
|
||||
options.warnings = true;
|
||||
}
|
||||
if (options.warnings) {
|
||||
UglifyJS.AST_Node.log_function(print_error, options.warnings == "verbose");
|
||||
delete options.warnings;
|
||||
}
|
||||
if (program.beautify) {
|
||||
options.output = typeof program.beautify == "object" ? program.beautify : {};
|
||||
if (!("beautify" in options.output)) {
|
||||
@@ -124,7 +133,7 @@ if (program.parse) {
|
||||
if (!program.parse.acorn && !program.parse.spidermonkey) {
|
||||
options.parse = program.parse;
|
||||
} else if (program.sourceMap && program.sourceMap.content == "inline") {
|
||||
fatal("ERROR: inline source map only works with built-in parser");
|
||||
fatal("inline source map only works with built-in parser");
|
||||
}
|
||||
}
|
||||
if (~program.rawArgs.indexOf("--rename")) {
|
||||
@@ -144,15 +153,8 @@ if (typeof program.sourceMap == "object" && "base" in program.sourceMap) {
|
||||
};
|
||||
}();
|
||||
}
|
||||
if (program.verbose) {
|
||||
options.warnings = "verbose";
|
||||
} else if (program.warn) {
|
||||
options.warnings = true;
|
||||
}
|
||||
if (program.self) {
|
||||
if (program.args.length) {
|
||||
print_error("WARN: Ignoring input files since --self was passed");
|
||||
}
|
||||
if (program.args.length) UglifyJS.AST_Node.warn("Ignoring input files since --self was passed");
|
||||
if (!options.wrap) options.wrap = "UglifyJS";
|
||||
simple_glob(UglifyJS.FILES).forEach(function(name) {
|
||||
files[convert_path(name)] = read_file(name);
|
||||
@@ -180,12 +182,9 @@ function convert_ast(fn) {
|
||||
}
|
||||
|
||||
function run() {
|
||||
UglifyJS.AST_Node.warn_function = function(msg) {
|
||||
print_error("WARN: " + msg);
|
||||
};
|
||||
var content = program.sourceMap && program.sourceMap.content;
|
||||
if (content && content != "inline") {
|
||||
print_error("INFO: Using input source map: " + content);
|
||||
UglifyJS.AST_Node.info("Using input source map: " + content);
|
||||
options.sourceMap.content = read_file(content, content);
|
||||
}
|
||||
if (program.timings) options.timings = true;
|
||||
@@ -216,24 +215,26 @@ function run() {
|
||||
var ex = result.error;
|
||||
if (ex.name == "SyntaxError") {
|
||||
print_error("Parse error at " + ex.filename + ":" + ex.line + "," + ex.col);
|
||||
var col = ex.col;
|
||||
var lines = files[ex.filename].split(/\r?\n/);
|
||||
var line = lines[ex.line - 1];
|
||||
if (!line && !col) {
|
||||
line = lines[ex.line - 2];
|
||||
col = line.length;
|
||||
}
|
||||
if (line) {
|
||||
var limit = 70;
|
||||
if (col > limit) {
|
||||
line = line.slice(col - limit);
|
||||
col = limit;
|
||||
var file = files[ex.filename];
|
||||
if (file) {
|
||||
var col = ex.col;
|
||||
var lines = file.split(/\r?\n/);
|
||||
var line = lines[ex.line - 1];
|
||||
if (!line && !col) {
|
||||
line = lines[ex.line - 2];
|
||||
col = line.length;
|
||||
}
|
||||
if (line) {
|
||||
var limit = 70;
|
||||
if (col > limit) {
|
||||
line = line.slice(col - limit);
|
||||
col = limit;
|
||||
}
|
||||
print_error(line.slice(0, 80));
|
||||
print_error(line.slice(0, col).replace(/\S/g, " ") + "^");
|
||||
}
|
||||
print_error(line.slice(0, 80));
|
||||
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));
|
||||
}
|
||||
@@ -293,7 +294,11 @@ function run() {
|
||||
}
|
||||
|
||||
function fatal(message) {
|
||||
if (message instanceof Error) message = message.stack.replace(/^\S*?Error:/, "ERROR:")
|
||||
if (message instanceof Error) {
|
||||
message = message.stack.replace(/^\S*?Error:/, "ERROR:")
|
||||
} else {
|
||||
message = "ERROR: " + message;
|
||||
}
|
||||
print_error(message);
|
||||
process.exit(1);
|
||||
}
|
||||
@@ -370,9 +375,9 @@ function parse_js(flag) {
|
||||
});
|
||||
}
|
||||
}));
|
||||
} catch(ex) {
|
||||
} catch (ex) {
|
||||
if (flag) {
|
||||
fatal("Error parsing arguments for '" + flag + "': " + value);
|
||||
fatal("cannot parse arguments for '" + flag + "': " + value);
|
||||
} else {
|
||||
options[value] = null;
|
||||
}
|
||||
|
||||
47
lib/ast.js
47
lib/ast.js
@@ -118,9 +118,25 @@ var AST_Node = DEFNODE("Node", "start end", {
|
||||
}
|
||||
}, null);
|
||||
|
||||
AST_Node.warn = function(txt, props) {
|
||||
if (AST_Node.warn_function) AST_Node.warn_function(string_template(txt, props));
|
||||
};
|
||||
(AST_Node.log_function = function(fn, verbose) {
|
||||
var printed = Object.create(null);
|
||||
if (fn) {
|
||||
AST_Node.info = verbose ? function(text, props) {
|
||||
log("INFO: " + string_template(text, props));
|
||||
} : noop;
|
||||
AST_Node.warn = function(text, props) {
|
||||
log("WARN: " + string_template(text, props));
|
||||
};
|
||||
} else {
|
||||
AST_Node.info = AST_Node.warn = noop;
|
||||
}
|
||||
|
||||
function log(msg) {
|
||||
if (printed[msg]) return;
|
||||
printed[msg] = true;
|
||||
fn(msg);
|
||||
}
|
||||
})();
|
||||
|
||||
/* -----[ statements ]----- */
|
||||
|
||||
@@ -321,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;
|
||||
@@ -343,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);
|
||||
}
|
||||
@@ -773,7 +798,7 @@ var AST_Label = DEFNODE("Label", "references", {
|
||||
}
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_SymbolRef = DEFNODE("SymbolRef", null, {
|
||||
var AST_SymbolRef = DEFNODE("SymbolRef", "fixed", {
|
||||
$documentation: "Reference to some symbol (not definition/declaration)",
|
||||
}, AST_Symbol);
|
||||
|
||||
|
||||
438
lib/compress.js
438
lib/compress.js
@@ -94,7 +94,6 @@ function Compressor(options, false_by_default) {
|
||||
unsafe_regexp : false,
|
||||
unsafe_undefined: false,
|
||||
unused : !false_by_default,
|
||||
warnings : false,
|
||||
}, true);
|
||||
var global_defs = this.options["global_defs"];
|
||||
if (typeof global_defs == "object") for (var key in global_defs) {
|
||||
@@ -144,7 +143,6 @@ function Compressor(options, false_by_default) {
|
||||
};
|
||||
var sequences = this.options["sequences"];
|
||||
this.sequences_limit = sequences == 1 ? 800 : sequences | 0;
|
||||
this.warnings_produced = {};
|
||||
}
|
||||
|
||||
Compressor.prototype = new TreeTransformer;
|
||||
@@ -175,7 +173,7 @@ merge(Compressor.prototype, {
|
||||
node.walk(new TreeWalker(function() {
|
||||
count++;
|
||||
}));
|
||||
this.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
|
||||
AST_Node.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
|
||||
if (count < min_count) {
|
||||
min_count = count;
|
||||
stopping = false;
|
||||
@@ -191,24 +189,6 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return node;
|
||||
},
|
||||
info: function() {
|
||||
if (this.options.warnings == "verbose") {
|
||||
AST_Node.warn.apply(AST_Node, arguments);
|
||||
}
|
||||
},
|
||||
warn: function(text, props) {
|
||||
if (this.options.warnings) {
|
||||
// only emit unique warnings
|
||||
var message = string_template(text, props);
|
||||
if (!(message in this.warnings_produced)) {
|
||||
this.warnings_produced[message] = true;
|
||||
AST_Node.warn.apply(AST_Node, arguments);
|
||||
}
|
||||
}
|
||||
},
|
||||
clear_warnings: function() {
|
||||
this.warnings_produced = {};
|
||||
},
|
||||
before: function(node, descend, in_list) {
|
||||
if (node._squeezed) return node;
|
||||
var is_scope = node instanceof AST_Scope;
|
||||
@@ -551,6 +531,7 @@ merge(Compressor.prototype, {
|
||||
mark_assignment_to_arguments(sym);
|
||||
return;
|
||||
}
|
||||
if (sym.fixed) delete sym.fixed;
|
||||
var d = sym.definition();
|
||||
var safe = safe_to_assign(tw, d, sym.scope, node.right);
|
||||
d.assignments++;
|
||||
@@ -560,7 +541,7 @@ merge(Compressor.prototype, {
|
||||
var value = eq ? node.right : node;
|
||||
if (is_modified(compressor, tw, node, value, 0)) return;
|
||||
if (!eq) d.chained = true;
|
||||
d.fixed = eq ? function() {
|
||||
sym.fixed = d.fixed = eq ? function() {
|
||||
return node.right;
|
||||
} : function() {
|
||||
return make_node(AST_Binary, node, {
|
||||
@@ -574,7 +555,7 @@ merge(Compressor.prototype, {
|
||||
mark(tw, d, false);
|
||||
node.right.walk(tw);
|
||||
mark(tw, d, true);
|
||||
mark_escaped(tw, d, sym.scope, node, value, 0, 1);
|
||||
if (eq) mark_escaped(tw, d, sym.scope, node, value, 0, 1);
|
||||
return true;
|
||||
});
|
||||
def(AST_Binary, function(tw) {
|
||||
@@ -730,6 +711,7 @@ merge(Compressor.prototype, {
|
||||
this.definition().fixed = false;
|
||||
});
|
||||
def(AST_SymbolRef, function(tw, descend, compressor) {
|
||||
if (this.fixed) delete this.fixed;
|
||||
var d = this.definition();
|
||||
d.references.push(this);
|
||||
if (d.references.length == 1
|
||||
@@ -742,7 +724,7 @@ merge(Compressor.prototype, {
|
||||
d.fixed = false;
|
||||
} else if (d.fixed) {
|
||||
value = this.fixed_value();
|
||||
if (value instanceof AST_Lambda && recursive_ref(tw, d)) {
|
||||
if (recursive_ref(tw, d)) {
|
||||
d.recursive_refs++;
|
||||
} else if (value && ref_once(tw, compressor, d)) {
|
||||
d.single_use = value instanceof AST_Lambda && !value.pinned()
|
||||
@@ -797,13 +779,14 @@ merge(Compressor.prototype, {
|
||||
mark_assignment_to_arguments(exp);
|
||||
return;
|
||||
}
|
||||
if (exp.fixed) delete exp.fixed;
|
||||
var d = exp.definition();
|
||||
var safe = safe_to_assign(tw, d, exp.scope, true);
|
||||
d.assignments++;
|
||||
var fixed = d.fixed;
|
||||
if (!fixed) return;
|
||||
d.chained = true;
|
||||
d.fixed = function() {
|
||||
exp.fixed = d.fixed = function() {
|
||||
return make_node(AST_Binary, node, {
|
||||
operator: node.operator.slice(0, -1),
|
||||
left: make_node(AST_UnaryPrefix, node, {
|
||||
@@ -874,10 +857,11 @@ merge(Compressor.prototype, {
|
||||
this.walk(tw);
|
||||
});
|
||||
|
||||
AST_Symbol.DEFMETHOD("fixed_value", function() {
|
||||
AST_Symbol.DEFMETHOD("fixed_value", function(final) {
|
||||
var fixed = this.definition().fixed;
|
||||
if (!fixed || fixed instanceof AST_Node) return fixed;
|
||||
return fixed();
|
||||
if (!fixed) return fixed;
|
||||
if (!final && this.fixed) fixed = this.fixed;
|
||||
return fixed instanceof AST_Node ? fixed : fixed();
|
||||
});
|
||||
|
||||
AST_SymbolRef.DEFMETHOD("is_immutable", function() {
|
||||
@@ -1134,7 +1118,7 @@ merge(Compressor.prototype, {
|
||||
if (value_def && candidate instanceof AST_VarDef) return node;
|
||||
}
|
||||
CHANGED = abort = true;
|
||||
compressor.info("Collapsing {name} [{file}:{line},{col}]", {
|
||||
AST_Node.info("Collapsing {name} [{file}:{line},{col}]", {
|
||||
name: node.print_to_string(),
|
||||
file: node.start.file,
|
||||
line: node.start.line,
|
||||
@@ -1287,7 +1271,9 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_Try) return true;
|
||||
if (node instanceof AST_With) return true;
|
||||
if (replace_all) return false;
|
||||
return node instanceof AST_SymbolRef && !node.is_declared(compressor);
|
||||
return node instanceof AST_SymbolRef
|
||||
&& !node.is_declared(compressor)
|
||||
&& !(parent instanceof AST_Assign && parent.left === node);
|
||||
}
|
||||
|
||||
function in_conditional(node, parent) {
|
||||
@@ -1957,7 +1943,7 @@ merge(Compressor.prototype, {
|
||||
statements.length = n;
|
||||
CHANGED = n != len;
|
||||
if (has_quit) has_quit.forEach(function(stat) {
|
||||
extract_declarations_from_unreachable_code(compressor, stat, statements);
|
||||
extract_declarations_from_unreachable_code(stat, statements);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -2210,13 +2196,13 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
function extract_declarations_from_unreachable_code(compressor, stat, target) {
|
||||
function extract_declarations_from_unreachable_code(stat, target) {
|
||||
if (!(stat instanceof AST_Defun)) {
|
||||
compressor.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
|
||||
AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
|
||||
}
|
||||
stat.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_Definitions) {
|
||||
compressor.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
|
||||
AST_Node.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
|
||||
node.remove_initializers();
|
||||
target.push(node);
|
||||
return true;
|
||||
@@ -2290,17 +2276,14 @@ 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);
|
||||
})
|
||||
return this.operator == "=" && this.right._dot_throw(compressor);
|
||||
});
|
||||
def(AST_Binary, function(compressor) {
|
||||
return (this.operator == "&&" || this.operator == "||")
|
||||
&& (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
|
||||
})
|
||||
return lazy_op[this.operator] && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
|
||||
});
|
||||
def(AST_Conditional, function(compressor) {
|
||||
return this.consequent._dot_throw(compressor)
|
||||
|| this.alternative._dot_throw(compressor);
|
||||
})
|
||||
return this.consequent._dot_throw(compressor) || this.alternative._dot_throw(compressor);
|
||||
});
|
||||
def(AST_Constant, return_false);
|
||||
def(AST_Dot, function(compressor) {
|
||||
if (!is_strict(compressor)) return false;
|
||||
@@ -2340,6 +2323,51 @@ merge(Compressor.prototype, {
|
||||
node.DEFMETHOD("_dot_throw", func);
|
||||
});
|
||||
|
||||
(function(def) {
|
||||
def(AST_Node, return_false);
|
||||
def(AST_Array, return_true);
|
||||
def(AST_Assign, function(compressor) {
|
||||
return this.operator != "=" || this.right.is_defined(compressor);
|
||||
});
|
||||
def(AST_Binary, function(compressor) {
|
||||
switch (this.operator) {
|
||||
case "&&":
|
||||
return this.left.is_defined(compressor) && this.right.is_defined(compressor);
|
||||
case "||":
|
||||
return this.left.is_defined(compressor) || this.right.is_defined(compressor);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
});
|
||||
def(AST_Conditional, function(compressor) {
|
||||
return this.consequent.is_defined(compressor) && this.alternative.is_defined(compressor);
|
||||
});
|
||||
def(AST_Constant, return_true);
|
||||
def(AST_Lambda, return_true);
|
||||
def(AST_Object, return_true);
|
||||
def(AST_Sequence, function(compressor) {
|
||||
return this.tail_node().is_defined(compressor);
|
||||
});
|
||||
def(AST_SymbolRef, function(compressor) {
|
||||
if (this.is_undefined) return false;
|
||||
if (is_undeclared_ref(this) && this.is_declared(compressor)) return true;
|
||||
if (this.is_immutable()) return true;
|
||||
var fixed = this.fixed_value();
|
||||
if (!fixed) return false;
|
||||
this.is_defined = return_true;
|
||||
var result = fixed.is_defined(compressor);
|
||||
delete this.is_defined;
|
||||
return result;
|
||||
});
|
||||
def(AST_UnaryPrefix, function() {
|
||||
return this.operator != "void";
|
||||
});
|
||||
def(AST_UnaryPostfix, return_true);
|
||||
def(AST_Undefined, return_false);
|
||||
})(function(node, func) {
|
||||
node.DEFMETHOD("is_defined", func);
|
||||
});
|
||||
|
||||
/* -----[ boolean/negation helpers ]----- */
|
||||
|
||||
// methods to determine whether an expression has a boolean result type
|
||||
@@ -2484,6 +2512,20 @@ merge(Compressor.prototype, {
|
||||
return this.operator == "+" &&
|
||||
(this.left.is_string(compressor) || this.right.is_string(compressor));
|
||||
});
|
||||
var fn = makePredicate([
|
||||
"charAt",
|
||||
"substr",
|
||||
"substring",
|
||||
"toLowerCase",
|
||||
"toString",
|
||||
"toUpperCase",
|
||||
"trim",
|
||||
]);
|
||||
def(AST_Call, function(compressor) {
|
||||
if (!compressor.option("unsafe")) return false;
|
||||
var exp = this.expression;
|
||||
return exp instanceof AST_Dot && fn[exp.property];
|
||||
});
|
||||
def(AST_Conditional, function(compressor) {
|
||||
return this.consequent.is_string(compressor) && this.alternative.is_string(compressor);
|
||||
});
|
||||
@@ -2537,8 +2579,8 @@ merge(Compressor.prototype, {
|
||||
return make_node_from_constant(value, orig);
|
||||
}
|
||||
|
||||
function warn(compressor, node) {
|
||||
compressor.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start);
|
||||
function warn(node) {
|
||||
AST_Node.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start);
|
||||
}
|
||||
|
||||
AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
|
||||
@@ -2554,7 +2596,7 @@ merge(Compressor.prototype, {
|
||||
child = parent;
|
||||
}
|
||||
if (is_lhs(child, parent)) {
|
||||
warn(compressor, node);
|
||||
warn(node);
|
||||
return;
|
||||
}
|
||||
return def;
|
||||
@@ -2566,7 +2608,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_SymbolDeclaration, function(compressor) {
|
||||
if (!this.global()) return;
|
||||
if (HOP(compressor.option("global_defs"), this.name)) warn(compressor, this);
|
||||
if (HOP(compressor.option("global_defs"), this.name)) warn(this);
|
||||
});
|
||||
def(AST_SymbolRef, function(compressor, suffix) {
|
||||
if (!this.global()) return;
|
||||
@@ -2597,9 +2639,9 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function convert_to_predicate(obj) {
|
||||
for (var key in obj) {
|
||||
Object.keys(obj).forEach(function(key) {
|
||||
obj[key] = makePredicate(obj[key]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var object_fns = [
|
||||
@@ -2929,7 +2971,8 @@ merge(Compressor.prototype, {
|
||||
val = global_objs[exp.name];
|
||||
} else {
|
||||
val = exp._eval(compressor, cached, depth + 1);
|
||||
if (!val || val === exp || !HOP(val, key)) return this;
|
||||
if (!val || val === exp) return this;
|
||||
if (typeof val == "object" && !HOP(val, key)) return this;
|
||||
if (typeof val == "function") switch (key) {
|
||||
case "name":
|
||||
return val.node.name ? val.node.name.name : "";
|
||||
@@ -2970,10 +3013,11 @@ merge(Compressor.prototype, {
|
||||
if (arg === value) return this;
|
||||
args.push(value);
|
||||
}
|
||||
if (key == "replace" && typeof args[1] == "function") return this;
|
||||
try {
|
||||
return val[key].apply(val, args);
|
||||
} catch (ex) {
|
||||
compressor.warn("Error evaluating {code} [{file}:{line},{col}]", {
|
||||
AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
|
||||
code: this.print_to_string(),
|
||||
file: this.start.file,
|
||||
line: this.start.line,
|
||||
@@ -3078,24 +3122,31 @@ merge(Compressor.prototype, {
|
||||
return this.pure || !compressor.pure_funcs(this);
|
||||
});
|
||||
AST_Node.DEFMETHOD("is_call_pure", return_false);
|
||||
AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
|
||||
if (!compressor.option("unsafe")) return;
|
||||
var expr = this.expression;
|
||||
AST_Call.DEFMETHOD("is_call_pure", function(compressor) {
|
||||
if (!compressor.option("unsafe")) return false;
|
||||
var dot = this.expression;
|
||||
if (!(dot instanceof AST_Dot)) return false;
|
||||
var exp = dot.expression;
|
||||
var map;
|
||||
if (expr instanceof AST_Array) {
|
||||
var prop = dot.property;
|
||||
if (exp instanceof AST_Array) {
|
||||
map = native_fns.Array;
|
||||
} else if (expr.is_boolean(compressor)) {
|
||||
} else if (exp.is_boolean(compressor)) {
|
||||
map = native_fns.Boolean;
|
||||
} else if (expr.is_number(compressor)) {
|
||||
} else if (exp.is_number(compressor)) {
|
||||
map = native_fns.Number;
|
||||
} else if (expr instanceof AST_RegExp) {
|
||||
} else if (exp instanceof AST_RegExp) {
|
||||
map = native_fns.RegExp;
|
||||
} else if (expr.is_string(compressor)) {
|
||||
} else if (exp.is_string(compressor)) {
|
||||
map = native_fns.String;
|
||||
} else if (!this.may_throw_on_access(compressor)) {
|
||||
if (prop == "replace") {
|
||||
var arg = this.args[1];
|
||||
if (arg && !arg.is_string(compressor)) return false;
|
||||
}
|
||||
} else if (!dot.may_throw_on_access(compressor)) {
|
||||
map = native_fns.Object;
|
||||
}
|
||||
return map && map[this.property];
|
||||
return map && map[prop];
|
||||
});
|
||||
|
||||
// determine if expression has side effects
|
||||
@@ -3120,8 +3171,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_Call, function(compressor) {
|
||||
if (!this.is_expr_pure(compressor)
|
||||
&& (!this.expression.is_call_pure(compressor)
|
||||
|| this.expression.has_side_effects(compressor))) {
|
||||
&& (!this.is_call_pure(compressor) || this.expression.has_side_effects(compressor))) {
|
||||
return true;
|
||||
}
|
||||
return any(this.args, compressor);
|
||||
@@ -3512,7 +3562,7 @@ merge(Compressor.prototype, {
|
||||
if (def.value.has_side_effects(compressor)) {
|
||||
def.value.walk(tw);
|
||||
}
|
||||
if (!node_def.chained && def.name.fixed_value() === def.value) {
|
||||
if (!node_def.chained && def.name.fixed_value(true) === def.value) {
|
||||
fixed_ids[node_def.id] = def;
|
||||
}
|
||||
}
|
||||
@@ -3578,7 +3628,7 @@ merge(Compressor.prototype, {
|
||||
sym.__unused = true;
|
||||
if (trim) {
|
||||
a.pop();
|
||||
compressor[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
|
||||
AST_Node[sym.unreferenced() ? "warn" : "info"]("Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
|
||||
}
|
||||
} else {
|
||||
trim = false;
|
||||
@@ -3588,7 +3638,7 @@ merge(Compressor.prototype, {
|
||||
if (drop_funcs && node instanceof AST_Defun && node !== self) {
|
||||
var def = node.name.definition();
|
||||
if (!(def.id in in_use_ids)) {
|
||||
compressor[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
||||
AST_Node[node.name.unreferenced() ? "warn" : "info"]("Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
||||
def.eliminated++;
|
||||
return make_node(AST_EmptyStatement, node);
|
||||
}
|
||||
@@ -3609,7 +3659,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
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));
|
||||
AST_Node.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);
|
||||
@@ -3634,7 +3684,7 @@ merge(Compressor.prototype, {
|
||||
&& def.value instanceof AST_Function
|
||||
&& can_rename(def.value, def.name.name)
|
||||
&& (!compressor.has_directive("use strict") || parent instanceof AST_Scope)) {
|
||||
compressor.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
||||
AST_Node.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);
|
||||
var name_def = def.name.scope.resolve().def_function(defun.name);
|
||||
@@ -3671,10 +3721,10 @@ merge(Compressor.prototype, {
|
||||
} else {
|
||||
var value = def.value && def.value.drop_side_effect_free(compressor);
|
||||
if (value) {
|
||||
compressor.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
side_effects.push(value);
|
||||
} else {
|
||||
compressor[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
AST_Node[def.name.unreferenced() ? "warn" : "info"]("Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
}
|
||||
sym.eliminated++;
|
||||
}
|
||||
@@ -3783,7 +3833,7 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_Assign) {
|
||||
node.right.walk(tw);
|
||||
if (node.left === sym) {
|
||||
if (!node_def.chained && sym.fixed_value() === node.right) {
|
||||
if (!node_def.chained && sym.fixed_value(true) === node.right) {
|
||||
fixed_ids[node_def.id] = node;
|
||||
}
|
||||
if (!node.write_only) {
|
||||
@@ -4100,10 +4150,16 @@ merge(Compressor.prototype, {
|
||||
var right = this.right.drop_side_effect_free(compressor, first_in_statement);
|
||||
if (!right) return this.left.drop_side_effect_free(compressor, first_in_statement);
|
||||
if (lazy_op[this.operator]) {
|
||||
if (right === this.right) return this;
|
||||
var node = this.clone();
|
||||
node.right = right.drop_side_effect_free(compressor);
|
||||
return node;
|
||||
var node = this;
|
||||
if (right !== node.right) {
|
||||
node = this.clone();
|
||||
node.right = right.drop_side_effect_free(compressor);
|
||||
}
|
||||
return (first_in_statement ? best_of_statement : best_of_expression)(node, make_node(AST_Binary, this, {
|
||||
operator: node.operator == "&&" ? "||" : "&&",
|
||||
left: node.left.negate(compressor, first_in_statement),
|
||||
right: node.right
|
||||
}));
|
||||
} else {
|
||||
var left = this.left.drop_side_effect_free(compressor, first_in_statement);
|
||||
if (!left) return right;
|
||||
@@ -4112,16 +4168,15 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
def(AST_Call, function(compressor, first_in_statement) {
|
||||
if (!this.is_expr_pure(compressor)) {
|
||||
if (this.expression.is_call_pure(compressor)) {
|
||||
var exp = this.expression;
|
||||
if (this.is_call_pure(compressor)) {
|
||||
var exprs = this.args.slice();
|
||||
exprs.unshift(this.expression.expression);
|
||||
exprs.unshift(exp.expression);
|
||||
exprs = trim(exprs, compressor, first_in_statement);
|
||||
return exprs && make_sequence(this, exprs);
|
||||
}
|
||||
if (this.expression instanceof AST_Function
|
||||
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
|
||||
if (exp instanceof AST_Function && (!exp.name || !exp.name.definition().references.length)) {
|
||||
var node = this.clone();
|
||||
var exp = node.expression;
|
||||
exp.process_expression(false, compressor);
|
||||
exp.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_Return && node.value) {
|
||||
@@ -4134,9 +4189,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
return this;
|
||||
}
|
||||
if (this.pure) {
|
||||
compressor.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
|
||||
}
|
||||
if (this.pure) AST_Node.warn("Dropping __PURE__ call [{file}:{line},{col}]", this.start);
|
||||
var args = trim(this.args, compressor, first_in_statement);
|
||||
return args && make_sequence(this, args);
|
||||
});
|
||||
@@ -4219,7 +4272,7 @@ merge(Compressor.prototype, {
|
||||
var body = self.body;
|
||||
var node = body.drop_side_effect_free(compressor, true);
|
||||
if (!node) {
|
||||
compressor.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Dropping side-effect-free statement [{file}:{line},{col}]", self.start);
|
||||
return make_node(AST_EmptyStatement, self);
|
||||
}
|
||||
if (node !== body) {
|
||||
@@ -4298,7 +4351,7 @@ merge(Compressor.prototype, {
|
||||
body: self.condition
|
||||
}));
|
||||
}
|
||||
extract_declarations_from_unreachable_code(compressor, self.body, body);
|
||||
extract_declarations_from_unreachable_code(self.body, body);
|
||||
return make_node(AST_BlockStatement, self, {
|
||||
body: body
|
||||
});
|
||||
@@ -4371,7 +4424,7 @@ merge(Compressor.prototype, {
|
||||
if (!cond) {
|
||||
if (compressor.option("dead_code")) {
|
||||
var body = [];
|
||||
extract_declarations_from_unreachable_code(compressor, self.body, body);
|
||||
extract_declarations_from_unreachable_code(self.body, body);
|
||||
if (self.init instanceof AST_Statement) {
|
||||
body.push(self.init);
|
||||
} else if (self.init) {
|
||||
@@ -4418,20 +4471,18 @@ merge(Compressor.prototype, {
|
||||
cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||
}
|
||||
if (!cond) {
|
||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
||||
AST_Node.warn("Condition always false [{file}:{line},{col}]", self.condition.start);
|
||||
var body = [];
|
||||
extract_declarations_from_unreachable_code(compressor, self.body, body);
|
||||
extract_declarations_from_unreachable_code(self.body, body);
|
||||
body.push(make_node(AST_SimpleStatement, self.condition, {
|
||||
body: self.condition
|
||||
}));
|
||||
if (self.alternative) body.push(self.alternative);
|
||||
return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
|
||||
} else if (!(cond instanceof AST_Node)) {
|
||||
compressor.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
|
||||
AST_Node.warn("Condition always true [{file}:{line},{col}]", self.condition.start);
|
||||
var body = [];
|
||||
if (self.alternative) {
|
||||
extract_declarations_from_unreachable_code(compressor, self.alternative, body);
|
||||
}
|
||||
if (self.alternative) extract_declarations_from_unreachable_code(self.alternative, body);
|
||||
body.push(make_node(AST_SimpleStatement, self.condition, {
|
||||
body: self.condition
|
||||
}));
|
||||
@@ -4480,14 +4531,14 @@ merge(Compressor.prototype, {
|
||||
operator : "||",
|
||||
left : negated,
|
||||
right : self.body.body
|
||||
})
|
||||
}).transform(compressor)
|
||||
}).optimize(compressor);
|
||||
return make_node(AST_SimpleStatement, self, {
|
||||
body: make_node(AST_Binary, self, {
|
||||
operator : "&&",
|
||||
left : self.condition,
|
||||
right : self.body.body
|
||||
})
|
||||
}).transform(compressor)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_EmptyStatement
|
||||
@@ -4497,7 +4548,7 @@ merge(Compressor.prototype, {
|
||||
operator : "||",
|
||||
left : self.condition,
|
||||
right : self.alternative.body
|
||||
})
|
||||
}).transform(compressor)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.body instanceof AST_Exit
|
||||
@@ -4646,7 +4697,7 @@ merge(Compressor.prototype, {
|
||||
if (prev && !aborts(prev)) {
|
||||
prev.body = prev.body.concat(branch.body);
|
||||
} else {
|
||||
extract_declarations_from_unreachable_code(compressor, branch, decl);
|
||||
extract_declarations_from_unreachable_code(branch, decl);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -4657,7 +4708,7 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("dead_code") && all(self.body, is_empty)) {
|
||||
var body = [];
|
||||
if (self.bcatch) {
|
||||
extract_declarations_from_unreachable_code(compressor, self.bcatch, body);
|
||||
extract_declarations_from_unreachable_code(self.bcatch, body);
|
||||
body.forEach(function(stat) {
|
||||
if (!(stat instanceof AST_Definitions)) return;
|
||||
stat.definitions.forEach(function(var_def) {
|
||||
@@ -4769,7 +4820,7 @@ merge(Compressor.prototype, {
|
||||
elements: elements
|
||||
});
|
||||
} catch (ex) {
|
||||
compressor.warn("Invalid array length: {length} [{file}:{line},{col}]", {
|
||||
AST_Node.warn("Invalid array length: {length} [{file}:{line},{col}]", {
|
||||
length: length,
|
||||
file: self.start.file,
|
||||
line: self.start.line,
|
||||
@@ -4829,7 +4880,7 @@ merge(Compressor.prototype, {
|
||||
value: RegExp.apply(RegExp, params),
|
||||
}));
|
||||
} catch (ex) {
|
||||
compressor.warn("Error converting {expr} [{file}:{line},{col}]", {
|
||||
AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
|
||||
expr: self.print_to_string(),
|
||||
file: self.start.file,
|
||||
line: self.start.line,
|
||||
@@ -4913,15 +4964,21 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
break;
|
||||
case "charAt":
|
||||
if (exp.expression.is_string(compressor)) {
|
||||
var arg = self.args[0];
|
||||
var index = arg ? arg.evaluate(compressor) : 0;
|
||||
if (index !== arg) {
|
||||
return make_node(AST_Sub, exp, {
|
||||
expression: exp.expression,
|
||||
property: make_node_from_constant(index | 0, arg || exp)
|
||||
}).optimize(compressor);
|
||||
}
|
||||
if (self.args.length < 2) {
|
||||
var node = make_node(AST_Sub, self, {
|
||||
expression: exp.expression,
|
||||
property: self.args.length ? make_node(AST_Binary, self.args[0], {
|
||||
operator: "|",
|
||||
left: make_node(AST_Number, self, {
|
||||
value: 0
|
||||
}),
|
||||
right: self.args[0]
|
||||
}) : make_node(AST_Number, self, {
|
||||
value: 0
|
||||
})
|
||||
});
|
||||
node.is_string = return_true;
|
||||
return node.optimize(compressor);
|
||||
}
|
||||
break;
|
||||
case "apply":
|
||||
@@ -5006,8 +5063,8 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
} catch (ex) {
|
||||
if (ex instanceof JS_Parse_Error) {
|
||||
compressor.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
|
||||
compressor.warn(ex.toString());
|
||||
AST_Node.warn("Error parsing code passed to new Function [{file}:{line},{col}]", self.args[self.args.length - 1].start);
|
||||
AST_Node.warn(ex.toString());
|
||||
} else {
|
||||
throw ex;
|
||||
}
|
||||
@@ -5102,7 +5159,7 @@ merge(Compressor.prototype, {
|
||||
})) {
|
||||
return false;
|
||||
}
|
||||
} else if (line instanceof AST_EmptyStatement) {
|
||||
} else if (line instanceof AST_Defun || line instanceof AST_EmptyStatement) {
|
||||
continue;
|
||||
} else if (stat) {
|
||||
return false;
|
||||
@@ -5113,16 +5170,15 @@ merge(Compressor.prototype, {
|
||||
return return_value(stat);
|
||||
}
|
||||
|
||||
function var_exists(catches, name) {
|
||||
return catches[name] || identifier_atom[name] || scope.var_names()[name];
|
||||
}
|
||||
|
||||
function can_inject_args(catches, 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
|
||||
|| catches[arg.name]
|
||||
|| identifier_atom[arg.name]
|
||||
|| scope.var_names()[arg.name]) {
|
||||
return false;
|
||||
}
|
||||
if (!safe_to_inject || var_exists(catches, arg.name)) return false;
|
||||
if (in_loop) in_loop.push(arg.definition());
|
||||
}
|
||||
return true;
|
||||
@@ -5131,15 +5187,15 @@ merge(Compressor.prototype, {
|
||||
function can_inject_vars(catches, 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;
|
||||
continue;
|
||||
}
|
||||
if (!(stat instanceof AST_Var)) continue;
|
||||
if (!safe_to_inject) return false;
|
||||
for (var j = stat.definitions.length; --j >= 0;) {
|
||||
var name = stat.definitions[j].name;
|
||||
if (catches[name.name]
|
||||
|| identifier_atom[name.name]
|
||||
|| scope.var_names()[name.name]) {
|
||||
return false;
|
||||
}
|
||||
if (var_exists(catches, name.name)) return false;
|
||||
if (in_loop) in_loop.push(name.definition());
|
||||
}
|
||||
}
|
||||
@@ -5243,12 +5299,21 @@ merge(Compressor.prototype, {
|
||||
flatten_args(decls, expressions);
|
||||
flatten_vars(decls, expressions);
|
||||
expressions.push(value);
|
||||
if (decls.length) {
|
||||
i = scope.body.indexOf(compressor.parent(level - 1)) + 1;
|
||||
scope.body.splice(i, 0, make_node(AST_Var, fn, {
|
||||
definitions: decls
|
||||
}));
|
||||
}
|
||||
var args = fn.body.filter(function(stat) {
|
||||
if (stat instanceof AST_Defun) {
|
||||
var def = stat.name.definition();
|
||||
scope.functions.set(def.name, def);
|
||||
scope.variables.set(def.name, def);
|
||||
scope.enclosed.push(def);
|
||||
scope.var_names()[def.name] = true;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
args.unshift(scope.body.indexOf(compressor.parent(level - 1)) + 1, 0);
|
||||
if (decls.length) args.push(make_node(AST_Var, fn, {
|
||||
definitions: decls
|
||||
}));
|
||||
[].splice.apply(scope.body, args);
|
||||
return expressions;
|
||||
}
|
||||
});
|
||||
@@ -5369,7 +5434,7 @@ merge(Compressor.prototype, {
|
||||
case "typeof":
|
||||
// typeof always returns a non-empty string, thus it's
|
||||
// always true in booleans
|
||||
compressor.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
||||
return (e instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
|
||||
e,
|
||||
make_node(AST_True, self)
|
||||
@@ -5437,6 +5502,7 @@ merge(Compressor.prototype, {
|
||||
return this;
|
||||
});
|
||||
|
||||
var indexFns = makePredicate("indexOf lastIndexOf");
|
||||
var commutativeOperators = makePredicate("== === != !== * & | ^");
|
||||
function is_object(node) {
|
||||
return node instanceof AST_Array
|
||||
@@ -5476,6 +5542,13 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("comparisons")) switch (self.operator) {
|
||||
case "===":
|
||||
case "!==":
|
||||
if (is_undefined(self.left, compressor) && self.right.is_defined(compressor)) {
|
||||
AST_Node.warn("Expression always defined [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [
|
||||
self.right,
|
||||
make_node(self.operator == "===" ? AST_False : AST_True, self)
|
||||
]).optimize(compressor);
|
||||
}
|
||||
var is_strict_comparison = true;
|
||||
if ((self.left.is_string(compressor) && self.right.is_string(compressor)) ||
|
||||
(self.left.is_number(compressor) && self.right.is_number(compressor)) ||
|
||||
@@ -5514,6 +5587,8 @@ merge(Compressor.prototype, {
|
||||
break;
|
||||
case "&&":
|
||||
case "||":
|
||||
// void 0 !== x && null !== x => null != x
|
||||
// void 0 === x || null === x => null == x
|
||||
var lhs = self.left;
|
||||
if (lhs.operator == self.operator) {
|
||||
lhs = lhs.right;
|
||||
@@ -5542,23 +5617,38 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (compressor.option("booleans") && self.operator == "+" && compressor.in_boolean_context()) {
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) switch (self.operator) {
|
||||
case "+":
|
||||
var ll = self.left.evaluate(compressor);
|
||||
var rr = self.right.evaluate(compressor);
|
||||
if (ll && typeof ll == "string") {
|
||||
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [
|
||||
self.right,
|
||||
make_node(AST_True, self)
|
||||
]).optimize(compressor);
|
||||
}
|
||||
if (rr && typeof rr == "string") {
|
||||
compressor.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("+ in boolean context always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [
|
||||
self.left,
|
||||
make_node(AST_True, self)
|
||||
]).optimize(compressor);
|
||||
}
|
||||
break;
|
||||
case "==":
|
||||
if (self.left instanceof AST_String && self.left.getValue() == "" && self.right.is_string(compressor)) {
|
||||
return make_node(AST_UnaryPrefix, self, {
|
||||
operator: "!",
|
||||
expression: self.right
|
||||
}).optimize(compressor);
|
||||
}
|
||||
break;
|
||||
case "!=":
|
||||
if (self.left instanceof AST_String && self.left.getValue() == "" && self.right.is_string(compressor)) {
|
||||
return self.right.optimize(compressor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
if (compressor.option("comparisons") && self.is_boolean(compressor)) {
|
||||
if (!(compressor.parent() instanceof AST_Binary)
|
||||
@@ -5578,12 +5668,12 @@ merge(Compressor.prototype, {
|
||||
if (self.right instanceof AST_String
|
||||
&& self.right.getValue() == ""
|
||||
&& self.left.is_string(compressor)) {
|
||||
return self.left;
|
||||
return self.left.optimize(compressor);
|
||||
}
|
||||
if (self.left instanceof AST_String
|
||||
&& self.left.getValue() == ""
|
||||
&& self.right.is_string(compressor)) {
|
||||
return self.right;
|
||||
return self.right.optimize(compressor);
|
||||
}
|
||||
if (self.left instanceof AST_Binary
|
||||
&& self.left.operator == "+"
|
||||
@@ -5591,7 +5681,7 @@ merge(Compressor.prototype, {
|
||||
&& self.left.left.getValue() == ""
|
||||
&& self.right.is_string(compressor)) {
|
||||
self.left = self.left.right;
|
||||
return self.transform(compressor);
|
||||
return self.optimize(compressor);
|
||||
}
|
||||
}
|
||||
if (compressor.option("evaluate")) {
|
||||
@@ -5599,16 +5689,16 @@ merge(Compressor.prototype, {
|
||||
case "&&":
|
||||
var ll = fuzzy_eval(self.left);
|
||||
if (!ll) {
|
||||
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
} else if (!(ll instanceof AST_Node)) {
|
||||
compressor.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Condition left of && always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
||||
}
|
||||
var rr = self.right.evaluate(compressor);
|
||||
if (!rr) {
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
compressor.warn("Boolean && always false [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Boolean && always false [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [
|
||||
self.left,
|
||||
make_node(AST_False, self)
|
||||
@@ -5618,7 +5708,7 @@ merge(Compressor.prototype, {
|
||||
var parent = compressor.parent();
|
||||
if (parent.operator == "&&" && parent.left === compressor.self()
|
||||
|| compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
compressor.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Dropping side-effect-free && [{file}:{line},{col}]", self.start);
|
||||
return self.left.optimize(compressor);
|
||||
}
|
||||
}
|
||||
@@ -5635,10 +5725,10 @@ merge(Compressor.prototype, {
|
||||
case "||":
|
||||
var ll = fuzzy_eval(self.left);
|
||||
if (!ll) {
|
||||
compressor.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Condition left of || always false [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
||||
} else if (!(ll instanceof AST_Node)) {
|
||||
compressor.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Condition left of || always true [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
}
|
||||
var rr = self.right.evaluate(compressor);
|
||||
@@ -5646,12 +5736,12 @@ merge(Compressor.prototype, {
|
||||
var parent = compressor.parent();
|
||||
if (parent.operator == "||" && parent.left === compressor.self()
|
||||
|| compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
compressor.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Dropping side-effect-free || [{file}:{line},{col}]", self.start);
|
||||
return self.left.optimize(compressor);
|
||||
}
|
||||
} else if (!(rr instanceof AST_Node)) {
|
||||
if (compressor.option("booleans") && compressor.in_boolean_context()) {
|
||||
compressor.warn("Boolean || always true [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Boolean || always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [
|
||||
self.left,
|
||||
make_node(AST_True, self)
|
||||
@@ -5846,6 +5936,35 @@ 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("booleans")
|
||||
&& (self.operator == "==" || self.operator == "!=")
|
||||
&& self.left instanceof AST_Number
|
||||
&& self.left.getValue() == 0
|
||||
&& compressor.in_boolean_context()) {
|
||||
return (self.operator == "==" ? make_node(AST_UnaryPrefix, self, {
|
||||
operator: "!",
|
||||
expression: self.right
|
||||
}) : self.right).optimize(compressor);
|
||||
}
|
||||
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
|
||||
})
|
||||
});
|
||||
if (self.operator == "!=" || self.operator == "<=") node = make_node(AST_UnaryPrefix, self, {
|
||||
operator: "!",
|
||||
expression: node
|
||||
});
|
||||
return node.optimize(compressor);
|
||||
}
|
||||
}
|
||||
// x && (y && z) ==> x && y && z
|
||||
// x || (y || z) ==> x || y || z
|
||||
// x + ("y" + z) ==> x + "y" + z
|
||||
@@ -5879,6 +5998,23 @@ merge(Compressor.prototype, {
|
||||
if (node.is_truthy()) return true;
|
||||
return node.evaluate(compressor);
|
||||
}
|
||||
|
||||
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;
|
||||
case "==":
|
||||
case "!=":
|
||||
// -1 == array.indexOf(string) => !~array.indexOf(string)
|
||||
// -1 != array.indexOf(string) => !!~array.indexOf(string)
|
||||
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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function recursive_ref(compressor, def) {
|
||||
@@ -5907,7 +6043,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
var parent = compressor.parent();
|
||||
if (compressor.option("reduce_vars") && is_lhs(self, parent) !== self) {
|
||||
if (compressor.option("reduce_vars") && is_lhs(compressor.self(), parent) !== compressor.self()) {
|
||||
var def = self.definition();
|
||||
var fixed = self.fixed_value();
|
||||
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
|
||||
@@ -6125,7 +6261,7 @@ 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;
|
||||
if (self.operator == "=") return self.right.optimize(compressor);
|
||||
def.fixed = false;
|
||||
return make_node(AST_Binary, self, {
|
||||
operator: self.operator.slice(0, -1),
|
||||
@@ -6195,10 +6331,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
var cond = self.condition.is_truthy() || self.condition.tail_node().evaluate(compressor);
|
||||
if (!cond) {
|
||||
compressor.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Condition always false [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.condition, self.alternative ]).optimize(compressor);
|
||||
} else if (!(cond instanceof AST_Node)) {
|
||||
compressor.warn("Condition always true [{file}:{line},{col}]", self.start);
|
||||
AST_Node.warn("Condition always true [{file}:{line},{col}]", self.start);
|
||||
return make_sequence(self, [ self.condition, self.consequent ]).optimize(compressor);
|
||||
}
|
||||
var negated = cond.negate(compressor, first_in_statement(compressor));
|
||||
@@ -6437,7 +6573,7 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
var p = compressor.parent();
|
||||
if (p instanceof AST_Binary && (p.operator == "==" || p.operator == "!=")) {
|
||||
compressor.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
|
||||
AST_Node.warn("Non-strict equality against boolean: {operator} {value} [{file}:{line},{col}]", {
|
||||
operator : p.operator,
|
||||
value : self.value,
|
||||
file : p.start.file,
|
||||
@@ -6529,7 +6665,7 @@ merge(Compressor.prototype, {
|
||||
return sym;
|
||||
}
|
||||
}
|
||||
if (is_lhs(self, compressor.parent())) return self;
|
||||
if (is_lhs(compressor.self(), compressor.parent())) return self;
|
||||
if (key !== prop) {
|
||||
var sub = self.flatten_object(property, compressor);
|
||||
if (sub) {
|
||||
@@ -6620,14 +6756,14 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Dot, function(self, compressor) {
|
||||
if (self.property == "arguments" || self.property == "caller") {
|
||||
compressor.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
|
||||
AST_Node.warn("Function.prototype.{prop} not supported [{file}:{line},{col}]", {
|
||||
prop: self.property,
|
||||
file: self.start.file,
|
||||
line: self.start.line,
|
||||
col: self.start.col
|
||||
});
|
||||
}
|
||||
if (is_lhs(self, compressor.parent())) return self;
|
||||
if (is_lhs(compressor.self(), compressor.parent())) return self;
|
||||
if (compressor.option("unsafe_proto")
|
||||
&& self.expression instanceof AST_Dot
|
||||
&& self.expression.property == "prototype") {
|
||||
|
||||
@@ -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;
|
||||
@@ -51,7 +51,6 @@ function to_json(cache) {
|
||||
}
|
||||
|
||||
function minify(files, options) {
|
||||
var warn_function = AST_Node.warn_function;
|
||||
try {
|
||||
options = defaults(options, {
|
||||
compress: {},
|
||||
@@ -78,7 +77,6 @@ function minify(files, options) {
|
||||
set_shorthand("ie8", options, [ "compress", "mangle", "output" ]);
|
||||
set_shorthand("keep_fnames", options, [ "compress", "mangle" ]);
|
||||
set_shorthand("toplevel", options, [ "compress", "mangle" ]);
|
||||
set_shorthand("warnings", options, [ "compress" ]);
|
||||
var quoted_props;
|
||||
if (options.mangle) {
|
||||
options.mangle = defaults(options.mangle, {
|
||||
@@ -116,11 +114,9 @@ function minify(files, options) {
|
||||
}, true);
|
||||
}
|
||||
var warnings = [];
|
||||
if (options.warnings && !AST_Node.warn_function) {
|
||||
AST_Node.warn_function = function(warning) {
|
||||
warnings.push(warning);
|
||||
};
|
||||
}
|
||||
if (options.warnings) AST_Node.log_function(function(warning) {
|
||||
warnings.push(warning);
|
||||
}, options.warnings == "verbose");
|
||||
if (timings) timings.parse = Date.now();
|
||||
var source_maps, toplevel;
|
||||
if (files instanceof AST_Toplevel) {
|
||||
@@ -155,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);
|
||||
@@ -208,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") {
|
||||
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;
|
||||
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 {
|
||||
result.code += "\n//# sourceMappingURL=" + url;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -240,7 +241,5 @@ function minify(files, options) {
|
||||
return result;
|
||||
} catch (ex) {
|
||||
return { error: ex };
|
||||
} finally {
|
||||
AST_Node.warn_function = warn_function;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -217,23 +217,12 @@ function OutputStream(options) {
|
||||
|
||||
var flush_mappings = mappings ? function() {
|
||||
mappings.forEach(function(mapping) {
|
||||
try {
|
||||
options.source_map.add(
|
||||
mapping.token.file,
|
||||
mapping.line, mapping.col,
|
||||
mapping.token.line, mapping.token.col,
|
||||
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
|
||||
);
|
||||
} catch(ex) {
|
||||
AST_Node.warn("Couldn't figure out mapping for {file}:{line},{col} → {cline},{ccol} [{name}]", {
|
||||
file: mapping.token.file,
|
||||
line: mapping.token.line,
|
||||
col: mapping.token.col,
|
||||
cline: mapping.line,
|
||||
ccol: mapping.col,
|
||||
name: mapping.name || ""
|
||||
})
|
||||
}
|
||||
options.source_map.add(
|
||||
mapping.token.file,
|
||||
mapping.line, mapping.col,
|
||||
mapping.token.line, mapping.token.col,
|
||||
!mapping.name && mapping.token.type == "name" ? mapping.token.value : mapping.name
|
||||
);
|
||||
});
|
||||
mappings = [];
|
||||
} : noop;
|
||||
@@ -462,16 +451,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
|
||||
@@ -482,11 +466,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;
|
||||
}
|
||||
@@ -529,13 +510,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) {
|
||||
|
||||
@@ -504,7 +504,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
var regexp = new RegExp(source, mods);
|
||||
regexp.raw_source = source;
|
||||
return token("regexp", regexp);
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
parse_error(e.message);
|
||||
}
|
||||
});
|
||||
@@ -556,7 +556,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
return function(x) {
|
||||
try {
|
||||
return cont(x);
|
||||
} catch(ex) {
|
||||
} catch (ex) {
|
||||
if (ex === EX_EOF) parse_error(eof_error);
|
||||
else throw ex;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ function configure_error_stack(fn) {
|
||||
err.name = this.name;
|
||||
try {
|
||||
throw err;
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
return e.stack;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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.6",
|
||||
"version": "3.5.11",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -289,3 +289,25 @@ increment_decrement_2: {
|
||||
}
|
||||
expect_stdout: "42"
|
||||
}
|
||||
|
||||
issue_3375: {
|
||||
options = {
|
||||
assignments: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function(b) {
|
||||
var a = b += 1;
|
||||
--b;
|
||||
return a;
|
||||
}("object"));
|
||||
}
|
||||
expect: {
|
||||
console.log(typeof function(b) {
|
||||
var a = b += 1;
|
||||
--b;
|
||||
return a;
|
||||
}("object"));
|
||||
}
|
||||
expect_stdout: "string"
|
||||
}
|
||||
|
||||
@@ -4194,7 +4194,7 @@ may_throw_2: {
|
||||
var a = x();
|
||||
++b;
|
||||
return b(a);
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
console.log(b);
|
||||
}
|
||||
f(0);
|
||||
@@ -4204,7 +4204,7 @@ may_throw_2: {
|
||||
try {
|
||||
var a = x();
|
||||
return (++b)(a);
|
||||
} catch(e) {}
|
||||
} catch (e) {}
|
||||
console.log(b);
|
||||
}
|
||||
f(0);
|
||||
@@ -6157,3 +6157,24 @@ sub_property: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
assign_undeclared: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var A = (console.log(42), function() {});
|
||||
B = new A();
|
||||
console.log(typeof B);
|
||||
}
|
||||
expect: {
|
||||
B = new (console.log(42), function() {})();
|
||||
console.log(typeof B);
|
||||
}
|
||||
expect_stdout: [
|
||||
"42",
|
||||
"object",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -345,3 +345,38 @@ is_boolean_var: {
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
is_defined: {
|
||||
options = {
|
||||
comparisons: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function a() {
|
||||
return void 0 === a;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function a() {
|
||||
return a, false;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "false"
|
||||
expect_warnings: [
|
||||
"WARN: Expression always defined [test/compress/comparisons.js:2,19]",
|
||||
]
|
||||
}
|
||||
|
||||
unsafe_indexOf: {
|
||||
options = {
|
||||
booleans: true,
|
||||
comparisons: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
if (Object.keys({ foo: 42 }).indexOf("foo") >= 0) console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
if (~Object.keys({ foo: 42 }).indexOf("foo")) console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -1416,3 +1416,58 @@ issue_3271: {
|
||||
}
|
||||
expect_stdout: "1 1"
|
||||
}
|
||||
|
||||
iife_condition: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
if (function() {
|
||||
return console;
|
||||
}())
|
||||
console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
!function() {
|
||||
return console;
|
||||
}() || console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
angularjs_chain: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
passes: 2,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
function nonComputedMember(left, right, context, create) {
|
||||
var lhs = left();
|
||||
if (create && create !== 1) {
|
||||
if (lhs && lhs[right] == null) {
|
||||
lhs[right] = {};
|
||||
}
|
||||
}
|
||||
var value = lhs != null ? lhs[right] : undefined;
|
||||
if (context) {
|
||||
return { context: lhs, name: right, value: value };
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function nonComputedMember(left, right, context, create) {
|
||||
var lhs = left();
|
||||
create && 1 !== create && lhs && null == lhs[right] && (lhs[right] = {});
|
||||
var value = null != lhs ? lhs[right] : void 0;
|
||||
return context ? {
|
||||
context: lhs,
|
||||
name: right,
|
||||
value: value
|
||||
} : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -942,3 +942,21 @@ issue_2929: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
unsafe_string_replace: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
"foo".replace("f", function() {
|
||||
console.log("PASS");
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
"foo".replace("f", function() {
|
||||
console.log("PASS");
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -144,7 +144,7 @@ unused_var_in_catch: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
} catch (ex) {
|
||||
var x = 10;
|
||||
}
|
||||
}
|
||||
@@ -153,7 +153,7 @@ unused_var_in_catch: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {}
|
||||
} catch (ex) {}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -166,7 +166,7 @@ used_var_in_catch: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
} catch (ex) {
|
||||
var x = 10;
|
||||
}
|
||||
return x;
|
||||
@@ -176,7 +176,7 @@ used_var_in_catch: {
|
||||
function foo() {
|
||||
try {
|
||||
foo();
|
||||
} catch(ex) {
|
||||
} catch (ex) {
|
||||
var x = 10;
|
||||
}
|
||||
return x;
|
||||
@@ -1156,7 +1156,7 @@ var_catch_toplevel: {
|
||||
try {
|
||||
a++;
|
||||
x();
|
||||
} catch(a) {
|
||||
} catch (a) {
|
||||
if (a) var a;
|
||||
var a = 10;
|
||||
}
|
||||
@@ -1167,7 +1167,7 @@ var_catch_toplevel: {
|
||||
!function() {
|
||||
try {
|
||||
x();
|
||||
} catch(a) {
|
||||
} catch (a) {
|
||||
var a;
|
||||
}
|
||||
}();
|
||||
@@ -2005,3 +2005,24 @@ issue_3233: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3375: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var b = 1;
|
||||
var a = c = [], c = --b + ("function" == typeof f && f());
|
||||
var a = c && c[a];
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
var b = 1;
|
||||
var a = [], c = --b + ("function" == typeof f && f());
|
||||
a = c && c[a];
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: "0 0"
|
||||
}
|
||||
|
||||
@@ -246,7 +246,7 @@ unsafe_constant: {
|
||||
}
|
||||
expect: {
|
||||
console.log(
|
||||
true.a,
|
||||
void 0,
|
||||
false.a,
|
||||
null.a,
|
||||
(void 0).a
|
||||
@@ -278,7 +278,7 @@ unsafe_object: {
|
||||
o + 1,
|
||||
2,
|
||||
o.b + 1,
|
||||
1..b + 1
|
||||
NaN
|
||||
);
|
||||
}
|
||||
expect_stdout: true
|
||||
@@ -365,7 +365,7 @@ unsafe_object_repeated: {
|
||||
o + 1,
|
||||
2,
|
||||
o.b + 1,
|
||||
1..b + 1
|
||||
NaN
|
||||
);
|
||||
}
|
||||
expect_stdout: true
|
||||
@@ -444,8 +444,8 @@ unsafe_integer_key: {
|
||||
2,
|
||||
2,
|
||||
({0:1})[1] + 1,
|
||||
1[1] + 1,
|
||||
1["1"] + 1
|
||||
NaN,
|
||||
NaN
|
||||
);
|
||||
}
|
||||
expect_stdout: true
|
||||
@@ -500,8 +500,8 @@ unsafe_float_key: {
|
||||
2,
|
||||
2,
|
||||
({2.72:1})[3.14] + 1,
|
||||
1[3.14] + 1,
|
||||
1["3.14"] + 1
|
||||
NaN,
|
||||
NaN
|
||||
);
|
||||
}
|
||||
expect_stdout: true
|
||||
@@ -635,12 +635,12 @@ unsafe_string_bad_index: {
|
||||
}
|
||||
expect: {
|
||||
console.log(
|
||||
"1234".a + 1,
|
||||
"1234"["a"] + 1,
|
||||
"1234"[3.14] + 1
|
||||
NaN,
|
||||
NaN,
|
||||
NaN
|
||||
);
|
||||
}
|
||||
expect_stdout: true
|
||||
expect_stdout: "NaN NaN NaN"
|
||||
}
|
||||
|
||||
prototype_function: {
|
||||
@@ -820,8 +820,8 @@ unsafe_charAt_noop: {
|
||||
}
|
||||
expect: {
|
||||
console.log(
|
||||
s.charAt(0),
|
||||
"string".charAt(x),
|
||||
s[0],
|
||||
"string"[0 | x],
|
||||
(typeof x)[0]
|
||||
);
|
||||
}
|
||||
@@ -1712,3 +1712,48 @@ unsafe_escaped: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
unsafe_string_replace: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
"foo".replace("f", function() {
|
||||
console.log("PASS");
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
"foo".replace("f", function() {
|
||||
console.log("PASS");
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3387_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
}
|
||||
input: {
|
||||
console.log(1 + (2 + "3"[4]));
|
||||
}
|
||||
expect: {
|
||||
console.log(1 + (2 + "3"[4]));
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
issue_3387_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(1 + (2 + "3"[4]));
|
||||
}
|
||||
expect: {
|
||||
console.log(NaN);
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
@@ -3003,12 +3003,10 @@ issue_3366: {
|
||||
f();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
function a() {}
|
||||
(function() {
|
||||
this && a && console.log("PASS");
|
||||
})();
|
||||
})();
|
||||
void function() {
|
||||
this && a && console.log("PASS");
|
||||
}();
|
||||
function a() {}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
@@ -3041,3 +3039,29 @@ issue_3371: {
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
class_iife: {
|
||||
options = {
|
||||
inline: true,
|
||||
sequences: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
var A = function() {
|
||||
function B() {}
|
||||
B.prototype.m = function() {
|
||||
console.log("PASS");
|
||||
};
|
||||
return B;
|
||||
}();
|
||||
new A().m();
|
||||
}
|
||||
expect: {
|
||||
var A = (B.prototype.m = function() {
|
||||
console.log("PASS");
|
||||
}, B);
|
||||
function B() {}
|
||||
new A().m();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -59,9 +59,9 @@ do_screw_try_catch: {
|
||||
input: {
|
||||
good = function(e){
|
||||
return function(error){
|
||||
try{
|
||||
try {
|
||||
e()
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
error(e)
|
||||
}
|
||||
}
|
||||
@@ -70,9 +70,9 @@ do_screw_try_catch: {
|
||||
expect: {
|
||||
good = function(n){
|
||||
return function(t){
|
||||
try{
|
||||
try {
|
||||
n()
|
||||
} catch(n) {
|
||||
} catch (n) {
|
||||
t(n)
|
||||
}
|
||||
}
|
||||
@@ -93,9 +93,9 @@ dont_screw_try_catch: {
|
||||
input: {
|
||||
bad = function(e){
|
||||
return function(error){
|
||||
try{
|
||||
try {
|
||||
e()
|
||||
} catch(e) {
|
||||
} catch (e) {
|
||||
error(e)
|
||||
}
|
||||
}
|
||||
@@ -104,9 +104,9 @@ dont_screw_try_catch: {
|
||||
expect: {
|
||||
bad = function(t){
|
||||
return function(n){
|
||||
try{
|
||||
try {
|
||||
t()
|
||||
} catch(t) {
|
||||
} catch (t) {
|
||||
n(t)
|
||||
}
|
||||
}
|
||||
@@ -137,7 +137,7 @@ do_screw_try_catch_undefined: {
|
||||
}
|
||||
expect: {
|
||||
function a(o){
|
||||
try{
|
||||
try {
|
||||
throw "Stuff"
|
||||
} catch (o) {
|
||||
console.log("caught: "+o)
|
||||
@@ -172,7 +172,7 @@ dont_screw_try_catch_undefined: {
|
||||
}
|
||||
expect: {
|
||||
function a(n){
|
||||
try{
|
||||
try {
|
||||
throw "Stuff"
|
||||
} catch (undefined) {
|
||||
console.log("caught: " + undefined)
|
||||
|
||||
@@ -59,7 +59,6 @@ non_hoisted_function_after_return_2a: {
|
||||
passes: 2,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
warnings: "verbose",
|
||||
}
|
||||
input: {
|
||||
function foo(x) {
|
||||
@@ -91,13 +90,13 @@ non_hoisted_function_after_return_2a: {
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
|
||||
"WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]",
|
||||
"WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]",
|
||||
"WARN: pass 0: last_count: Infinity, count: 37",
|
||||
"INFO: pass 0: last_count: Infinity, count: 37",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
|
||||
"WARN: Dropping unused variable b [test/compress/issue-1034.js:7,20]",
|
||||
"WARN: Dropping unused variable c [test/compress/issue-1034.js:9,16]",
|
||||
"WARN: pass 1: last_count: 37, count: 18",
|
||||
"INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]",
|
||||
"INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]",
|
||||
"INFO: pass 1: last_count: 37, count: 18",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -213,7 +212,6 @@ non_hoisted_function_after_return_2a_strict: {
|
||||
passes: 2,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
warnings: "verbose",
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
@@ -250,13 +248,13 @@ non_hoisted_function_after_return_2a_strict: {
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]",
|
||||
"WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]",
|
||||
"WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]",
|
||||
"WARN: pass 0: last_count: Infinity, count: 48",
|
||||
"INFO: pass 0: last_count: Infinity, count: 48",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
|
||||
"WARN: Dropping unused variable b [test/compress/issue-1034.js:8,20]",
|
||||
"WARN: Dropping unused variable c [test/compress/issue-1034.js:10,16]",
|
||||
"WARN: pass 1: last_count: 48, count: 29",
|
||||
"INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]",
|
||||
"INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]",
|
||||
"INFO: pass 1: last_count: 48, count: 29",
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
issue_1639_1: {
|
||||
options = {
|
||||
booleans: true,
|
||||
@@ -12,7 +11,6 @@ issue_1639_1: {
|
||||
}
|
||||
input: {
|
||||
var a = 100, b = 10;
|
||||
|
||||
var L1 = 5;
|
||||
while (--L1 > 0) {
|
||||
if ((--b), false) {
|
||||
@@ -21,7 +19,6 @@ issue_1639_1: {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
@@ -29,7 +26,7 @@ issue_1639_1: {
|
||||
if (--b, 0) var ignore = 0;
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: true
|
||||
expect_stdout: "100 6"
|
||||
}
|
||||
|
||||
issue_1639_2: {
|
||||
@@ -44,25 +41,23 @@ issue_1639_2: {
|
||||
}
|
||||
input: {
|
||||
var a = 100, b = 10;
|
||||
|
||||
function f19() {
|
||||
if (++a, false)
|
||||
if (a)
|
||||
if (++a);
|
||||
}
|
||||
f19();
|
||||
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
var a = 100, b = 10;
|
||||
function f19() {
|
||||
++a, 0;
|
||||
++a, 1;
|
||||
}
|
||||
f19(),
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: true
|
||||
expect_stdout: "101 10"
|
||||
}
|
||||
|
||||
issue_1639_3: {
|
||||
@@ -84,5 +79,5 @@ issue_1639_3: {
|
||||
a++,
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: true
|
||||
expect_stdout: "101 10"
|
||||
}
|
||||
|
||||
@@ -1862,3 +1862,29 @@ join_expr: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3389: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
properties: true,
|
||||
reduce_vars: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a = "PASS";
|
||||
if (delete b)
|
||||
b = a[null] = 42;
|
||||
console.log(a);
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a = "PASS";
|
||||
if (delete b)
|
||||
b = a.null = 42;
|
||||
console.log(a);
|
||||
})();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -192,38 +192,35 @@ unsafe_evaluate: {
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
function f0(){
|
||||
var a = {
|
||||
b:1
|
||||
};
|
||||
function f0() {
|
||||
var a = { b: 1 };
|
||||
console.log(a.b + 3);
|
||||
}
|
||||
|
||||
function f1(){
|
||||
function f1() {
|
||||
var a = {
|
||||
b:{
|
||||
c:1
|
||||
},
|
||||
d:2
|
||||
b: { c: 1 },
|
||||
d: 2
|
||||
};
|
||||
console.log(a.b + 3, a.d + 4, a.b.c + 5, a.d.c + 6);
|
||||
}
|
||||
f0();
|
||||
f1();
|
||||
}
|
||||
expect: {
|
||||
function f0(){
|
||||
function f0() {
|
||||
console.log(4);
|
||||
}
|
||||
|
||||
function f1(){
|
||||
function f1() {
|
||||
var a = {
|
||||
b:{
|
||||
c:1
|
||||
},
|
||||
d:2
|
||||
b: { c: 1 },
|
||||
d: 2
|
||||
};
|
||||
console.log(a.b + 3, 6, 6, 2..c + 6);
|
||||
console.log(a.b + 3, 6, 6, NaN);
|
||||
}
|
||||
f0();
|
||||
f1();
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
unsafe_evaluate_side_effect_free_1: {
|
||||
@@ -6737,3 +6734,21 @@ drop_side_effect_free: {
|
||||
}
|
||||
expect_stdout: "123"
|
||||
}
|
||||
|
||||
issue_3377: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function f() {
|
||||
return f[0], (f = 42);
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function f() {
|
||||
return f[0], (f = 42);
|
||||
}());
|
||||
}
|
||||
expect_stdout: "42"
|
||||
}
|
||||
|
||||
@@ -702,7 +702,7 @@ side_effects_cascade_3: {
|
||||
}
|
||||
expect: {
|
||||
function f(a, b) {
|
||||
!(b += a) && ((b = a) || (b -= a, b ^= a)),
|
||||
(b += a) || (b = a) || (b -= a, b ^= a),
|
||||
a--;
|
||||
}
|
||||
}
|
||||
@@ -964,3 +964,39 @@ missing_link: {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
|
||||
angularjs_chain: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
function nonComputedMember(left, right, context, create) {
|
||||
var lhs = left();
|
||||
if (create && create !== 1) {
|
||||
if (lhs && lhs[right] == null) {
|
||||
lhs[right] = {};
|
||||
}
|
||||
}
|
||||
var value = lhs != null ? lhs[right] : undefined;
|
||||
if (context) {
|
||||
return { context: lhs, name: right, value: value };
|
||||
} else {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
function nonComputedMember(left, right, context, create) {
|
||||
var lhs = left();
|
||||
create && 1 !== create && lhs && null == lhs[right] && (lhs[right] = {});
|
||||
var value = null != lhs ? lhs[right] : void 0;
|
||||
return context ? {
|
||||
context: lhs,
|
||||
name: right,
|
||||
value: value
|
||||
} : value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
@@ -47,7 +47,7 @@ describe("bin/uglifyjs", function() {
|
||||
});
|
||||
});
|
||||
it("Should give sensible error against invalid input source map", function(done) {
|
||||
var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline";
|
||||
var command = uglifyjscmd + " test/mocha.js --source-map content=blah,url=inline --verbose";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.deepEqual(stderr.split(/\n/).slice(0, 2), [
|
||||
@@ -83,6 +83,7 @@ describe("bin/uglifyjs", function() {
|
||||
"test/input/issue-2082/sample.js",
|
||||
"--source-map", "content=test/input/issue-2082/sample.js.map",
|
||||
"--source-map", "url=inline",
|
||||
"--verbose",
|
||||
].join(" ");
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
@@ -113,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) {
|
||||
@@ -202,7 +202,7 @@ describe("bin/uglifyjs", function() {
|
||||
});
|
||||
});
|
||||
it("Should warn for missing inline source map", function(done) {
|
||||
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline";
|
||||
var command = uglifyjscmd + " test/input/issue-1323/sample.js --source-map content=inline,url=inline --warn";
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
assert.strictEqual(stdout, [
|
||||
@@ -221,6 +221,7 @@ describe("bin/uglifyjs", function() {
|
||||
"test/input/issue-520/input.js",
|
||||
"test/input/issue-1323/sample.js",
|
||||
"--source-map", "content=inline,url=inline",
|
||||
"--warn",
|
||||
].join(" ");
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
if (err) throw err;
|
||||
@@ -466,7 +467,7 @@ describe("bin/uglifyjs", function() {
|
||||
done();
|
||||
});
|
||||
});
|
||||
it("Should throw syntax error (catch(eval))", function(done) {
|
||||
it("Should throw syntax error (catch (eval))", function(done) {
|
||||
var command = uglifyjscmd + ' test/input/invalid/try.js';
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
@@ -647,7 +648,7 @@ describe("bin/uglifyjs", function() {
|
||||
exec(command, function(err, stdout, stderr) {
|
||||
assert.ok(err);
|
||||
assert.strictEqual(stdout, "");
|
||||
assert.strictEqual(stderr, "Error parsing arguments for 'define': a-b\n");
|
||||
assert.strictEqual(stderr, "ERROR: cannot parse arguments for 'define': a-b\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
@@ -711,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);');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -118,51 +118,33 @@ describe("sourcemaps", function() {
|
||||
assert.strictEqual(result.code + "\n", readFileSync("test/input/issue-520/output.js", "utf8"));
|
||||
});
|
||||
it("Should warn for missing inline source map", function() {
|
||||
var warn_function = UglifyJS.AST_Node.warn_function;
|
||||
var warnings = [];
|
||||
UglifyJS.AST_Node.warn_function = function(txt) {
|
||||
warnings.push(txt);
|
||||
};
|
||||
try {
|
||||
var result = UglifyJS.minify(read("./test/input/issue-1323/sample.js"), {
|
||||
mangle: false,
|
||||
sourceMap: {
|
||||
content: "inline"
|
||||
}
|
||||
});
|
||||
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
|
||||
assert.strictEqual(warnings.length, 1);
|
||||
assert.strictEqual(warnings[0], "inline source map not found: 0");
|
||||
} finally {
|
||||
UglifyJS.AST_Node.warn_function = warn_function;
|
||||
}
|
||||
var result = UglifyJS.minify(read("./test/input/issue-1323/sample.js"), {
|
||||
mangle: false,
|
||||
sourceMap: {
|
||||
content: "inline"
|
||||
},
|
||||
warnings: true,
|
||||
});
|
||||
assert.strictEqual(result.code, "var bar=function(bar){return bar};");
|
||||
assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 0" ]);
|
||||
});
|
||||
it("Should handle multiple input and inline source map", function() {
|
||||
var warn_function = UglifyJS.AST_Node.warn_function;
|
||||
var warnings = [];
|
||||
UglifyJS.AST_Node.warn_function = function(txt) {
|
||||
warnings.push(txt);
|
||||
};
|
||||
try {
|
||||
var result = UglifyJS.minify([
|
||||
read("./test/input/issue-520/input.js"),
|
||||
read("./test/input/issue-1323/sample.js"),
|
||||
], {
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
url: "inline",
|
||||
}
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code, [
|
||||
"var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};",
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=",
|
||||
].join("\n"));
|
||||
assert.strictEqual(warnings.length, 1);
|
||||
assert.strictEqual(warnings[0], "inline source map not found: 1");
|
||||
} finally {
|
||||
UglifyJS.AST_Node.warn_function = warn_function;
|
||||
}
|
||||
var result = UglifyJS.minify([
|
||||
read("./test/input/issue-520/input.js"),
|
||||
read("./test/input/issue-1323/sample.js"),
|
||||
], {
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
url: "inline",
|
||||
},
|
||||
warnings: true,
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code, [
|
||||
"var Foo=function(){console.log(3)};new Foo;var bar=function(o){return o};",
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbInN0ZGluIiwiMSJdLCJuYW1lcyI6WyJGb28iLCJjb25zb2xlIiwibG9nIiwiYmFyIl0sIm1hcHBpbmdzIjoiQUFBQSxJQUFNQSxJQUFJLFdBQWdCQyxRQUFRQyxJQUFJLElBQVMsSUFBSUYsSUNBbkQsSUFBSUcsSUFDQSxTQUFjQSxHQUNWLE9BQU9BIn0=",
|
||||
].join("\n"));
|
||||
assert.deepEqual(result.warnings, [ "WARN: inline source map not found: 1" ]);
|
||||
});
|
||||
it("Should drop source contents for includeSources=false", function() {
|
||||
var result = UglifyJS.minify(read("./test/input/issue-520/input.js"), {
|
||||
@@ -187,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() {
|
||||
|
||||
@@ -219,8 +219,9 @@ function run_compress_tests() {
|
||||
var input_code = make_code(input);
|
||||
var input_formatted = make_code(test.input, {
|
||||
beautify: true,
|
||||
comments: "all",
|
||||
keep_quoted_props: true,
|
||||
quote_style: 3,
|
||||
keep_quoted_props: true
|
||||
});
|
||||
try {
|
||||
U.parse(input_code);
|
||||
@@ -231,16 +232,15 @@ function run_compress_tests() {
|
||||
});
|
||||
return false;
|
||||
}
|
||||
var options = U.defaults(test.options, {
|
||||
warnings: false
|
||||
});
|
||||
var warnings_emitted = [];
|
||||
var original_warn_function = U.AST_Node.warn_function;
|
||||
if (test.expect_warnings) {
|
||||
U.AST_Node.warn_function = function(text) {
|
||||
warnings_emitted.push("WARN: " + text);
|
||||
};
|
||||
if (!options.warnings) options.warnings = true;
|
||||
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;
|
||||
@@ -252,7 +252,7 @@ function run_compress_tests() {
|
||||
input.figure_out_scope(test.mangle);
|
||||
input.expand_names(test.mangle);
|
||||
}
|
||||
var cmp = new U.Compressor(options, true);
|
||||
var cmp = new U.Compressor(test.options, true);
|
||||
var output = cmp.compress(input);
|
||||
output.figure_out_scope(test.mangle);
|
||||
if (test.mangle) {
|
||||
@@ -283,11 +283,6 @@ function run_compress_tests() {
|
||||
return false;
|
||||
}
|
||||
if (test.expect_warnings) {
|
||||
U.AST_Node.warn_function = original_warn_function;
|
||||
var expected_warnings = make_code(test.expect_warnings, {
|
||||
beautify: false,
|
||||
quote_style: 2, // force double quote to match JSON
|
||||
});
|
||||
warnings_emitted = warnings_emitted.map(function(input) {
|
||||
return input.split(process.cwd() + path.sep).join("").split(path.sep).join("/");
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user