Compare commits
25 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5843494ee2 | ||
|
|
efa21ae3e6 | ||
|
|
24d9633a35 | ||
|
|
7963b96681 | ||
|
|
8c62d854ce | ||
|
|
69931574e1 | ||
|
|
b5af8a1914 | ||
|
|
c14d09ba84 | ||
|
|
4fc39d8dad | ||
|
|
0b7c70f726 | ||
|
|
f72d3029dd | ||
|
|
1a0d6edc81 | ||
|
|
7b59b2f5b2 | ||
|
|
7bc7704edf | ||
|
|
14e712ee80 | ||
|
|
f83adcc995 | ||
|
|
df8a99439a | ||
|
|
6b91d12ec3 | ||
|
|
f37b91879f | ||
|
|
d835c72c80 | ||
|
|
c4cebb4b01 | ||
|
|
d51a00a450 | ||
|
|
fc0f168a0c | ||
|
|
a0ca595c2c | ||
|
|
1a314e9f60 |
@@ -118,7 +118,8 @@ a double dash to prevent input files being used as option arguments:
|
||||
JS that was generated from some other original
|
||||
code. Specify "inline" if the source map is
|
||||
included within the sources.
|
||||
`filename` Name and/or location of the output source.
|
||||
`filename` Filename and/or location of the output source
|
||||
(sets `file` attribute in source map).
|
||||
`includeSources` Pass this flag if you want to include
|
||||
the content of source files in the
|
||||
source map as sourcesContent property.
|
||||
@@ -149,7 +150,9 @@ debugging your compressed JavaScript. To get a source map, pass
|
||||
|
||||
Additional options:
|
||||
|
||||
- `--source-map "filename='<NAME>'"` to specify the name of the source map.
|
||||
- `--source-map "filename='<NAME>'"` to specify the name of the source map. The value of
|
||||
`filename` is only used to set `file` attribute (see [the spec][sm-spec])
|
||||
in source map file.
|
||||
|
||||
- `--source-map "root='<URL>'"` to pass the URL where the original files can be found.
|
||||
|
||||
|
||||
12
lib/ast.js
12
lib/ast.js
@@ -314,6 +314,9 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
|
||||
if (this.functions) node.functions = this.functions.clone();
|
||||
if (this.enclosed) node.enclosed = this.enclosed.slice();
|
||||
return node;
|
||||
},
|
||||
pinned: function() {
|
||||
return this.uses_eval || this.uses_with;
|
||||
}
|
||||
}, AST_Block);
|
||||
|
||||
@@ -543,12 +546,11 @@ var AST_Call = DEFNODE("Call", "expression args", {
|
||||
args: "[AST_Node*] array of arguments"
|
||||
},
|
||||
_walk: function(visitor) {
|
||||
return visitor._visit(this, function(){
|
||||
var args = this.args;
|
||||
for (var i = 0, len = args.length; i < len; i++) {
|
||||
args[i]._walk(visitor);
|
||||
}
|
||||
return visitor._visit(this, function() {
|
||||
this.expression._walk(visitor);
|
||||
this.args.forEach(function(node) {
|
||||
node._walk(visitor);
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
272
lib/compress.js
272
lib/compress.js
@@ -340,30 +340,38 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
|
||||
(function(def){
|
||||
(function(def) {
|
||||
def(AST_Node, noop);
|
||||
|
||||
function reset_def(compressor, def) {
|
||||
function reset_def(tw, compressor, def) {
|
||||
def.assignments = 0;
|
||||
def.chained = false;
|
||||
def.direct_access = false;
|
||||
def.escaped = false;
|
||||
if (def.scope.uses_eval || def.scope.uses_with) {
|
||||
if (def.scope.pinned()) {
|
||||
def.fixed = false;
|
||||
} else if (!compressor.exposed(def)) {
|
||||
def.fixed = def.init;
|
||||
} else {
|
||||
def.fixed = false;
|
||||
}
|
||||
if (def.fixed instanceof AST_Defun && !all(def.references, function(ref) {
|
||||
var scope = ref.scope;
|
||||
do {
|
||||
if (def.scope === scope) return true;
|
||||
} while (scope instanceof AST_Function && (scope = scope.parent_scope));
|
||||
})) {
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
def.recursive_refs = 0;
|
||||
def.references = [];
|
||||
def.should_replace = undefined;
|
||||
def.single_use = undefined;
|
||||
}
|
||||
|
||||
function reset_variables(tw, compressor, node) {
|
||||
node.variables.each(function(def) {
|
||||
reset_def(compressor, def);
|
||||
function reset_variables(tw, compressor, scope) {
|
||||
scope.variables.each(function(def) {
|
||||
reset_def(tw, compressor, def);
|
||||
if (def.fixed === null) {
|
||||
def.safe_ids = tw.safe_ids;
|
||||
mark(tw, def, true);
|
||||
@@ -372,6 +380,48 @@ merge(Compressor.prototype, {
|
||||
mark(tw, def, true);
|
||||
}
|
||||
});
|
||||
scope.may_call_this = function() {
|
||||
scope.may_call_this = noop;
|
||||
if (!scope.contains_this()) return;
|
||||
scope.functions.each(function(def) {
|
||||
if (def.init instanceof AST_Defun && !(def.id in tw.defun_ids)) {
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function mark_defun(tw, def) {
|
||||
if (def.id in tw.defun_ids) {
|
||||
var marker = tw.defun_ids[def.id];
|
||||
if (!marker) return;
|
||||
var visited = tw.defun_visited[def.id];
|
||||
if (marker === tw.safe_ids) {
|
||||
if (!visited) return def.fixed;
|
||||
} else if (visited) {
|
||||
def.init.enclosed.forEach(function(d) {
|
||||
if (def.init.variables.get(d.name) === d) return;
|
||||
if (!safe_to_read(tw, d)) d.fixed = false;
|
||||
});
|
||||
} else {
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
} else {
|
||||
if (!tw.in_loop) {
|
||||
tw.defun_ids[def.id] = tw.safe_ids;
|
||||
return def.fixed;
|
||||
}
|
||||
tw.defun_ids[def.id] = false;
|
||||
}
|
||||
}
|
||||
|
||||
function walk_defuns(tw, scope) {
|
||||
scope.functions.each(function(def) {
|
||||
if (def.init instanceof AST_Defun && !tw.defun_visited[def.id]) {
|
||||
tw.defun_ids[def.id] = tw.safe_ids;
|
||||
def.init.walk(tw);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function push(tw) {
|
||||
@@ -398,7 +448,7 @@ merge(Compressor.prototype, {
|
||||
return def.fixed instanceof AST_Defun;
|
||||
}
|
||||
|
||||
function safe_to_assign(tw, def, value) {
|
||||
function safe_to_assign(tw, def, scope, value) {
|
||||
if (def.fixed === undefined) return true;
|
||||
if (def.fixed === null && def.safe_ids) {
|
||||
def.safe_ids[def.id] = false;
|
||||
@@ -409,6 +459,9 @@ merge(Compressor.prototype, {
|
||||
if (!safe_to_read(tw, def)) return false;
|
||||
if (def.fixed === false) return false;
|
||||
if (def.fixed != null && (!value || def.references.length > def.assignments)) return false;
|
||||
if (def.fixed instanceof AST_Defun) {
|
||||
return value instanceof AST_Node && def.fixed.parent_scope === scope;
|
||||
}
|
||||
return all(def.orig, function(sym) {
|
||||
return !(sym instanceof AST_SymbolDefun
|
||||
|| sym instanceof AST_SymbolLambda);
|
||||
@@ -417,8 +470,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
function ref_once(tw, compressor, def) {
|
||||
return compressor.option("unused")
|
||||
&& !def.scope.uses_eval
|
||||
&& !def.scope.uses_with
|
||||
&& !def.scope.pinned()
|
||||
&& def.references.length - def.recursive_refs == 1
|
||||
&& tw.loop_ids[def.id] === tw.in_loop;
|
||||
}
|
||||
@@ -453,7 +505,10 @@ merge(Compressor.prototype, {
|
||||
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
|
||||
if (value) return;
|
||||
}
|
||||
if (level == 0) d.direct_access = true;
|
||||
if (level > 0) return;
|
||||
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
|
||||
if (parent instanceof AST_SimpleStatement) return;
|
||||
d.direct_access = true;
|
||||
}
|
||||
|
||||
var suppressor = new TreeWalker(function(node) {
|
||||
@@ -468,19 +523,24 @@ merge(Compressor.prototype, {
|
||||
reset_variables(tw, compressor, this);
|
||||
descend();
|
||||
pop(tw);
|
||||
walk_defuns(tw, this);
|
||||
return true;
|
||||
});
|
||||
def(AST_Assign, function(tw) {
|
||||
def(AST_Assign, function(tw, descend, compressor) {
|
||||
var node = this;
|
||||
if (!(node.left instanceof AST_SymbolRef)) return;
|
||||
var d = node.left.definition();
|
||||
var sym = node.left;
|
||||
if (!(sym instanceof AST_SymbolRef)) return;
|
||||
var d = sym.definition();
|
||||
var fixed = d.fixed;
|
||||
if (!fixed && node.operator != "=") return;
|
||||
if (!safe_to_assign(tw, d, node.right)) return;
|
||||
d.references.push(node.left);
|
||||
if (!safe_to_assign(tw, d, sym.scope, node.right)) return;
|
||||
var eq = node.operator == "=";
|
||||
var value = eq ? node.right : node;
|
||||
if (is_modified(compressor, tw, node, value, 0)) return;
|
||||
d.references.push(sym);
|
||||
d.assignments++;
|
||||
if (node.operator != "=") d.chained = true;
|
||||
d.fixed = node.operator == "=" ? function() {
|
||||
if (!eq) d.chained = true;
|
||||
d.fixed = eq ? function() {
|
||||
return node.right;
|
||||
} : function() {
|
||||
return make_node(AST_Binary, node, {
|
||||
@@ -492,6 +552,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);
|
||||
return true;
|
||||
});
|
||||
def(AST_Binary, function(tw) {
|
||||
@@ -502,6 +563,18 @@ merge(Compressor.prototype, {
|
||||
pop(tw);
|
||||
return true;
|
||||
});
|
||||
def(AST_Call, function(tw, descend) {
|
||||
tw.find_parent(AST_Scope).may_call_this();
|
||||
var exp = this.expression;
|
||||
if (!(exp instanceof AST_SymbolRef)) return;
|
||||
var def = exp.definition();
|
||||
if (!(def.fixed instanceof AST_Defun)) return;
|
||||
var defun = mark_defun(tw, def);
|
||||
if (!defun) return;
|
||||
descend();
|
||||
defun.walk(tw);
|
||||
return true;
|
||||
});
|
||||
def(AST_Case, function(tw) {
|
||||
push(tw);
|
||||
this.expression.walk(tw);
|
||||
@@ -528,12 +601,16 @@ merge(Compressor.prototype, {
|
||||
return true;
|
||||
});
|
||||
def(AST_Defun, function(tw, descend, compressor) {
|
||||
var id = this.name.definition().id;
|
||||
if (tw.defun_visited[id]) return true;
|
||||
if (tw.defun_ids[id] !== tw.safe_ids) return true;
|
||||
tw.defun_visited[id] = true;
|
||||
this.inlined = false;
|
||||
var save_ids = tw.safe_ids;
|
||||
tw.safe_ids = Object.create(null);
|
||||
push(tw);
|
||||
reset_variables(tw, compressor, this);
|
||||
descend();
|
||||
tw.safe_ids = save_ids;
|
||||
pop(tw);
|
||||
walk_defuns(tw, this);
|
||||
return true;
|
||||
});
|
||||
def(AST_Do, function(tw) {
|
||||
@@ -606,6 +683,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
descend();
|
||||
pop(tw);
|
||||
walk_defuns(tw, node);
|
||||
return true;
|
||||
});
|
||||
def(AST_If, function(tw) {
|
||||
@@ -645,7 +723,7 @@ merge(Compressor.prototype, {
|
||||
if (value instanceof AST_Lambda && recursive_ref(tw, d)) {
|
||||
d.recursive_refs++;
|
||||
} else if (value && ref_once(tw, compressor, d)) {
|
||||
d.single_use = value instanceof AST_Lambda
|
||||
d.single_use = value instanceof AST_Lambda && !value.pinned()
|
||||
|| d.scope === this.scope && value.is_constant_expression();
|
||||
} else {
|
||||
d.single_use = false;
|
||||
@@ -659,12 +737,23 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
mark_escaped(tw, d, this.scope, this, value, 0, 1);
|
||||
var parent;
|
||||
if (d.fixed instanceof AST_Defun
|
||||
&& !((parent = tw.parent()) instanceof AST_Call && parent.expression === this)) {
|
||||
var defun = mark_defun(tw, d);
|
||||
if (defun) defun.walk(tw);
|
||||
}
|
||||
});
|
||||
def(AST_Toplevel, function(tw, descend, compressor) {
|
||||
this.globals.each(function(def) {
|
||||
reset_def(compressor, def);
|
||||
reset_def(tw, compressor, def);
|
||||
});
|
||||
push(tw);
|
||||
reset_variables(tw, compressor, this);
|
||||
descend();
|
||||
pop(tw);
|
||||
walk_defuns(tw, this);
|
||||
return true;
|
||||
});
|
||||
def(AST_Try, function(tw) {
|
||||
push(tw);
|
||||
@@ -681,12 +770,13 @@ merge(Compressor.prototype, {
|
||||
def(AST_Unary, function(tw, descend) {
|
||||
var node = this;
|
||||
if (node.operator != "++" && node.operator != "--") return;
|
||||
if (!(node.expression instanceof AST_SymbolRef)) return;
|
||||
var d = node.expression.definition();
|
||||
var exp = node.expression;
|
||||
if (!(exp instanceof AST_SymbolRef)) return;
|
||||
var d = exp.definition();
|
||||
var fixed = d.fixed;
|
||||
if (!fixed) return;
|
||||
if (!safe_to_assign(tw, d, true)) return;
|
||||
d.references.push(node.expression);
|
||||
if (!safe_to_assign(tw, d, exp.scope, true)) return;
|
||||
d.references.push(exp);
|
||||
d.assignments++;
|
||||
d.chained = true;
|
||||
d.fixed = function() {
|
||||
@@ -708,7 +798,7 @@ merge(Compressor.prototype, {
|
||||
var node = this;
|
||||
var d = node.name.definition();
|
||||
if (node.value) {
|
||||
if (safe_to_assign(tw, d, node.value)) {
|
||||
if (safe_to_assign(tw, d, node.name.scope, node.value)) {
|
||||
d.fixed = function() {
|
||||
return node.value;
|
||||
};
|
||||
@@ -736,19 +826,25 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
AST_Toplevel.DEFMETHOD("reset_opt_flags", function(compressor) {
|
||||
var reduce_vars = compressor.option("reduce_vars");
|
||||
var tw = new TreeWalker(function(node, descend) {
|
||||
var tw = new TreeWalker(compressor.option("reduce_vars") ? function(node, descend) {
|
||||
node._squeezed = false;
|
||||
node._optimized = false;
|
||||
return node.reduce_vars(tw, descend, compressor);
|
||||
} : function(node) {
|
||||
node._squeezed = false;
|
||||
node._optimized = false;
|
||||
if (reduce_vars) return node.reduce_vars(tw, descend, compressor);
|
||||
});
|
||||
// Flow control for visiting `AST_Defun`s
|
||||
tw.defun_ids = Object.create(null);
|
||||
tw.defun_visited = Object.create(null);
|
||||
// Record the loop body in which `AST_SymbolDeclaration` is first encountered
|
||||
tw.in_loop = null;
|
||||
tw.loop_ids = Object.create(null);
|
||||
// Stack of look-up tables to keep track of whether a `SymbolDef` has been
|
||||
// properly assigned before use:
|
||||
// - `push()` & `pop()` when visiting conditional branches
|
||||
// - backup & restore via `save_ids` when visiting out-of-order sections
|
||||
tw.safe_ids = Object.create(null);
|
||||
tw.in_loop = null;
|
||||
tw.loop_ids = Object.create(null);
|
||||
this.walk(tw);
|
||||
});
|
||||
|
||||
@@ -840,15 +936,21 @@ merge(Compressor.prototype, {
|
||||
type: typeof val
|
||||
}));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function needs_unbinding(compressor, val) {
|
||||
return val instanceof AST_PropAccess
|
||||
|| compressor.has_directive("use strict")
|
||||
&& is_undeclared_ref(val)
|
||||
&& val.name == "eval";
|
||||
}
|
||||
|
||||
// we shouldn't compress (1,func)(something) to
|
||||
// func(something) because that changes the meaning of
|
||||
// the func (becomes lexical instead of global).
|
||||
function maintain_this_binding(parent, orig, val) {
|
||||
function maintain_this_binding(compressor, parent, orig, val) {
|
||||
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|
||||
|| parent instanceof AST_Call && parent.expression === orig
|
||||
&& (val instanceof AST_PropAccess || val instanceof AST_SymbolRef && val.name == "eval")) {
|
||||
|| parent.TYPE == "Call" && parent.expression === orig && needs_unbinding(compressor, val)) {
|
||||
return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
|
||||
}
|
||||
return val;
|
||||
@@ -962,7 +1064,7 @@ merge(Compressor.prototype, {
|
||||
// Will not attempt to collapse assignments into or past code blocks
|
||||
// which are not sequentially executed, e.g. loops and conditionals.
|
||||
function collapse(statements, compressor) {
|
||||
if (scope.uses_eval || scope.uses_with) return statements;
|
||||
if (scope.pinned()) return statements;
|
||||
var args;
|
||||
var candidates = [];
|
||||
var stat_index = statements.length;
|
||||
@@ -1001,9 +1103,11 @@ merge(Compressor.prototype, {
|
||||
if (is_lhs(node, parent)) {
|
||||
if (value_def) replaced++;
|
||||
return node;
|
||||
} else {
|
||||
replaced++;
|
||||
if (value_def && candidate instanceof AST_VarDef) return node;
|
||||
}
|
||||
CHANGED = abort = true;
|
||||
replaced++;
|
||||
compressor.info("Collapsing {name} [{file}:{line},{col}]", {
|
||||
name: node.print_to_string(),
|
||||
file: node.start.file,
|
||||
@@ -1014,14 +1118,10 @@ merge(Compressor.prototype, {
|
||||
return make_node(AST_UnaryPrefix, candidate, candidate);
|
||||
}
|
||||
if (candidate instanceof AST_VarDef) {
|
||||
if (value_def) {
|
||||
abort = false;
|
||||
return node;
|
||||
}
|
||||
var def = candidate.name.definition();
|
||||
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
|
||||
def.replaced++;
|
||||
return maintain_this_binding(parent, node, candidate.value);
|
||||
return maintain_this_binding(compressor, parent, node, candidate.value);
|
||||
}
|
||||
return make_node(AST_Assign, candidate, {
|
||||
operator: "=",
|
||||
@@ -1062,7 +1162,7 @@ merge(Compressor.prototype, {
|
||||
if (is_lhs(node, multi_replacer.parent())) return node;
|
||||
def.replaced++;
|
||||
value_def.replaced--;
|
||||
return candidate.value;
|
||||
return candidate.value.clone();
|
||||
}
|
||||
// Skip (non-executed) functions and (leading) default case in switch statements
|
||||
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
|
||||
@@ -1149,7 +1249,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function should_stop(node, parent) {
|
||||
if (node instanceof AST_Assign) return node.operator != "=" && lhs.equivalent_to(node.left);
|
||||
if (parent instanceof AST_For) return node !== parent.init;
|
||||
if (node instanceof AST_Assign) {
|
||||
return node.operator != "=" && lhs.equivalent_to(node.left);
|
||||
}
|
||||
if (node instanceof AST_Call) {
|
||||
return lhs instanceof AST_PropAccess && lhs.equivalent_to(node.expression);
|
||||
}
|
||||
@@ -1158,7 +1261,6 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_LoopControl) return true;
|
||||
if (node instanceof AST_Try) return true;
|
||||
if (node instanceof AST_With) return true;
|
||||
if (parent instanceof AST_For) return node !== parent.init;
|
||||
if (replace_all) return false;
|
||||
return node instanceof AST_SymbolRef && !node.is_declared(compressor);
|
||||
}
|
||||
@@ -1198,7 +1300,7 @@ merge(Compressor.prototype, {
|
||||
if (fn instanceof AST_Function
|
||||
&& !fn.name
|
||||
&& !fn.uses_arguments
|
||||
&& !fn.uses_eval
|
||||
&& !fn.pinned()
|
||||
&& (iife = compressor.parent()) instanceof AST_Call
|
||||
&& iife.expression === fn) {
|
||||
var fn_strict = compressor.has_directive("use strict");
|
||||
@@ -1215,9 +1317,12 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
if (sym.name in names) continue;
|
||||
names[sym.name] = true;
|
||||
if (!arg) arg = make_node(AST_Undefined, sym).transform(compressor);
|
||||
else {
|
||||
var tw = new TreeWalker(function(node) {
|
||||
if (!arg) {
|
||||
arg = make_node(AST_Undefined, sym).transform(compressor);
|
||||
} else if (arg instanceof AST_Lambda && arg.pinned()) {
|
||||
arg = null;
|
||||
} else {
|
||||
arg.walk(new TreeWalker(function(node) {
|
||||
if (!arg) return true;
|
||||
if (node instanceof AST_SymbolRef && fn.variables.has(node.name)) {
|
||||
var s = node.definition().scope;
|
||||
@@ -1226,12 +1331,11 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
arg = null;
|
||||
}
|
||||
if (node instanceof AST_This && (fn_strict || !tw.find_parent(AST_Scope))) {
|
||||
if (node instanceof AST_This && (fn_strict || !this.find_parent(AST_Scope))) {
|
||||
arg = null;
|
||||
return true;
|
||||
}
|
||||
});
|
||||
arg.walk(tw);
|
||||
}));
|
||||
}
|
||||
if (arg) candidates.unshift([ make_node(AST_VarDef, sym, {
|
||||
name: sym,
|
||||
@@ -3159,7 +3263,7 @@ merge(Compressor.prototype, {
|
||||
if (!compressor.option("unused")) return;
|
||||
if (compressor.has_directive("use asm")) return;
|
||||
var self = this;
|
||||
if (self.uses_eval || self.uses_with) return;
|
||||
if (self.pinned()) return;
|
||||
var drop_funcs = !(self instanceof AST_Toplevel) || compressor.toplevel.funcs;
|
||||
var drop_vars = !(self instanceof AST_Toplevel) || compressor.toplevel.vars;
|
||||
var assign_as_unused = /keep_assign/.test(compressor.option("unused")) ? return_false : function(node, props) {
|
||||
@@ -3269,7 +3373,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (value) {
|
||||
props.push(value);
|
||||
return maintain_this_binding(parent, node, make_sequence(node, props.map(function(prop) {
|
||||
return maintain_this_binding(compressor, parent, node, make_sequence(node, props.map(function(prop) {
|
||||
return prop.transform(tt);
|
||||
})));
|
||||
}
|
||||
@@ -4395,7 +4499,24 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Call, function(self, compressor){
|
||||
AST_Call.DEFMETHOD("lift_sequences", function(compressor) {
|
||||
if (!compressor.option("sequences")) return this;
|
||||
var exp = this.expression;
|
||||
if (!(exp instanceof AST_Sequence)) return this;
|
||||
var tail = exp.tail_node();
|
||||
if (needs_unbinding(compressor, tail) && !(this instanceof AST_New)) return this;
|
||||
var expressions = exp.expressions.slice(0, -1);
|
||||
var node = this.clone();
|
||||
node.expression = tail;
|
||||
expressions.push(node);
|
||||
return make_sequence(this, expressions).optimize(compressor);
|
||||
});
|
||||
|
||||
OPT(AST_Call, function(self, compressor) {
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq;
|
||||
}
|
||||
var exp = self.expression;
|
||||
var fn = exp;
|
||||
if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
|
||||
@@ -4405,7 +4526,7 @@ merge(Compressor.prototype, {
|
||||
if (compressor.option("unused")
|
||||
&& is_func
|
||||
&& !fn.uses_arguments
|
||||
&& !fn.uses_eval) {
|
||||
&& !fn.pinned()) {
|
||||
var pos = 0, last = 0;
|
||||
for (var i = 0, len = self.args.length; i < len; i++) {
|
||||
var trim = i >= fn.argnames.length;
|
||||
@@ -4596,13 +4717,16 @@ merge(Compressor.prototype, {
|
||||
func = func.fixed_value();
|
||||
}
|
||||
if (func instanceof AST_Lambda && !func.contains_this()) {
|
||||
return make_sequence(this, [
|
||||
return (self.args.length ? make_sequence(this, [
|
||||
self.args[0],
|
||||
make_node(AST_Call, self, {
|
||||
expression: exp.expression,
|
||||
args: self.args.slice(1)
|
||||
})
|
||||
]).optimize(compressor);
|
||||
]) : make_node(AST_Call, self, {
|
||||
expression: exp.expression,
|
||||
args: []
|
||||
})).optimize(compressor);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -4682,7 +4806,7 @@ merge(Compressor.prototype, {
|
||||
var def, value, scope, in_loop, level = -1;
|
||||
if (can_inline
|
||||
&& !fn.uses_arguments
|
||||
&& !fn.uses_eval
|
||||
&& !fn.pinned()
|
||||
&& !(fn.name && fn instanceof AST_Function)
|
||||
&& (value = can_flatten_body(stat))
|
||||
&& (exp === fn
|
||||
@@ -4903,7 +5027,11 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
OPT(AST_New, function(self, compressor){
|
||||
OPT(AST_New, function(self, compressor) {
|
||||
var seq = self.lift_sequences(compressor);
|
||||
if (seq !== self) {
|
||||
return seq;
|
||||
}
|
||||
if (compressor.option("unsafe")) {
|
||||
var exp = self.expression;
|
||||
if (is_undeclared_ref(exp)) {
|
||||
@@ -4927,7 +5055,7 @@ merge(Compressor.prototype, {
|
||||
var end = expressions.length - 1;
|
||||
trim_right_for_undefined();
|
||||
if (end == 0) {
|
||||
self = maintain_this_binding(compressor.parent(), compressor.self(), expressions[0]);
|
||||
self = maintain_this_binding(compressor, compressor.parent(), compressor.self(), expressions[0]);
|
||||
if (!(self instanceof AST_Sequence)) self = self.optimize(compressor);
|
||||
return self;
|
||||
}
|
||||
@@ -4959,14 +5087,12 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
|
||||
AST_Unary.DEFMETHOD("lift_sequences", function(compressor){
|
||||
if (compressor.option("sequences")) {
|
||||
if (this.expression instanceof AST_Sequence) {
|
||||
var x = this.expression.expressions.slice();
|
||||
var e = this.clone();
|
||||
e.expression = x.pop();
|
||||
x.push(e);
|
||||
return make_sequence(this, x).optimize(compressor);
|
||||
}
|
||||
if (compressor.option("sequences") && this.expression instanceof AST_Sequence) {
|
||||
var x = this.expression.expressions.slice();
|
||||
var e = this.clone();
|
||||
e.expression = x.pop();
|
||||
x.push(e);
|
||||
return make_sequence(this, x).optimize(compressor);
|
||||
}
|
||||
return this;
|
||||
});
|
||||
@@ -5248,7 +5374,7 @@ merge(Compressor.prototype, {
|
||||
var ll = fuzzy_eval(self.left);
|
||||
if (!ll) {
|
||||
compressor.warn("Condition left of && always false [{file}:{line},{col}]", self.start);
|
||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
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);
|
||||
return make_sequence(self, [ self.left, self.right ]).optimize(compressor);
|
||||
@@ -5287,7 +5413,7 @@ merge(Compressor.prototype, {
|
||||
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);
|
||||
return maintain_this_binding(compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
return maintain_this_binding(compressor, compressor.parent(), compressor.self(), self.left).optimize(compressor);
|
||||
}
|
||||
var rr = self.right.evaluate(compressor);
|
||||
if (!rr) {
|
||||
@@ -6163,7 +6289,7 @@ merge(Compressor.prototype, {
|
||||
return self;
|
||||
});
|
||||
|
||||
AST_Lambda.DEFMETHOD("contains_this", function() {
|
||||
AST_Scope.DEFMETHOD("contains_this", function() {
|
||||
var result;
|
||||
var self = this;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
|
||||
19
lib/scope.js
19
lib/scope.js
@@ -63,12 +63,12 @@ SymbolDef.prototype = {
|
||||
unmangleable: function(options) {
|
||||
if (!options) options = {};
|
||||
|
||||
return (this.global && !options.toplevel)
|
||||
return this.global && !options.toplevel
|
||||
|| this.undeclared
|
||||
|| (!options.eval && (this.scope.uses_eval || this.scope.uses_with))
|
||||
|| (options.keep_fnames
|
||||
|| !options.eval && this.scope.pinned()
|
||||
|| options.keep_fnames
|
||||
&& (this.orig[0] instanceof AST_SymbolLambda
|
||||
|| this.orig[0] instanceof AST_SymbolDefun));
|
||||
|| this.orig[0] instanceof AST_SymbolDefun);
|
||||
},
|
||||
mangle: function(options) {
|
||||
var cache = options.cache && options.cache.props;
|
||||
@@ -355,7 +355,7 @@ function next_mangled_name(scope, options, def) {
|
||||
return name;
|
||||
}
|
||||
|
||||
AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||
AST_Symbol.DEFMETHOD("unmangleable", function(options) {
|
||||
var def = this.definition();
|
||||
return !def || def.unmangleable(options);
|
||||
});
|
||||
@@ -363,16 +363,15 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options){
|
||||
// labels are always mangleable
|
||||
AST_Label.DEFMETHOD("unmangleable", return_false);
|
||||
|
||||
AST_Symbol.DEFMETHOD("unreferenced", function(){
|
||||
return this.definition().references.length == 0
|
||||
&& !(this.scope.uses_eval || this.scope.uses_with);
|
||||
AST_Symbol.DEFMETHOD("unreferenced", function() {
|
||||
return !this.definition().references.length && !this.scope.pinned();
|
||||
});
|
||||
|
||||
AST_Symbol.DEFMETHOD("definition", function(){
|
||||
AST_Symbol.DEFMETHOD("definition", function() {
|
||||
return this.thedef;
|
||||
});
|
||||
|
||||
AST_Symbol.DEFMETHOD("global", function(){
|
||||
AST_Symbol.DEFMETHOD("global", function() {
|
||||
return this.definition().global;
|
||||
});
|
||||
|
||||
|
||||
@@ -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.3.23",
|
||||
"version": "3.3.28",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
@@ -28,7 +28,6 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"acorn": "~5.5.3",
|
||||
"mocha": "~3.5.1",
|
||||
"semver": "~5.5.0"
|
||||
},
|
||||
"scripts": {
|
||||
|
||||
@@ -4056,6 +4056,36 @@ replace_all_var: {
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
replace_all_var_scope: {
|
||||
rename = true;
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
mangle = {}
|
||||
input: {
|
||||
var a = 100, b = 10;
|
||||
(function(r, a) {
|
||||
switch (~a) {
|
||||
case (b += a):
|
||||
case a++:
|
||||
}
|
||||
})(--b, a);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
var a = 100, b = 10;
|
||||
(function(c, o) {
|
||||
switch (~a) {
|
||||
case (b += a):
|
||||
case o++:
|
||||
}
|
||||
})(--b, a);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: "100 109"
|
||||
}
|
||||
|
||||
cascade_statement: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
@@ -4901,6 +4931,27 @@ collapse_rhs_lhs_2: {
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_loop: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var s;
|
||||
s = "<tpl>PASS</tpl>";
|
||||
for (var m, r = /<tpl>(.*)<\/tpl>/; m = s.match(r);)
|
||||
s = s.replace(m[0], m[1]);
|
||||
console.log(s);
|
||||
}
|
||||
expect: {
|
||||
var s;
|
||||
s = "<tpl>PASS</tpl>";
|
||||
for (var m, r = /<tpl>(.*)<\/tpl>/; m = s.match(r);)
|
||||
s = s.replace(m[0], m[1]);
|
||||
console.log(s);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
collapse_rhs_side_effects: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
|
||||
@@ -1814,3 +1814,115 @@ issue_2995: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3146_1: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(f) {
|
||||
f("g()");
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
function g(b) {
|
||||
if (!b) b = "PASS";
|
||||
console.log(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
(function(f) {
|
||||
f("g()");
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
function g(b) {
|
||||
if (!b) b = "PASS";
|
||||
console.log(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3146_2: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function(f) {
|
||||
f("g()");
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
function g(b) {
|
||||
if (!b) b = "PASS";
|
||||
console.log(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
(function(f) {
|
||||
f("g()");
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
function g(b) {
|
||||
if (!b) b = "PASS";
|
||||
console.log(b);
|
||||
}
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3146_3: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var g = "PASS";
|
||||
(function(f) {
|
||||
var g = "FAIL";
|
||||
f("console.log(g)", g[g]);
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
var g = "PASS";
|
||||
(function(f) {
|
||||
var g = "FAIL";
|
||||
f("console.log(g)", g[g]);
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3146_4: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var g = "PASS";
|
||||
(function(f) {
|
||||
var g = "FAIL";
|
||||
f("console.log(g)", g[g]);
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
var g = "PASS";
|
||||
(function(f) {
|
||||
var g = "FAIL";
|
||||
f("console.log(g)", g[g]);
|
||||
})(function(a) {
|
||||
eval(a);
|
||||
});
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -2303,3 +2303,19 @@ issue_3076: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3125: {
|
||||
options = {
|
||||
inline: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
return "PASS";
|
||||
}.call());
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
@@ -1,11 +1,31 @@
|
||||
remove_redundant_sequence_items: {
|
||||
options = { side_effects: true };
|
||||
remove_sequence: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
(0, 1, eval)();
|
||||
(0, 1, logThis)();
|
||||
(0, 1, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
eval();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
}
|
||||
}
|
||||
|
||||
remove_redundant_sequence_items: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
(0, 1, eval)();
|
||||
(0, 1, logThis)();
|
||||
(0, 1, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
(0, eval)();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
@@ -13,13 +33,17 @@ remove_redundant_sequence_items: {
|
||||
}
|
||||
|
||||
dont_remove_this_binding_sequence: {
|
||||
options = { side_effects: true };
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
(0, eval)();
|
||||
(0, logThis)();
|
||||
(0, _decorators.logThis)();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
(0, eval)();
|
||||
logThis();
|
||||
(0, _decorators.logThis)();
|
||||
|
||||
@@ -3,8 +3,9 @@ this_binding_conditionals: {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
side_effects: true,
|
||||
};
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
(1 && a)();
|
||||
(0 || a)();
|
||||
(0 || 1 && a)();
|
||||
@@ -26,6 +27,7 @@ this_binding_conditionals: {
|
||||
(1 ? eval : 0)();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
a();
|
||||
a();
|
||||
a();
|
||||
@@ -53,13 +55,15 @@ this_binding_collapse_vars: {
|
||||
collapse_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
};
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
var c = a; c();
|
||||
var d = a.b; d();
|
||||
var e = eval; e();
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
a();
|
||||
(0, a.b)();
|
||||
(0, eval)();
|
||||
@@ -69,31 +73,88 @@ this_binding_collapse_vars: {
|
||||
this_binding_side_effects: {
|
||||
options = {
|
||||
side_effects : true
|
||||
};
|
||||
}
|
||||
input: {
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
"use strict";
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
(function(foo) {
|
||||
var eval = console;
|
||||
(0, foo)();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
eval("console.log(foo);");
|
||||
}());
|
||||
(function (foo) {
|
||||
(function(foo) {
|
||||
"use strict";
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)("console.log(foo);");
|
||||
}());
|
||||
(function(foo) {
|
||||
var eval = console;
|
||||
foo();
|
||||
(0, foo.bar)();
|
||||
(0, eval)('console.log(foo);');
|
||||
eval("console.log(foo);");
|
||||
}());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this_binding_sequences: {
|
||||
options = {
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
console.log(typeof function() {
|
||||
return eval("this");
|
||||
}());
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return eval("this");
|
||||
}());
|
||||
console.log(typeof function() {
|
||||
return (0, eval)("this");
|
||||
}());
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return (0, eval)("this");
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(typeof function() {
|
||||
return eval("this");
|
||||
}()),
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return eval("this");
|
||||
}()),
|
||||
console.log(typeof function() {
|
||||
return eval("this");
|
||||
}()),
|
||||
console.log(typeof function() {
|
||||
"use strict";
|
||||
return (0, eval)("this");
|
||||
}());
|
||||
}
|
||||
expect_stdout: [
|
||||
"object",
|
||||
"undefined",
|
||||
"object",
|
||||
"object",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -1476,18 +1476,18 @@ defun_redefine: {
|
||||
};
|
||||
return g() + h();
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
function g() {
|
||||
return 1;
|
||||
}
|
||||
g = function() {
|
||||
(function() {
|
||||
return 3;
|
||||
};
|
||||
return g() + 2;
|
||||
});
|
||||
return 3 + 2;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect_stdout: "5"
|
||||
}
|
||||
|
||||
func_inline: {
|
||||
@@ -1527,23 +1527,37 @@ func_modified: {
|
||||
}
|
||||
input: {
|
||||
function f(a) {
|
||||
function a() { return 1; }
|
||||
function b() { return 2; }
|
||||
function c() { return 3; }
|
||||
function a() {
|
||||
return 1;
|
||||
}
|
||||
function b() {
|
||||
return 2;
|
||||
}
|
||||
function c() {
|
||||
return 3;
|
||||
}
|
||||
b.inject = [];
|
||||
c = function() { return 4; };
|
||||
c = function() {
|
||||
return 4;
|
||||
};
|
||||
return a() + b() + c();
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect: {
|
||||
function f(a) {
|
||||
function b() { return 2; }
|
||||
function c() { return 3; }
|
||||
function b() {
|
||||
return 2;
|
||||
}
|
||||
b.inject = [];
|
||||
c = function() { return 4; };
|
||||
return 1 + 2 + c();
|
||||
(function() {
|
||||
return 4;
|
||||
});
|
||||
return 1 + 2 + 4;
|
||||
}
|
||||
console.log(f());
|
||||
}
|
||||
expect_stdout: "7"
|
||||
}
|
||||
|
||||
defun_label: {
|
||||
@@ -5054,9 +5068,7 @@ defun_var_1: {
|
||||
console.log(typeof a, typeof b);
|
||||
}
|
||||
expect: {
|
||||
var a = 42;
|
||||
function a() {}
|
||||
console.log(typeof a, "function");
|
||||
console.log("number", "function");
|
||||
}
|
||||
expect_stdout: "number function"
|
||||
}
|
||||
@@ -5076,9 +5088,7 @@ defun_var_2: {
|
||||
console.log(typeof a, typeof b);
|
||||
}
|
||||
expect: {
|
||||
function a() {}
|
||||
var a = 42;
|
||||
console.log(typeof a, "function");
|
||||
console.log("number", "function");
|
||||
}
|
||||
expect_stdout: "number function"
|
||||
}
|
||||
@@ -5710,3 +5720,686 @@ issue_3068_2: {
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_3110_1: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 3,
|
||||
properties: true,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
function foo() {
|
||||
return isDev ? "foo" : "bar";
|
||||
}
|
||||
var isDev = true;
|
||||
var obj = {
|
||||
foo: foo
|
||||
};
|
||||
console.log(foo());
|
||||
console.log(obj.foo());
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
console.log("foo"),
|
||||
console.log("foo");
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"foo",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3110_2: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 4,
|
||||
properties: true,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
function foo() {
|
||||
return isDev ? "foo" : "bar";
|
||||
}
|
||||
var isDev = true;
|
||||
console.log(foo());
|
||||
var obj = {
|
||||
foo: foo
|
||||
};
|
||||
console.log(obj.foo());
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
console.log("foo"),
|
||||
console.log("foo");
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"foo",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3110_3: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
properties: true,
|
||||
reduce_vars: true,
|
||||
sequences: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
function foo() {
|
||||
return isDev ? "foo" : "bar";
|
||||
}
|
||||
console.log(foo());
|
||||
var isDev = true;
|
||||
var obj = {
|
||||
foo: foo
|
||||
};
|
||||
console.log(obj.foo());
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
function foo() {
|
||||
return isDev ? "foo" : "bar";
|
||||
}
|
||||
console.log(foo());
|
||||
var isDev = true;
|
||||
var obj = {
|
||||
foo: foo
|
||||
};
|
||||
console.log(obj.foo());
|
||||
})();
|
||||
}
|
||||
expect_stdout: [
|
||||
"bar",
|
||||
"foo",
|
||||
]
|
||||
}
|
||||
|
||||
issue_3113_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
(function() {
|
||||
function f() {
|
||||
while (g());
|
||||
}
|
||||
var a = f();
|
||||
function g() {
|
||||
a && a[c++];
|
||||
}
|
||||
g(a = 1);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
(function() {
|
||||
function f() {
|
||||
while (g());
|
||||
}
|
||||
var a = f();
|
||||
function g() {
|
||||
a && a[c++];
|
||||
}
|
||||
g(a = 1);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_3113_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
(function() {
|
||||
function f() {
|
||||
while (g());
|
||||
}
|
||||
var a = f();
|
||||
function g() {
|
||||
a && a[c++];
|
||||
}
|
||||
a = 1;
|
||||
g();
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
(function() {
|
||||
function f() {
|
||||
while (g());
|
||||
}
|
||||
var a = f();
|
||||
function g() {
|
||||
a && a[c++];
|
||||
}
|
||||
a = 1;
|
||||
g();
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_3113_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
pure_getters: "strict",
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
(function() {
|
||||
function f() {
|
||||
while (g());
|
||||
}
|
||||
var a;
|
||||
function g() {
|
||||
a && a[c++];
|
||||
}
|
||||
g(a = 1);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
c++;
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
issue_3113_4: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
var a = 0, b = 0;
|
||||
function f() {
|
||||
b += a;
|
||||
}
|
||||
f(f(), ++a);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect: {
|
||||
var a = 0, b = 0;
|
||||
function f() {
|
||||
b += a;
|
||||
}
|
||||
f(f(), ++a);
|
||||
console.log(a, b);
|
||||
}
|
||||
expect_stdout: "1 1"
|
||||
}
|
||||
|
||||
issue_3113_5: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
console.log(a);
|
||||
}
|
||||
function g() {
|
||||
f();
|
||||
}
|
||||
while (g());
|
||||
var a = 1;
|
||||
f();
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
console.log(a);
|
||||
}
|
||||
function g() {
|
||||
f();
|
||||
}
|
||||
while (g());
|
||||
var a = 1;
|
||||
f();
|
||||
}
|
||||
expect_stdout: [
|
||||
"undefined",
|
||||
"1",
|
||||
]
|
||||
}
|
||||
|
||||
conditional_nested_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 1, b = 0;
|
||||
(function f(c) {
|
||||
function g() {
|
||||
c && (c.a = 0);
|
||||
c && (c.a = 0);
|
||||
c && (c[b++] *= 0);
|
||||
}
|
||||
g(a-- && f(g(c = 42)));
|
||||
})();
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
var a = 1, b = 0;
|
||||
(function f(c) {
|
||||
function g() {
|
||||
c && (c.a = 0);
|
||||
c && (c.a = 0);
|
||||
c && (c[b++] *= 0);
|
||||
}
|
||||
g(a-- && f(g(c = 42)));
|
||||
})();
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
conditional_nested_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var c = 0;
|
||||
(function(a) {
|
||||
function f() {
|
||||
a && c++;
|
||||
}
|
||||
f(!c && f(), a = 1);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = 0;
|
||||
(function(a) {
|
||||
function f() {
|
||||
a && c++;
|
||||
}
|
||||
f(!c && f(), a = 1);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
conditional_nested_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var n = 2, c = 0;
|
||||
(function f(a) {
|
||||
0 < n-- && g(a = 1);
|
||||
function g() {
|
||||
a && c++;
|
||||
}
|
||||
g();
|
||||
0 < n-- && f();
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var n = 2, c = 0;
|
||||
(function f(a) {
|
||||
0 < n-- && g(a = 1);
|
||||
function g() {
|
||||
a && c++;
|
||||
}
|
||||
g();
|
||||
0 < n-- && f();
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "2"
|
||||
}
|
||||
|
||||
issue_2436: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
var c;
|
||||
console.log(((c = {
|
||||
a: 1,
|
||||
b: 2
|
||||
}).a = 3, {
|
||||
x: c.a,
|
||||
y: c.b
|
||||
}));
|
||||
}
|
||||
expect: {
|
||||
var c;
|
||||
console.log(((c = {
|
||||
a: 1,
|
||||
b: 2
|
||||
}).a = 3, {
|
||||
x: c.a,
|
||||
y: c.b
|
||||
}));
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_2916: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
evaluate: true,
|
||||
inline: true,
|
||||
passes: 2,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unsafe: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var c = "FAIL";
|
||||
(function(b) {
|
||||
(function(d) {
|
||||
d[0] = 1;
|
||||
})(b);
|
||||
+b && (c = "PASS");
|
||||
})([]);
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var c = "FAIL";
|
||||
(function(b) {
|
||||
b[0] = 1;
|
||||
+b && (c = "PASS");
|
||||
})([]);
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3125: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
var o;
|
||||
console.log((function() {
|
||||
this.p++;
|
||||
}.call(o = {
|
||||
p: 6
|
||||
}), o.p));
|
||||
}
|
||||
expect: {
|
||||
var o;
|
||||
console.log((function() {
|
||||
this.p++;
|
||||
}.call(o = {
|
||||
p: 6
|
||||
}), o.p));
|
||||
}
|
||||
expect_stdout: "7"
|
||||
}
|
||||
|
||||
issue_3140_1: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
this();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
this();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3140_2: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var self = this;
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
self();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
this();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3140_3: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var self = this;
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
(function() {
|
||||
return self;
|
||||
})()();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var self = this;
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
(function() {
|
||||
return self;
|
||||
})()();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3140_4: {
|
||||
options = {
|
||||
reduce_vars: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var o = {
|
||||
p: this
|
||||
};
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
o.p();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect: {
|
||||
(function() {
|
||||
var a;
|
||||
function f() {
|
||||
}
|
||||
f.g = function g() {
|
||||
var o = {
|
||||
p: this
|
||||
};
|
||||
function h() {
|
||||
console.log(a ? "PASS" : "FAIL");
|
||||
}
|
||||
a = true;
|
||||
o.p();
|
||||
a = false;
|
||||
h.g = g;
|
||||
return h;
|
||||
};
|
||||
return f;
|
||||
})().g().g();
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_3140_5: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
reduce_vars: true,
|
||||
}
|
||||
input: {
|
||||
var n = 1, c = 0;
|
||||
(function(a) {
|
||||
var b = function() {
|
||||
this;
|
||||
n-- && h();
|
||||
}();
|
||||
function h() {
|
||||
b && c++;
|
||||
}
|
||||
h(b = 1);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect: {
|
||||
var n = 1, c = 0;
|
||||
(function(a) {
|
||||
var b = function() {
|
||||
this;
|
||||
n-- && h();
|
||||
}();
|
||||
function h() {
|
||||
b && c++;
|
||||
}
|
||||
h(b = 1);
|
||||
})();
|
||||
console.log(c);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
37
test/compress/regexp.js
Normal file
37
test/compress/regexp.js
Normal file
@@ -0,0 +1,37 @@
|
||||
regexp_simple: {
|
||||
input: {
|
||||
/rx/ig
|
||||
}
|
||||
expect_exact: "/rx/gi;"
|
||||
}
|
||||
|
||||
regexp_slashes: {
|
||||
input: {
|
||||
/\\\/rx\/\\/ig
|
||||
}
|
||||
expect_exact: "/\\\\\\/rx\\/\\\\/gi;"
|
||||
}
|
||||
|
||||
regexp_1: {
|
||||
input: {
|
||||
console.log(JSON.stringify("COMPASS? Overpass.".match(/([Sap]+)/ig)));
|
||||
}
|
||||
expect: {
|
||||
console.log(JSON.stringify("COMPASS? Overpass.".match(/([Sap]+)/gi)));
|
||||
}
|
||||
expect_stdout: '["PASS","pass"]'
|
||||
}
|
||||
|
||||
regexp_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
console.log(JSON.stringify("COMPASS? Overpass.".match(new RegExp("([Sap]+)", "ig"))));
|
||||
}
|
||||
expect: {
|
||||
console.log(JSON.stringify("COMPASS? Overpass.".match(/([Sap]+)/gi)));
|
||||
}
|
||||
expect_stdout: '["PASS","pass"]'
|
||||
}
|
||||
@@ -876,3 +876,59 @@ forin: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
call: {
|
||||
options = {
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
var a = function() {
|
||||
return this;
|
||||
}();
|
||||
function b() {
|
||||
console.log("foo");
|
||||
}
|
||||
b.c = function() {
|
||||
console.log(this === b ? "bar" : "baz");
|
||||
};
|
||||
(a, b)();
|
||||
(a, b.c)();
|
||||
(a, function() {
|
||||
console.log(this === a);
|
||||
})();
|
||||
new (a, b)();
|
||||
new (a, b.c)();
|
||||
new (a, function() {
|
||||
console.log(this === a);
|
||||
})();
|
||||
}
|
||||
expect: {
|
||||
var a = function() {
|
||||
return this;
|
||||
}();
|
||||
function b() {
|
||||
console.log("foo");
|
||||
}
|
||||
b.c = function() {
|
||||
console.log(this === b ? "bar" : "baz");
|
||||
},
|
||||
a, b(),
|
||||
(a, b.c)(),
|
||||
a, function() {
|
||||
console.log(this === a);
|
||||
}(),
|
||||
a, new b(),
|
||||
a, new b.c(),
|
||||
a, new function() {
|
||||
console.log(this === a);
|
||||
}();
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"baz",
|
||||
"true",
|
||||
"foo",
|
||||
"baz",
|
||||
"false",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -14,6 +14,13 @@ issue_1929: {
|
||||
function f(s) {
|
||||
return s.split(/[\\/]/);
|
||||
}
|
||||
console.log(JSON.stringify(f("A/B\\C\\D/E\\F")));
|
||||
}
|
||||
expect_exact: "function f(s){return s.split(/[\\\\/]/)}"
|
||||
expect: {
|
||||
function f(s) {
|
||||
return s.split(/[\\/]/);
|
||||
}
|
||||
console.log(JSON.stringify(f("A/B\\C\\D/E\\F")));
|
||||
}
|
||||
expect_stdout: '["A","B","C","D","E","F"]'
|
||||
}
|
||||
|
||||
@@ -90,17 +90,11 @@ typeof_defun_1: {
|
||||
"function" == typeof h && h();
|
||||
}
|
||||
expect: {
|
||||
function g() {
|
||||
h = 42;
|
||||
console.log("NOPE");
|
||||
}
|
||||
function h() {
|
||||
console.log("YUP");
|
||||
}
|
||||
g = 42;
|
||||
console.log("YES");
|
||||
"function" == typeof g && g();
|
||||
"function" == typeof h && h();
|
||||
h();
|
||||
}
|
||||
expect_stdout: [
|
||||
"YES",
|
||||
|
||||
117
test/mocha.js
117
test/mocha.js
@@ -1,24 +1,109 @@
|
||||
var fs = require("fs");
|
||||
var Mocha = require("mocha");
|
||||
var path = require("path");
|
||||
|
||||
// Instantiate a Mocha instance
|
||||
var mocha = new Mocha({
|
||||
timeout: 5000
|
||||
});
|
||||
var testDir = __dirname + "/mocha/";
|
||||
var config = {
|
||||
limit: 5000,
|
||||
timeout: function(limit) {
|
||||
this.limit = limit;
|
||||
}
|
||||
};
|
||||
var tasks = [];
|
||||
var titles = [];
|
||||
describe = function(title, fn) {
|
||||
config = Object.create(config);
|
||||
titles.push(title);
|
||||
fn.call(config);
|
||||
titles.pop();
|
||||
config = Object.getPrototypeOf(config);
|
||||
};
|
||||
it = function(title, fn) {
|
||||
fn.limit = config.limit;
|
||||
fn.titles = titles.slice();
|
||||
fn.titles.push(title);
|
||||
tasks.push(fn);
|
||||
};
|
||||
|
||||
// Add each .js file to the Mocha instance
|
||||
fs.readdirSync(testDir).filter(function(file) {
|
||||
fs.readdirSync("test/mocha").filter(function(file) {
|
||||
return /\.js$/.test(file);
|
||||
}).forEach(function(file) {
|
||||
mocha.addFile(path.join(testDir, file));
|
||||
require("./mocha/" + file);
|
||||
});
|
||||
|
||||
module.exports = function() {
|
||||
mocha.run(function(failures) {
|
||||
if (failures) process.on("exit", function() {
|
||||
process.exit(failures);
|
||||
function log_titles(log, current, marker) {
|
||||
var indent = "";
|
||||
var writing = false;
|
||||
for (var i = 0; i < current.length; i++, indent += " ") {
|
||||
if (titles[i] != current[i]) writing = true;
|
||||
if (writing) log(indent + (i == current.length - 1 && marker || "") + current[i]);
|
||||
}
|
||||
titles = current;
|
||||
}
|
||||
|
||||
function red(text) {
|
||||
return "\u001B[31m" + text + "\u001B[39m";
|
||||
}
|
||||
|
||||
function green(text) {
|
||||
return "\u001B[32m" + text + "\u001B[39m";
|
||||
}
|
||||
|
||||
var errors = [];
|
||||
var total = tasks.length;
|
||||
titles = [];
|
||||
process.nextTick(function run() {
|
||||
var task = tasks.shift();
|
||||
if (task) try {
|
||||
var elapsed = Date.now();
|
||||
var timer;
|
||||
var done = function() {
|
||||
clearTimeout(timer);
|
||||
done = function() {};
|
||||
elapsed = Date.now() - elapsed;
|
||||
if (elapsed > task.limit) {
|
||||
throw new Error("Timed out: " + elapsed + "ms > " + task.limit + "ms");
|
||||
}
|
||||
log_titles(console.log, task.titles, green('\u221A '));
|
||||
process.nextTick(run);
|
||||
};
|
||||
if (task.length) {
|
||||
task.timeout = function(limit) {
|
||||
clearTimeout(timer);
|
||||
task.limit = limit;
|
||||
timer = setTimeout(function() {
|
||||
raise(new Error("Timed out: exceeds " + limit + "ms"));
|
||||
}, limit);
|
||||
};
|
||||
task.timeout(task.limit);
|
||||
task.call(task, done);
|
||||
} else {
|
||||
task.timeout = config.timeout;
|
||||
task.call(task);
|
||||
done();
|
||||
}
|
||||
} catch (err) {
|
||||
raise(err);
|
||||
} else if (errors.length) {
|
||||
console.error();
|
||||
console.log(red(errors.length + " test(s) failed!"));
|
||||
titles = [];
|
||||
errors.forEach(function(titles, index) {
|
||||
console.error();
|
||||
log_titles(console.error, titles, (index + 1) + ") ");
|
||||
var lines = titles.error.stack.split('\n');
|
||||
console.error(red(lines[0]));
|
||||
console.error(lines.slice(1).join("\n"));
|
||||
});
|
||||
});
|
||||
};
|
||||
process.exit(1);
|
||||
} else {
|
||||
console.log();
|
||||
console.log(green(total + " test(s) passed."));
|
||||
}
|
||||
|
||||
function raise(err) {
|
||||
clearTimeout(timer);
|
||||
done = function() {};
|
||||
task.titles.error = err;
|
||||
errors.push(task.titles);
|
||||
log_titles(console.log, task.titles, red('\u00D7 '));
|
||||
process.nextTick(run);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -57,24 +57,23 @@ describe("sourcemaps", function() {
|
||||
includeSources: true
|
||||
}
|
||||
});
|
||||
|
||||
if (result.error) throw result.error;
|
||||
var map = JSON.parse(result.map);
|
||||
|
||||
assert.equal(map.file, 'simple.min.js');
|
||||
assert.equal(map.file, "simple.min.js");
|
||||
assert.equal(map.sourcesContent.length, 1);
|
||||
assert.equal(map.sourcesContent[0],
|
||||
'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
|
||||
assert.equal(map.sourcesContent[0], 'let foo = x => "foo " + x;\nconsole.log(foo("bar"));');
|
||||
});
|
||||
it("Should process inline source map", function() {
|
||||
var code = Uglify.minify(read("./test/input/issue-520/input.js"), {
|
||||
var result = Uglify.minify(read("./test/input/issue-520/input.js"), {
|
||||
compress: { toplevel: true },
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
includeSources: true,
|
||||
url: "inline"
|
||||
}
|
||||
}).code + "\n";
|
||||
assert.strictEqual(code, readFileSync("test/input/issue-520/output.js", "utf8"));
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
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 = Uglify.AST_Node.warn_function;
|
||||
@@ -149,22 +148,24 @@ describe("sourcemaps", function() {
|
||||
});
|
||||
|
||||
describe("sourceMapInline", function() {
|
||||
it("should append source map to output js when sourceMapInline is enabled", function() {
|
||||
it("Should append source map to output js when sourceMapInline is enabled", function() {
|
||||
var result = Uglify.minify('var a = function(foo) { return foo; };', {
|
||||
sourceMap: {
|
||||
url: "inline"
|
||||
}
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
var code = result.code;
|
||||
assert.strictEqual(code, "var a=function(n){return n};\n" +
|
||||
"//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIjAiXSwibmFtZXMiOlsiYSIsImZvbyJdLCJtYXBwaW5ncyI6IkFBQUEsSUFBSUEsRUFBSSxTQUFTQyxHQUFPLE9BQU9BIn0=");
|
||||
});
|
||||
it("should not append source map to output js when sourceMapInline is not enabled", function() {
|
||||
it("Should not append source map to output js when sourceMapInline is not enabled", function() {
|
||||
var result = Uglify.minify('var a = function(foo) { return foo; };');
|
||||
if (result.error) throw result.error;
|
||||
var code = result.code;
|
||||
assert.strictEqual(code, "var a=function(n){return n};");
|
||||
});
|
||||
it("should work with max_line_len", function() {
|
||||
it("Should work with max_line_len", function() {
|
||||
var result = Uglify.minify(read("./test/input/issue-505/input.js"), {
|
||||
output: {
|
||||
max_line_len: 20
|
||||
@@ -173,8 +174,39 @@ describe("sourcemaps", function() {
|
||||
url: "inline"
|
||||
}
|
||||
});
|
||||
assert.strictEqual(result.error, undefined);
|
||||
if (result.error) throw result.error;
|
||||
assert.strictEqual(result.code, read("./test/input/issue-505/output.js"));
|
||||
});
|
||||
it("Should work with unicode characters", function() {
|
||||
var code = [
|
||||
"var tëst = '→unicøde←';",
|
||||
"alert(tëst);",
|
||||
].join("\n");
|
||||
var result = Uglify.minify(code, {
|
||||
sourceMap: {
|
||||
includeSources: true,
|
||||
url: "inline",
|
||||
}
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
var map = JSON.parse(result.map);
|
||||
assert.strictEqual(map.sourcesContent.length, 1);
|
||||
assert.strictEqual(map.sourcesContent[0], code);
|
||||
var encoded = result.code.slice(result.code.lastIndexOf(",") + 1);
|
||||
map = JSON.parse(new Buffer(encoded, "base64").toString());
|
||||
assert.strictEqual(map.sourcesContent.length, 1);
|
||||
assert.strictEqual(map.sourcesContent[0], code);
|
||||
result = Uglify.minify(result.code, {
|
||||
sourceMap: {
|
||||
content: "inline",
|
||||
includeSources: true,
|
||||
}
|
||||
});
|
||||
if (result.error) throw result.error;
|
||||
map = JSON.parse(result.map);
|
||||
assert.strictEqual(map.names.length, 2);
|
||||
assert.strictEqual(map.names[0], "tëst");
|
||||
assert.strictEqual(map.names[1], "alert");
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -18,9 +18,8 @@ if (failures) {
|
||||
console.error("!!! " + Object.keys(failed_files).join(", "));
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var mocha_tests = require("./mocha.js");
|
||||
mocha_tests();
|
||||
console.log();
|
||||
require("./mocha.js");
|
||||
|
||||
/* -----[ utils ]----- */
|
||||
|
||||
|
||||
@@ -18,6 +18,13 @@
|
||||
{
|
||||
"toplevel": true
|
||||
},
|
||||
{
|
||||
"compress": {
|
||||
"passes": 1e6,
|
||||
"unsafe": true
|
||||
},
|
||||
"toplevel": true
|
||||
},
|
||||
{
|
||||
"compress": {
|
||||
"keep_fargs": false,
|
||||
|
||||
Reference in New Issue
Block a user