Compare commits

...

13 Commits

Author SHA1 Message Date
Alex Lam S.L
429d2b56b7 v3.5.10 2019-05-02 00:01:40 +08:00
Alex Lam S.L
2ea96549c5 unify logging functionality (#3392)
fixes #3253
fixes #3254
2019-04-30 06:32:24 +08:00
Alex Lam S.L
fba008e298 remove blanket safeguard from source-map (#3391)
Things has stabilised since 80a18fe2fa, so it makes sense to remove the unconditional masking for ease of debugging.
2019-04-30 02:40:36 +08:00
Alex Lam S.L
c37a8e927e fix corner case in properties (#3390)
fixes #3389
2019-04-29 17:23:00 +08:00
Alex Lam S.L
413bbe0480 fix corner case in evaluate (#3388)
fixes #3387
2019-04-29 08:55:46 +08:00
Alex Lam S.L
34075fc4c4 v3.5.9 2019-04-27 17:00:58 +08:00
Alex Lam S.L
e5436ca566 enhance side_effects (#3384) 2019-04-25 15:15:50 +08:00
Alex Lam S.L
cfde686eab v3.5.8 2019-04-25 12:33:13 +08:00
Alex Lam S.L
a206964c0a enhance side_effects (#3383) 2019-04-25 04:14:21 +08:00
Alex Lam S.L
c56d89f804 enhance unsafe (#3382) 2019-04-25 02:42:54 +08:00
Alex Lam S.L
c215706350 enhance unsafe comparisons (#3381) 2019-04-25 00:08:08 +08:00
Alex Lam S.L
d3b93ec682 fix corner case in unsafe (#3380) 2019-04-24 22:21:28 +08:00
Alex Lam S.L
6fe20dbe33 enhance comparisons (#3379) 2019-04-24 21:38:55 +08:00
23 changed files with 608 additions and 301 deletions

View File

@@ -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;
@@ -293,7 +292,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);
}
@@ -372,7 +375,7 @@ function parse_js(flag) {
}));
} catch (ex) {
if (flag) {
fatal("Error parsing arguments for '" + flag + "': " + value);
fatal("cannot parse arguments for '" + flag + "': " + value);
} else {
options[value] = null;
}

View File

@@ -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 ]----- */

View File

@@ -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;
@@ -1138,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,
@@ -1963,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);
});
}
@@ -2216,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;
@@ -2296,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;
@@ -2346,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
@@ -2490,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);
});
@@ -2543,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) {
@@ -2560,7 +2596,7 @@ merge(Compressor.prototype, {
child = parent;
}
if (is_lhs(child, parent)) {
warn(compressor, node);
warn(node);
return;
}
return def;
@@ -2572,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;
@@ -2935,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 : "";
@@ -2976,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,
@@ -3084,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
@@ -3126,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);
@@ -3584,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;
@@ -3594,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);
}
@@ -3615,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);
@@ -3640,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);
@@ -3677,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++;
}
@@ -4106,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();
var node = this;
if (right !== node.right) {
node = this.clone();
node.right = right.drop_side_effect_free(compressor);
return node;
}
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;
@@ -4118,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) {
@@ -4140,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);
});
@@ -4225,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) {
@@ -4304,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
});
@@ -4377,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) {
@@ -4424,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
}));
@@ -4486,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
@@ -4503,7 +4548,7 @@ merge(Compressor.prototype, {
operator : "||",
left : self.condition,
right : self.alternative.body
})
}).transform(compressor)
}).optimize(compressor);
}
if (self.body instanceof AST_Exit
@@ -4652,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);
}
}
});
@@ -4663,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) {
@@ -4775,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,
@@ -4835,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,
@@ -4919,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, {
if (self.args.length < 2) {
var node = make_node(AST_Sub, self, {
expression: exp.expression,
property: make_node_from_constant(index | 0, arg || exp)
}).optimize(compressor);
}
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":
@@ -5012,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;
}
@@ -5383,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)
@@ -5451,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
@@ -5490,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)) ||
@@ -5528,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;
@@ -5556,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)
@@ -5592,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 == "+"
@@ -5605,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")) {
@@ -5613,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)
@@ -5632,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);
}
}
@@ -5649,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);
@@ -5660,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)
@@ -5860,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
@@ -5893,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) {
@@ -5921,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));
@@ -6139,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),
@@ -6209,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));
@@ -6451,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,
@@ -6543,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) {
@@ -6634,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") {

View File

@@ -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) {
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) {
@@ -240,7 +236,5 @@ function minify(files, options) {
return result;
} catch (ex) {
return { error: ex };
} finally {
AST_Node.warn_function = warn_function;
}
}

View File

@@ -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 || ""
})
}
});
mappings = [];
} : noop;

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.7",
"version": "3.5.10",
"engines": {
"node": ">=0.8.0"
},

View File

@@ -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"
}

View File

@@ -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;
}
}
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -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",
]
}

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -193,37 +193,34 @@ unsafe_evaluate: {
}
input: {
function f0() {
var a = {
b:1
};
var a = { b: 1 };
console.log(a.b + 3);
}
function f1() {
var a = {
b:{
c:1
},
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() {
console.log(4);
}
function f1() {
var a = {
b:{
c:1
},
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: {

View File

@@ -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;
}
}
}

View File

@@ -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;
@@ -202,7 +203,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 +222,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;
@@ -647,7 +649,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();
});
});

View File

@@ -118,32 +118,17 @@ 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"
}
},
warnings: true,
});
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;
}
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"),
@@ -151,18 +136,15 @@ describe("sourcemaps", function() {
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.strictEqual(warnings.length, 1);
assert.strictEqual(warnings[0], "inline source map not found: 1");
} finally {
UglifyJS.AST_Node.warn_function = warn_function;
}
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"), {

View File

@@ -231,16 +231,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 +251,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 +282,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("/");
});