Compare commits
22 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
30761eede5 | ||
|
|
fb30aeccaf | ||
|
|
226aa1f76b | ||
|
|
6e235602fb | ||
|
|
980fcbb56b | ||
|
|
375ebe316d | ||
|
|
2500930234 | ||
|
|
2f0da2ff05 | ||
|
|
83a3cbf151 | ||
|
|
da8d154571 | ||
|
|
e33c727e8b | ||
|
|
f886b3fb2b | ||
|
|
b1cc15e85b | ||
|
|
3aa765e429 | ||
|
|
93d084a1d1 | ||
|
|
c7a3e09407 | ||
|
|
09525c7530 | ||
|
|
a7e15fe73c | ||
|
|
a31c27c7cf | ||
|
|
1caf7c7bd2 | ||
|
|
0eb0c9b388 | ||
|
|
7dc61cdc89 |
19
lib/ast.js
19
lib/ast.js
@@ -288,10 +288,13 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
|
|||||||
var label = node.label;
|
var label = node.label;
|
||||||
var def = this.label;
|
var def = this.label;
|
||||||
node.walk(new TreeWalker(function(node) {
|
node.walk(new TreeWalker(function(node) {
|
||||||
if (node instanceof AST_LoopControl && node.label && node.label.thedef === def) {
|
if (node instanceof AST_LoopControl) {
|
||||||
|
if (!node.label || node.label.thedef !== def) return;
|
||||||
node.label.thedef = label;
|
node.label.thedef = label;
|
||||||
label.references.push(node);
|
label.references.push(node);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Scope) return true;
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
return node;
|
return node;
|
||||||
@@ -409,16 +412,16 @@ var AST_With = DEFNODE("With", "expression", {
|
|||||||
|
|
||||||
/* -----[ scope and functions ]----- */
|
/* -----[ scope and functions ]----- */
|
||||||
|
|
||||||
var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent_scope enclosed cname", {
|
var AST_Scope = DEFNODE("Scope", "cname enclosed uses_eval uses_with parent_scope functions variables make_def", {
|
||||||
$documentation: "Base class for all statements introducing a lexical scope",
|
$documentation: "Base class for all statements introducing a lexical scope",
|
||||||
$propdoc: {
|
$propdoc: {
|
||||||
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
|
||||||
functions: "[Object/S] like `variables`, but only lists function declarations",
|
|
||||||
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
|
||||||
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
|
||||||
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
|
||||||
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
|
||||||
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
cname: "[integer/S] current index for mangling variables (used internally by the mangler)",
|
||||||
|
enclosed: "[SymbolDef*/S] a list of all symbol definitions that are accessed from this scope or any subscopes",
|
||||||
|
uses_eval: "[boolean/S] tells whether this scope contains a direct call to the global `eval`",
|
||||||
|
uses_with: "[boolean/S] tells whether this scope uses the `with` statement",
|
||||||
|
parent_scope: "[AST_Scope?/S] link to the parent scope",
|
||||||
|
functions: "[Object/S] like `variables`, but only lists function declarations",
|
||||||
|
variables: "[Object/S] a map of name -> SymbolDef for all variables/functions defined in this scope",
|
||||||
},
|
},
|
||||||
clone: function(deep) {
|
clone: function(deep) {
|
||||||
var node = this._clone(deep);
|
var node = this._clone(deep);
|
||||||
|
|||||||
333
lib/compress.js
333
lib/compress.js
@@ -325,22 +325,22 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
var lhs = is_lhs(node, parent);
|
var lhs = is_lhs(node, parent);
|
||||||
if (lhs) return lhs;
|
if (lhs) return lhs;
|
||||||
if (!immutable
|
if (parent instanceof AST_Array) return is_modified(compressor, tw, parent, parent, level + 1);
|
||||||
&& parent instanceof AST_Call
|
if (parent instanceof AST_Call) {
|
||||||
&& parent.expression === node
|
return !immutable
|
||||||
&& !parent.is_expr_pure(compressor)
|
&& parent.expression === node
|
||||||
&& (!(value instanceof AST_Function)
|
&& !parent.is_expr_pure(compressor)
|
||||||
|| !(parent instanceof AST_New) && value.contains_this())) {
|
&& (!(value instanceof AST_Function)
|
||||||
return true;
|
|| !(parent instanceof AST_New) && value.contains_this());
|
||||||
}
|
}
|
||||||
if (parent instanceof AST_Array) {
|
if (parent instanceof AST_ForIn) return parent.init === node;
|
||||||
return is_modified(compressor, tw, parent, parent, level + 1);
|
if (parent instanceof AST_ObjectKeyVal) {
|
||||||
}
|
if (parent.value !== node) return;
|
||||||
if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
|
|
||||||
var obj = tw.parent(level + 1);
|
var obj = tw.parent(level + 1);
|
||||||
return is_modified(compressor, tw, obj, obj, level + 2);
|
return is_modified(compressor, tw, obj, obj, level + 2);
|
||||||
}
|
}
|
||||||
if (parent instanceof AST_PropAccess && parent.expression === node) {
|
if (parent instanceof AST_PropAccess) {
|
||||||
|
if (parent.expression !== node) return;
|
||||||
var prop = read_property(value, parent);
|
var prop = read_property(value, parent);
|
||||||
return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
|
return (!immutable || recursive) && is_modified(compressor, tw, parent, prop, level + 1);
|
||||||
}
|
}
|
||||||
@@ -514,33 +514,41 @@ merge(Compressor.prototype, {
|
|||||||
|| value instanceof AST_This;
|
|| value instanceof AST_This;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function has_escaped(d, node, parent) {
|
||||||
|
if (parent instanceof AST_Assign) return parent.operator == "=" && parent.right === node;
|
||||||
|
if (parent instanceof AST_Call) return parent.expression !== node || parent instanceof AST_New;
|
||||||
|
if (parent instanceof AST_Exit) return parent.value === node && node.scope !== d.scope;
|
||||||
|
if (parent instanceof AST_VarDef) return parent.value === node;
|
||||||
|
}
|
||||||
|
|
||||||
|
function value_in_use(node, parent) {
|
||||||
|
if (parent instanceof AST_Array) return true;
|
||||||
|
if (parent instanceof AST_Binary) return lazy_op[parent.operator];
|
||||||
|
if (parent instanceof AST_Conditional) return parent.condition !== node;
|
||||||
|
if (parent instanceof AST_Sequence) return parent.tail_node() === node;
|
||||||
|
}
|
||||||
|
|
||||||
function mark_escaped(tw, d, scope, node, value, level, depth) {
|
function mark_escaped(tw, d, scope, node, value, level, depth) {
|
||||||
var parent = tw.parent(level);
|
var parent = tw.parent(level);
|
||||||
if (value && value.is_constant()) return;
|
if (value && value.is_constant()) return;
|
||||||
if (parent instanceof AST_Assign && parent.operator == "=" && node === parent.right
|
if (has_escaped(d, node, parent)) {
|
||||||
|| parent instanceof AST_Call && (node !== parent.expression || parent instanceof AST_New)
|
|
||||||
|| parent instanceof AST_Exit && node === parent.value && node.scope !== d.scope
|
|
||||||
|| parent instanceof AST_VarDef && node === parent.value) {
|
|
||||||
d.escaped.push(parent);
|
d.escaped.push(parent);
|
||||||
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
|
if (depth > 1 && !(value && value.is_constant_expression(scope))) depth = 1;
|
||||||
if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
|
if (!d.escaped.depth || d.escaped.depth > depth) d.escaped.depth = depth;
|
||||||
return;
|
return;
|
||||||
} else if (parent instanceof AST_Array
|
} else if (value_in_use(node, parent)) {
|
||||||
|| parent instanceof AST_Binary && lazy_op[parent.operator]
|
|
||||||
|| parent instanceof AST_Conditional && node !== parent.condition
|
|
||||||
|| parent instanceof AST_Sequence && node === parent.tail_node()) {
|
|
||||||
mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
|
mark_escaped(tw, d, scope, parent, parent, level + 1, depth);
|
||||||
} else if (parent instanceof AST_ObjectKeyVal && node === parent.value) {
|
} else if (parent instanceof AST_ObjectKeyVal && parent.value === node) {
|
||||||
var obj = tw.parent(level + 1);
|
var obj = tw.parent(level + 1);
|
||||||
mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
|
mark_escaped(tw, d, scope, obj, obj, level + 2, depth);
|
||||||
} else if (parent instanceof AST_PropAccess && node === parent.expression) {
|
} else if (parent instanceof AST_PropAccess && parent.expression === node) {
|
||||||
value = read_property(value, parent);
|
value = read_property(value, parent);
|
||||||
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
|
mark_escaped(tw, d, scope, parent, value, level + 1, depth + 1);
|
||||||
if (value) return;
|
if (value) return;
|
||||||
}
|
}
|
||||||
if (level > 0) return;
|
if (level > 0) return;
|
||||||
if (parent instanceof AST_Call && node === parent.expression) return;
|
if (parent instanceof AST_Call && parent.expression === node) return;
|
||||||
if (parent instanceof AST_Sequence && node !== parent.tail_node()) return;
|
if (parent instanceof AST_Sequence && parent.tail_node() !== node) return;
|
||||||
if (parent instanceof AST_SimpleStatement) return;
|
if (parent instanceof AST_SimpleStatement) return;
|
||||||
if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
|
if (parent instanceof AST_Unary && !unary_side_effects[parent.operator]) return;
|
||||||
d.direct_access = true;
|
d.direct_access = true;
|
||||||
@@ -554,13 +562,6 @@ merge(Compressor.prototype, {
|
|||||||
if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
|
if (is_arguments(def) && node.property instanceof AST_Number) def.reassigned = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
var suppressor = new TreeWalker(function(node) {
|
|
||||||
if (!(node instanceof AST_Symbol)) return;
|
|
||||||
var d = node.definition();
|
|
||||||
if (!d) return;
|
|
||||||
if (node instanceof AST_SymbolRef) push_ref(d, node);
|
|
||||||
d.fixed = false;
|
|
||||||
});
|
|
||||||
def(AST_Accessor, function(tw, descend, compressor) {
|
def(AST_Accessor, function(tw, descend, compressor) {
|
||||||
push(tw);
|
push(tw);
|
||||||
reset_variables(tw, compressor, this);
|
reset_variables(tw, compressor, this);
|
||||||
@@ -740,11 +741,17 @@ merge(Compressor.prototype, {
|
|||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
def(AST_ForIn, function(tw) {
|
def(AST_ForIn, function(tw) {
|
||||||
this.init.walk(suppressor);
|
|
||||||
this.object.walk(tw);
|
this.object.walk(tw);
|
||||||
var saved_loop = tw.in_loop;
|
var saved_loop = tw.in_loop;
|
||||||
tw.in_loop = this;
|
tw.in_loop = this;
|
||||||
push(tw);
|
push(tw);
|
||||||
|
var init = this.init;
|
||||||
|
init.walk(tw);
|
||||||
|
if (init instanceof AST_SymbolRef) {
|
||||||
|
init.definition().fixed = false;
|
||||||
|
} else if (init instanceof AST_Var) {
|
||||||
|
init.definitions[0].name.definition().fixed = false;
|
||||||
|
}
|
||||||
this.body.walk(tw);
|
this.body.walk(tw);
|
||||||
pop(tw);
|
pop(tw);
|
||||||
tw.in_loop = saved_loop;
|
tw.in_loop = saved_loop;
|
||||||
@@ -1093,11 +1100,14 @@ merge(Compressor.prototype, {
|
|||||||
// func(something) because that changes the meaning of
|
// func(something) because that changes the meaning of
|
||||||
// the func (becomes lexical instead of global).
|
// the func (becomes lexical instead of global).
|
||||||
function maintain_this_binding(compressor, parent, orig, val) {
|
function maintain_this_binding(compressor, parent, orig, val) {
|
||||||
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete"
|
var wrap = false;
|
||||||
|| parent.TYPE == "Call" && parent.expression === orig && needs_unbinding(compressor, val)) {
|
if (parent.TYPE == "Call") {
|
||||||
return make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]);
|
wrap = parent.expression === orig && needs_unbinding(compressor, val);
|
||||||
|
} else if (parent instanceof AST_UnaryPrefix) {
|
||||||
|
wrap = parent.operator == "delete"
|
||||||
|
|| parent.operator == "typeof" && is_undeclared_ref(val);
|
||||||
}
|
}
|
||||||
return val;
|
return wrap ? make_sequence(orig, [ make_node(AST_Number, orig, { value: 0 }), val ]) : val;
|
||||||
}
|
}
|
||||||
|
|
||||||
function merge_sequence(array, node) {
|
function merge_sequence(array, node) {
|
||||||
@@ -1286,7 +1296,7 @@ merge(Compressor.prototype, {
|
|||||||
col: node.start.col
|
col: node.start.col
|
||||||
});
|
});
|
||||||
if (candidate instanceof AST_UnaryPostfix) {
|
if (candidate instanceof AST_UnaryPostfix) {
|
||||||
lhs.definition().fixed = false;
|
if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
|
||||||
return make_node(AST_UnaryPrefix, candidate, candidate);
|
return make_node(AST_UnaryPrefix, candidate, candidate);
|
||||||
}
|
}
|
||||||
if (candidate instanceof AST_VarDef) {
|
if (candidate instanceof AST_VarDef) {
|
||||||
@@ -2634,6 +2644,14 @@ merge(Compressor.prototype, {
|
|||||||
defs = stat.init;
|
defs = stat.init;
|
||||||
}
|
}
|
||||||
} else if (stat instanceof AST_ForIn) {
|
} else if (stat instanceof AST_ForIn) {
|
||||||
|
if (defs && defs.TYPE == stat.init.TYPE) {
|
||||||
|
defs.definitions = defs.definitions.concat(stat.init.definitions);
|
||||||
|
var name = stat.init.definitions[0].name;
|
||||||
|
var ref = make_node(AST_SymbolRef, name, name);
|
||||||
|
name.definition().references.push(ref);
|
||||||
|
stat.init = ref;
|
||||||
|
CHANGED = true;
|
||||||
|
}
|
||||||
stat.object = join_assigns_expr(stat.object);
|
stat.object = join_assigns_expr(stat.object);
|
||||||
} else if (stat instanceof AST_If) {
|
} else if (stat instanceof AST_If) {
|
||||||
stat.condition = join_assigns_expr(stat.condition);
|
stat.condition = join_assigns_expr(stat.condition);
|
||||||
@@ -3152,11 +3170,11 @@ merge(Compressor.prototype, {
|
|||||||
return this.expression._find_defs(compressor, "." + this.property + suffix);
|
return this.expression._find_defs(compressor, "." + this.property + suffix);
|
||||||
});
|
});
|
||||||
def(AST_SymbolDeclaration, function(compressor) {
|
def(AST_SymbolDeclaration, function(compressor) {
|
||||||
if (!this.global()) return;
|
if (!this.definition().global) return;
|
||||||
if (HOP(compressor.option("global_defs"), this.name)) warn(this);
|
if (HOP(compressor.option("global_defs"), this.name)) warn(this);
|
||||||
});
|
});
|
||||||
def(AST_SymbolRef, function(compressor, suffix) {
|
def(AST_SymbolRef, function(compressor, suffix) {
|
||||||
if (!this.global()) return;
|
if (!this.definition().global) return;
|
||||||
var defines = compressor.option("global_defs");
|
var defines = compressor.option("global_defs");
|
||||||
var name = this.name + suffix;
|
var name = this.name + suffix;
|
||||||
if (HOP(defines, name)) return to_node(defines[name], this);
|
if (HOP(defines, name)) return to_node(defines[name], this);
|
||||||
@@ -3501,7 +3519,8 @@ merge(Compressor.prototype, {
|
|||||||
var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
|
var left = this.left._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (left === this.left) return this;
|
if (left === this.left) return this;
|
||||||
if (this.operator == (left ? "||" : "&&")) return left;
|
if (this.operator == (left ? "||" : "&&")) return left;
|
||||||
var right = this.right._eval(compressor, ignore_side_effects, cached, depth);
|
var rhs_ignore_side_effects = ignore_side_effects && !(left && typeof left == "object");
|
||||||
|
var right = this.right._eval(compressor, rhs_ignore_side_effects, cached, depth);
|
||||||
if (right === this.right) return this;
|
if (right === this.right) return this;
|
||||||
var result;
|
var result;
|
||||||
switch (this.operator) {
|
switch (this.operator) {
|
||||||
@@ -3568,6 +3587,7 @@ merge(Compressor.prototype, {
|
|||||||
escaped[0].walk(new TreeWalker(function(node) {
|
escaped[0].walk(new TreeWalker(function(node) {
|
||||||
if (found) return true;
|
if (found) return true;
|
||||||
if (node === ref) return found = true;
|
if (node === ref) return found = true;
|
||||||
|
if (node instanceof AST_Scope) return true;
|
||||||
}));
|
}));
|
||||||
return found;
|
return found;
|
||||||
default:
|
default:
|
||||||
@@ -4292,6 +4312,7 @@ merge(Compressor.prototype, {
|
|||||||
return sym;
|
return sym;
|
||||||
};
|
};
|
||||||
var assign_in_use = Object.create(null);
|
var assign_in_use = Object.create(null);
|
||||||
|
var for_ins = Object.create(null);
|
||||||
var in_use = [];
|
var in_use = [];
|
||||||
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
|
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
|
||||||
var value_read = Object.create(null);
|
var value_read = Object.create(null);
|
||||||
@@ -4454,7 +4475,7 @@ merge(Compressor.prototype, {
|
|||||||
if (drop_funcs && node !== self && node instanceof AST_Defun) {
|
if (drop_funcs && node !== self && node instanceof AST_Defun) {
|
||||||
var def = node.name.definition();
|
var def = node.name.definition();
|
||||||
if (!(def.id in in_use_ids)) {
|
if (!(def.id in in_use_ids)) {
|
||||||
log(node.name, "Dropping unused function {name} [{file}:{line},{col}]", template(node.name));
|
log(node.name, "Dropping unused function {name}");
|
||||||
def.eliminated++;
|
def.eliminated++;
|
||||||
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
|
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
|
||||||
}
|
}
|
||||||
@@ -4469,7 +4490,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!(sym.definition().id in in_use_ids)) {
|
if (!(sym.definition().id in in_use_ids)) {
|
||||||
sym.__unused = true;
|
sym.__unused = true;
|
||||||
if (trim) {
|
if (trim) {
|
||||||
log(sym, "Dropping unused function argument {name} [{file}:{line},{col}]", template(sym));
|
log(sym, "Dropping unused function argument {name}");
|
||||||
a.pop();
|
a.pop();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -4572,7 +4593,7 @@ merge(Compressor.prototype, {
|
|||||||
AST_Node.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);
|
side_effects.push(value);
|
||||||
} else {
|
} else {
|
||||||
log(def.name, "Dropping unused variable {name} [{file}:{line},{col}]", template(def.name));
|
log(def.name, "Dropping unused variable {name}");
|
||||||
}
|
}
|
||||||
sym.eliminated++;
|
sym.eliminated++;
|
||||||
}
|
}
|
||||||
@@ -4582,23 +4603,37 @@ merge(Compressor.prototype, {
|
|||||||
return !def || fn.name && def === fn.name.definition();
|
return !def || fn.name && def === fn.name.definition();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
if (head.length == 0 && tail.length == duplicated) {
|
switch (head.length) {
|
||||||
[].unshift.apply(side_effects, tail.map(function(def) {
|
case 0:
|
||||||
AST_Node.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
if (tail.length == 0) break;
|
||||||
var sym = def.name.definition();
|
if (tail.length == duplicated) {
|
||||||
var ref = make_node(AST_SymbolRef, def.name, def.name);
|
[].unshift.apply(side_effects, tail.map(function(def) {
|
||||||
sym.references.push(ref);
|
AST_Node.warn("Dropping duplicated definition of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||||
var assign = make_node(AST_Assign, def, {
|
var sym = def.name.definition();
|
||||||
operator: "=",
|
var ref = make_node(AST_SymbolRef, def.name, def.name);
|
||||||
left: ref,
|
sym.references.push(ref);
|
||||||
right: def.value
|
var assign = make_node(AST_Assign, def, {
|
||||||
});
|
operator: "=",
|
||||||
var index = indexOf_assign(sym, def);
|
left: ref,
|
||||||
if (index >= 0) assign_in_use[sym.id][index] = assign;
|
right: def.value
|
||||||
sym.eliminated++;
|
});
|
||||||
return assign;
|
var index = indexOf_assign(sym, def);
|
||||||
}));
|
if (index >= 0) assign_in_use[sym.id][index] = assign;
|
||||||
} else if (head.length > 0 || tail.length > 0) {
|
sym.eliminated++;
|
||||||
|
return assign;
|
||||||
|
}));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 1:
|
||||||
|
if (tail.length == 0) {
|
||||||
|
var id = head[0].name.definition().id;
|
||||||
|
if (id in for_ins) {
|
||||||
|
node.definitions = head;
|
||||||
|
for_ins[id].init = node;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
node.definitions = head.concat(tail);
|
node.definitions = head.concat(tail);
|
||||||
body.push(node);
|
body.push(node);
|
||||||
}
|
}
|
||||||
@@ -4607,16 +4642,7 @@ merge(Compressor.prototype, {
|
|||||||
body: make_sequence(node, side_effects)
|
body: make_sequence(node, side_effects)
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
switch (body.length) {
|
return insert_statements(body, node, in_list);
|
||||||
case 0:
|
|
||||||
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
|
|
||||||
case 1:
|
|
||||||
return body[0];
|
|
||||||
default:
|
|
||||||
return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, {
|
|
||||||
body: body
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
|
if (node instanceof AST_LabeledStatement && node.body instanceof AST_For) {
|
||||||
// Certain combination of unused name + side effect leads to invalid AST:
|
// Certain combination of unused name + side effect leads to invalid AST:
|
||||||
@@ -4667,19 +4693,31 @@ merge(Compressor.prototype, {
|
|||||||
return !block ? node : in_list ? List.splice(block.body) : block;
|
return !block ? node : in_list ? List.splice(block.body) : block;
|
||||||
} else if (node instanceof AST_ForIn) {
|
} else if (node instanceof AST_ForIn) {
|
||||||
if (!drop_vars || !compressor.option("loops")) return;
|
if (!drop_vars || !compressor.option("loops")) return;
|
||||||
if (!(node.init instanceof AST_Definitions)) return;
|
|
||||||
var sym = node.init.definitions[0].name;
|
|
||||||
if (sym.definition().id in in_use_ids) return;
|
|
||||||
if (!is_empty(node.body)) return;
|
if (!is_empty(node.body)) return;
|
||||||
log(sym, "Dropping unused loop variable {name} [{file}:{line},{col}]", template(sym));
|
var sym = node.init;
|
||||||
|
if (sym instanceof AST_Definitions) {
|
||||||
|
sym = sym.definitions[0].name;
|
||||||
|
} else while (sym instanceof AST_PropAccess) {
|
||||||
|
sym = sym.expression.tail_node();
|
||||||
|
}
|
||||||
|
var def = sym.definition();
|
||||||
|
if (!def) return;
|
||||||
|
if (def.id in in_use_ids) return;
|
||||||
|
if (def.scope !== self && member(def, self.enclosed)) return;
|
||||||
|
log(sym, "Dropping unused loop variable {name}");
|
||||||
|
if (for_ins[def.id] === node) delete for_ins[def.id];
|
||||||
|
var body = [];
|
||||||
var value = node.object.drop_side_effect_free(compressor);
|
var value = node.object.drop_side_effect_free(compressor);
|
||||||
if (value) {
|
if (value) {
|
||||||
AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", template(sym));
|
AST_Node.warn("Side effects in object of for-in loop [{file}:{line},{col}]", value.start);
|
||||||
return make_node(AST_SimpleStatement, node, {
|
body.push(make_node(AST_SimpleStatement, node, {
|
||||||
body: value
|
body: value
|
||||||
});
|
}));
|
||||||
}
|
}
|
||||||
return in_list ? List.skip : make_node(AST_EmptyStatement, node);
|
if (node.init instanceof AST_Definitions && def.orig[0] instanceof AST_SymbolCatch) {
|
||||||
|
body.push(node.init);
|
||||||
|
}
|
||||||
|
return insert_statements(body, node, in_list);
|
||||||
} else if (node instanceof AST_Sequence) {
|
} else if (node instanceof AST_Sequence) {
|
||||||
if (node.expressions.length == 1) return node.expressions[0];
|
if (node.expressions.length == 1) return node.expressions[0];
|
||||||
}
|
}
|
||||||
@@ -4699,8 +4737,8 @@ merge(Compressor.prototype, {
|
|||||||
drop_unused_call_args(call, compressor, fns_with_marked_args);
|
drop_unused_call_args(call, compressor, fns_with_marked_args);
|
||||||
});
|
});
|
||||||
|
|
||||||
function log(sym, text, props) {
|
function log(sym, text) {
|
||||||
AST_Node[sym.unreferenced() ? "warn" : "info"](text, props);
|
AST_Node[sym.definition().references.length > 0 ? "info" : "warn"](text + " [{file}:{line},{col}]", template(sym));
|
||||||
}
|
}
|
||||||
|
|
||||||
function template(sym) {
|
function template(sym) {
|
||||||
@@ -4712,6 +4750,19 @@ merge(Compressor.prototype, {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function insert_statements(body, orig, in_list) {
|
||||||
|
switch (body.length) {
|
||||||
|
case 0:
|
||||||
|
return in_list ? List.skip : make_node(AST_EmptyStatement, orig);
|
||||||
|
case 1:
|
||||||
|
return body[0];
|
||||||
|
default:
|
||||||
|
return in_list ? List.splice(body) : make_node(AST_BlockStatement, orig, {
|
||||||
|
body: body
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function track_assigns(def, node) {
|
function track_assigns(def, node) {
|
||||||
if (def.scope !== self) return false;
|
if (def.scope !== self) return false;
|
||||||
if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
|
if (!def.fixed || !node.fixed) assign_in_use[def.id] = false;
|
||||||
@@ -4788,6 +4839,17 @@ merge(Compressor.prototype, {
|
|||||||
if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
|
if (track_assigns(node_def, sym) && is_lhs(sym, node) !== sym) add_assigns(node_def, sym);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_ForIn) {
|
||||||
|
if (node.init instanceof AST_SymbolRef && scope === self) {
|
||||||
|
var id = node.init.definition().id;
|
||||||
|
if (!(id in for_ins)) for_ins[id] = node;
|
||||||
|
}
|
||||||
|
if (!drop_vars || !compressor.option("loops")) return;
|
||||||
|
if (!is_empty(node.body)) return;
|
||||||
|
if (node.init.has_side_effects(compressor)) return;
|
||||||
|
node.object.walk(tw);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
if (node instanceof AST_SymbolRef) {
|
if (node instanceof AST_SymbolRef) {
|
||||||
node_def = node.definition();
|
node_def = node.definition();
|
||||||
if (!(node_def.id in in_use_ids)) {
|
if (!(node_def.id in in_use_ids)) {
|
||||||
@@ -6395,7 +6457,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
var code = OutputStream();
|
var code = OutputStream();
|
||||||
AST_BlockStatement.prototype._codegen.call(fun, fun, code);
|
AST_BlockStatement.prototype._codegen.call(fun, code);
|
||||||
self.args = [
|
self.args = [
|
||||||
make_node(AST_String, self, {
|
make_node(AST_String, self, {
|
||||||
value: fun.argnames.map(function(arg) {
|
value: fun.argnames.map(function(arg) {
|
||||||
@@ -6890,72 +6952,70 @@ merge(Compressor.prototype, {
|
|||||||
var SIGN_OPS = makePredicate("+ -");
|
var SIGN_OPS = makePredicate("+ -");
|
||||||
var MULTIPLICATIVE_OPS = makePredicate("* / %");
|
var MULTIPLICATIVE_OPS = makePredicate("* / %");
|
||||||
OPT(AST_UnaryPrefix, function(self, compressor) {
|
OPT(AST_UnaryPrefix, function(self, compressor) {
|
||||||
var e = self.expression;
|
var op = self.operator;
|
||||||
|
var exp = self.expression;
|
||||||
if (compressor.option("evaluate")
|
if (compressor.option("evaluate")
|
||||||
&& self.operator == "delete"
|
&& op == "delete"
|
||||||
&& !(e instanceof AST_SymbolRef
|
&& !(exp instanceof AST_SymbolRef
|
||||||
|| e instanceof AST_PropAccess
|
|| exp instanceof AST_PropAccess
|
||||||
|| is_identifier_atom(e))) {
|
|| is_identifier_atom(exp))) {
|
||||||
if (e instanceof AST_Sequence) {
|
if (exp instanceof AST_Sequence) {
|
||||||
e = e.expressions.slice();
|
exp = exp.expressions.slice();
|
||||||
e.push(make_node(AST_True, self));
|
exp.push(make_node(AST_True, self));
|
||||||
return make_sequence(self, e).optimize(compressor);
|
return make_sequence(self, exp).optimize(compressor);
|
||||||
}
|
}
|
||||||
return make_sequence(self, [ e, make_node(AST_True, self) ]).optimize(compressor);
|
return make_sequence(self, [ exp, make_node(AST_True, self) ]).optimize(compressor);
|
||||||
}
|
}
|
||||||
if (compressor.option("sequences")) {
|
if (compressor.option("sequences") && !(op == "typeof" && is_undeclared_ref(exp.tail_node()))) {
|
||||||
var seq = lift_sequence_in_expression(self, compressor);
|
var seq = lift_sequence_in_expression(self, compressor);
|
||||||
if (seq !== self) return seq.optimize(compressor);
|
if (seq !== self) return seq.optimize(compressor);
|
||||||
}
|
}
|
||||||
if (compressor.option("side_effects") && self.operator == "void") {
|
if (compressor.option("side_effects") && op == "void") {
|
||||||
e = e.drop_side_effect_free(compressor);
|
exp = exp.drop_side_effect_free(compressor);
|
||||||
if (e) {
|
if (!exp) return make_node(AST_Undefined, self).optimize(compressor);
|
||||||
self.expression = e;
|
self.expression = exp;
|
||||||
return self;
|
return self;
|
||||||
} else {
|
|
||||||
return make_node(AST_Undefined, self).optimize(compressor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (compressor.option("booleans")) {
|
if (compressor.option("booleans")) {
|
||||||
if (self.operator == "!" && e.is_truthy()) {
|
if (op == "!" && exp.is_truthy()) {
|
||||||
return make_sequence(self, [ e, make_node(AST_False, self) ]).optimize(compressor);
|
return make_sequence(self, [ exp, make_node(AST_False, self) ]).optimize(compressor);
|
||||||
} else if (compressor.in_boolean_context()) switch (self.operator) {
|
} else if (compressor.in_boolean_context()) switch (op) {
|
||||||
case "!":
|
case "!":
|
||||||
if (e instanceof AST_UnaryPrefix && e.operator == "!") {
|
if (exp instanceof AST_UnaryPrefix && exp.operator == "!") {
|
||||||
// !!foo => foo, if we're in boolean context
|
// !!foo => foo, if we're in boolean context
|
||||||
return e.expression;
|
return exp.expression;
|
||||||
}
|
}
|
||||||
if (e instanceof AST_Binary) {
|
if (exp instanceof AST_Binary) {
|
||||||
self = best_of(compressor, self, e.negate(compressor, first_in_statement(compressor)));
|
self = best_of(compressor, self, exp.negate(compressor, first_in_statement(compressor)));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case "typeof":
|
case "typeof":
|
||||||
// typeof always returns a non-empty string, thus it's
|
// typeof always returns a non-empty string, thus it's
|
||||||
// always true in booleans
|
// always true in booleans
|
||||||
AST_Node.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, [
|
return (exp instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
|
||||||
e,
|
exp,
|
||||||
make_node(AST_True, self)
|
make_node(AST_True, self)
|
||||||
])).optimize(compressor);
|
])).optimize(compressor);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (self.operator == "-" && e instanceof AST_Infinity) e = e.transform(compressor);
|
if (op == "-" && exp instanceof AST_Infinity) exp = exp.transform(compressor);
|
||||||
if (compressor.option("evaluate")
|
if (compressor.option("evaluate")
|
||||||
&& e instanceof AST_Binary
|
&& exp instanceof AST_Binary
|
||||||
&& SIGN_OPS[self.operator]
|
&& SIGN_OPS[op]
|
||||||
&& MULTIPLICATIVE_OPS[e.operator]
|
&& MULTIPLICATIVE_OPS[exp.operator]
|
||||||
&& (e.left.is_constant() || !e.right.has_side_effects(compressor))) {
|
&& (exp.left.is_constant() || !exp.right.has_side_effects(compressor))) {
|
||||||
return make_node(AST_Binary, self, {
|
return make_node(AST_Binary, self, {
|
||||||
operator: e.operator,
|
operator: exp.operator,
|
||||||
left: make_node(AST_UnaryPrefix, e.left, {
|
left: make_node(AST_UnaryPrefix, exp.left, {
|
||||||
operator: self.operator,
|
operator: op,
|
||||||
expression: e.left
|
expression: exp.left
|
||||||
}),
|
}),
|
||||||
right: e.right
|
right: exp.right
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// avoids infinite recursion of numerals
|
// avoids infinite recursion of numerals
|
||||||
return self.operator == "-" && (e instanceof AST_Number || e instanceof AST_Infinity)
|
return op == "-" && (exp instanceof AST_Number || exp instanceof AST_Infinity)
|
||||||
? self : try_evaluate(compressor, self);
|
? self : try_evaluate(compressor, self);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -7184,11 +7244,11 @@ merge(Compressor.prototype, {
|
|||||||
var parent = compressor.parent();
|
var parent = compressor.parent();
|
||||||
if (compressor.option("comparisons") && self.is_boolean(compressor)) {
|
if (compressor.option("comparisons") && self.is_boolean(compressor)) {
|
||||||
if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
|
if (!(parent instanceof AST_Binary) || parent instanceof AST_Assign) {
|
||||||
var negated = make_node(AST_UnaryPrefix, self, {
|
var negated = best_of(compressor, self, make_node(AST_UnaryPrefix, self, {
|
||||||
operator: "!",
|
operator: "!",
|
||||||
expression: self.negate(compressor, first_in_statement(compressor))
|
expression: self.negate(compressor, first_in_statement(compressor))
|
||||||
});
|
}));
|
||||||
self = best_of(compressor, self, negated);
|
if (negated !== self) return negated;
|
||||||
}
|
}
|
||||||
switch (self.operator) {
|
switch (self.operator) {
|
||||||
case ">": reverse("<"); break;
|
case ">": reverse("<"); break;
|
||||||
@@ -7496,13 +7556,15 @@ merge(Compressor.prototype, {
|
|||||||
&& self.left.is_number(compressor)) {
|
&& self.left.is_number(compressor)) {
|
||||||
if (self.left.left instanceof AST_Constant) {
|
if (self.left.left instanceof AST_Constant) {
|
||||||
var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
|
var lhs = make_binary(self.left, self.operator, self.left.left, self.right, self.left.left.start, self.right.end);
|
||||||
self = make_binary(self, self.left.operator, lhs, self.left.right);
|
self = make_binary(self, self.left.operator, try_evaluate(compressor, lhs), self.left.right);
|
||||||
} else if (self.left.right instanceof AST_Constant) {
|
} else if (self.left.right instanceof AST_Constant) {
|
||||||
var rhs = make_binary(self.left, align(self.left.operator, self.operator), self.left.right, self.right, self.left.right.start, self.right.end);
|
var op = align(self.left.operator, self.operator);
|
||||||
if (self.left.operator != "-"
|
var rhs = try_evaluate(compressor, make_binary(self.left, op, self.left.right, self.right));
|
||||||
|| !self.right.value
|
if (rhs.is_constant()
|
||||||
|| rhs.evaluate(compressor)
|
&& !(self.left.operator == "-"
|
||||||
|| !self.left.left.is_negative_zero()) {
|
&& self.right.value != 0
|
||||||
|
&& +rhs.value == 0
|
||||||
|
&& self.left.left.is_negative_zero())) {
|
||||||
self = make_binary(self, self.left.operator, self.left.left, rhs);
|
self = make_binary(self, self.left.operator, self.left.left, rhs);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -7658,7 +7720,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function is_indexFn(node) {
|
function is_indexFn(node) {
|
||||||
return node instanceof AST_Call
|
return node.TYPE == "Call"
|
||||||
&& node.expression instanceof AST_Dot
|
&& node.expression instanceof AST_Dot
|
||||||
&& indexFns[node.expression.property];
|
&& indexFns[node.expression.property];
|
||||||
}
|
}
|
||||||
@@ -8520,7 +8582,9 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (is_lhs(compressor.self(), parent)) return self;
|
if (is_lhs(compressor.self(), parent)) return self;
|
||||||
if (compressor.option("sequences") && compressor.parent().TYPE != "Call") {
|
if (compressor.option("sequences")
|
||||||
|
&& parent.TYPE != "Call"
|
||||||
|
&& !(parent instanceof AST_ForIn && parent.init === self)) {
|
||||||
var seq = lift_sequence_in_expression(self, compressor);
|
var seq = lift_sequence_in_expression(self, compressor);
|
||||||
if (seq !== self) return seq.optimize(compressor);
|
if (seq !== self) return seq.optimize(compressor);
|
||||||
}
|
}
|
||||||
@@ -8631,8 +8695,11 @@ merge(Compressor.prototype, {
|
|||||||
col: self.start.col
|
col: self.start.col
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
if (is_lhs(compressor.self(), compressor.parent())) return self;
|
var parent = compressor.parent();
|
||||||
if (compressor.option("sequences") && compressor.parent().TYPE != "Call") {
|
if (is_lhs(compressor.self(), parent)) return self;
|
||||||
|
if (compressor.option("sequences")
|
||||||
|
&& parent.TYPE != "Call"
|
||||||
|
&& !(parent instanceof AST_ForIn && parent.init === self)) {
|
||||||
var seq = lift_sequence_in_expression(self, compressor);
|
var seq = lift_sequence_in_expression(self, compressor);
|
||||||
if (seq !== self) return seq.optimize(compressor);
|
if (seq !== self) return seq.optimize(compressor);
|
||||||
}
|
}
|
||||||
|
|||||||
379
lib/output.js
379
lib/output.js
@@ -100,7 +100,7 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var indentation = 0;
|
var indentation = options.indent_start;
|
||||||
var current_col = 0;
|
var current_col = 0;
|
||||||
var current_line = 1;
|
var current_line = 1;
|
||||||
var current_pos = 0;
|
var current_pos = 0;
|
||||||
@@ -191,10 +191,6 @@ function OutputStream(options) {
|
|||||||
return name;
|
return name;
|
||||||
}
|
}
|
||||||
|
|
||||||
function make_indent(back) {
|
|
||||||
return repeat_string(" ", options.indent_start + indentation - back * options.indent_level);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* -----[ beautification/minification ]----- */
|
/* -----[ beautification/minification ]----- */
|
||||||
|
|
||||||
var has_parens = false;
|
var has_parens = false;
|
||||||
@@ -345,9 +341,7 @@ function OutputStream(options) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
var indent = options.beautify ? function(half) {
|
var indent = options.beautify ? function(half) {
|
||||||
if (options.beautify) {
|
print(repeat_string(" ", half ? indentation - (options.indent_level >> 1) : indentation));
|
||||||
print(make_indent(half ? 0.5 : 0));
|
|
||||||
}
|
|
||||||
} : noop;
|
} : noop;
|
||||||
|
|
||||||
var with_indent = options.beautify ? function(col, cont) {
|
var with_indent = options.beautify ? function(col, cont) {
|
||||||
@@ -575,9 +569,9 @@ function OutputStream(options) {
|
|||||||
get : get,
|
get : get,
|
||||||
toString : get,
|
toString : get,
|
||||||
indent : indent,
|
indent : indent,
|
||||||
indentation : function() { return indentation },
|
should_break : readonly ? noop : function() {
|
||||||
current_width : function() { return current_col - indentation },
|
return options.width && current_col - indentation >= options.width;
|
||||||
should_break : function() { return options.width && this.current_width() >= options.width },
|
},
|
||||||
has_parens : function() { return has_parens },
|
has_parens : function() { return has_parens },
|
||||||
newline : newline,
|
newline : newline,
|
||||||
print : print,
|
print : print,
|
||||||
@@ -630,13 +624,7 @@ function OutputStream(options) {
|
|||||||
var use_asm = false;
|
var use_asm = false;
|
||||||
|
|
||||||
AST_Node.DEFMETHOD("print", function(stream, force_parens) {
|
AST_Node.DEFMETHOD("print", function(stream, force_parens) {
|
||||||
var self = this, generator = self._codegen;
|
var self = this;
|
||||||
function doit() {
|
|
||||||
stream.prepend_comments(self);
|
|
||||||
self.add_source_map(stream);
|
|
||||||
generator(self, stream);
|
|
||||||
stream.append_comments(self);
|
|
||||||
}
|
|
||||||
stream.push_node(self);
|
stream.push_node(self);
|
||||||
if (force_parens || self.needs_parens(stream)) {
|
if (force_parens || self.needs_parens(stream)) {
|
||||||
stream.with_parens(doit);
|
stream.with_parens(doit);
|
||||||
@@ -644,9 +632,14 @@ function OutputStream(options) {
|
|||||||
doit();
|
doit();
|
||||||
}
|
}
|
||||||
stream.pop_node();
|
stream.pop_node();
|
||||||
});
|
|
||||||
AST_Node.DEFMETHOD("_print", AST_Node.prototype.print);
|
|
||||||
|
|
||||||
|
function doit() {
|
||||||
|
stream.prepend_comments(self);
|
||||||
|
self.add_source_map(stream);
|
||||||
|
self._codegen(stream);
|
||||||
|
stream.append_comments(self);
|
||||||
|
}
|
||||||
|
});
|
||||||
AST_Node.DEFMETHOD("print_to_string", function(options) {
|
AST_Node.DEFMETHOD("print_to_string", function(options) {
|
||||||
var s = OutputStream(options);
|
var s = OutputStream(options);
|
||||||
this.print(s);
|
this.print(s);
|
||||||
@@ -689,78 +682,66 @@ function OutputStream(options) {
|
|||||||
|
|
||||||
PARENS(AST_Unary, function(output) {
|
PARENS(AST_Unary, function(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
return p instanceof AST_PropAccess && p.expression === this
|
return (p instanceof AST_Call || p instanceof AST_PropAccess) && p.expression === this;
|
||||||
|| p instanceof AST_Call && p.expression === this;
|
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Sequence, function(output) {
|
PARENS(AST_Sequence, function(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// (foo, bar)() or foo(1, (2, 3), 4)
|
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||||
return p instanceof AST_Call
|
return p instanceof AST_Array
|
||||||
// !(foo, bar, baz)
|
|
||||||
|| p instanceof AST_Unary
|
|
||||||
// 1 + (2, 3) + 4 ==> 8
|
// 1 + (2, 3) + 4 ==> 8
|
||||||
|| p instanceof AST_Binary
|
|| p instanceof AST_Binary
|
||||||
// var a = (1, 2), b = a + a; ==> b == 4
|
// new (foo, bar) or foo(1, (2, 3), 4)
|
||||||
|| p instanceof AST_VarDef
|
|| p instanceof AST_Call
|
||||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
|
||||||
|| p instanceof AST_PropAccess && p.expression === this
|
|
||||||
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
|
||||||
|| p instanceof AST_Array
|
|
||||||
// { foo: (1, 2) }.foo ==> 2
|
|
||||||
|| p instanceof AST_ObjectProperty
|
|
||||||
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||||
// ==> 20 (side effect, set a := 10 and b := 20)
|
// ==> 20 (side effect, set a := 10 and b := 20)
|
||||||
|| p instanceof AST_Conditional;
|
|| p instanceof AST_Conditional
|
||||||
|
// { foo: (1, 2) }.foo ==> 2
|
||||||
|
|| p instanceof AST_ObjectProperty
|
||||||
|
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||||
|
|| p instanceof AST_PropAccess && p.expression === this
|
||||||
|
// !(foo, bar, baz)
|
||||||
|
|| p instanceof AST_Unary
|
||||||
|
// var a = (1, 2), b = a + a; ==> b == 4
|
||||||
|
|| p instanceof AST_VarDef;
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Binary, function(output) {
|
PARENS(AST_Binary, function(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// (foo && bar)()
|
|
||||||
if (p instanceof AST_Call && p.expression === this)
|
|
||||||
return true;
|
|
||||||
// typeof (foo && bar)
|
|
||||||
if (p instanceof AST_Unary)
|
|
||||||
return true;
|
|
||||||
// (foo && bar)["prop"], (foo && bar).prop
|
|
||||||
if (p instanceof AST_PropAccess && p.expression === this)
|
|
||||||
return true;
|
|
||||||
// this deals with precedence: 3 * (2 + 1)
|
// this deals with precedence: 3 * (2 + 1)
|
||||||
if (p instanceof AST_Binary) {
|
if (p instanceof AST_Binary) {
|
||||||
var po = p.operator, pp = PRECEDENCE[po];
|
var po = p.operator, pp = PRECEDENCE[po];
|
||||||
var so = this.operator, sp = PRECEDENCE[so];
|
var so = this.operator, sp = PRECEDENCE[so];
|
||||||
if (pp > sp
|
return pp > sp || (pp == sp && this === p.right);
|
||||||
|| (pp == sp
|
|
||||||
&& this === p.right)) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// (foo && bar)()
|
||||||
|
if (p instanceof AST_Call) return p.expression === this;
|
||||||
|
// (foo && bar)["prop"], (foo && bar).prop
|
||||||
|
if (p instanceof AST_PropAccess) return p.expression === this;
|
||||||
|
// typeof (foo && bar)
|
||||||
|
if (p instanceof AST_Unary) return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_PropAccess, function(output) {
|
PARENS(AST_PropAccess, function(output) {
|
||||||
|
var node = this;
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_New && p.expression === this) {
|
if (p instanceof AST_New && p.expression === node) {
|
||||||
// i.e. new (foo.bar().baz)
|
// i.e. new (foo().bar)
|
||||||
//
|
//
|
||||||
// if there's one call into this subtree, then we need
|
// if there's one call into this subtree, then we need
|
||||||
// parens around it too, otherwise the call will be
|
// parens around it too, otherwise the call will be
|
||||||
// interpreted as passing the arguments to the upper New
|
// interpreted as passing the arguments to the upper New
|
||||||
// expression.
|
// expression.
|
||||||
var parens = false;
|
do {
|
||||||
this.walk(new TreeWalker(function(node) {
|
node = node.expression;
|
||||||
if (parens || node instanceof AST_Scope) return true;
|
} while (node instanceof AST_PropAccess);
|
||||||
if (node instanceof AST_Call) {
|
return node.TYPE == "Call";
|
||||||
parens = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
return parens;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Call, function(output) {
|
PARENS(AST_Call, function(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_New && p.expression === this) return true;
|
if (p instanceof AST_New) return p.expression === this;
|
||||||
// https://bugs.webkit.org/show_bug.cgi?id=123506
|
// https://bugs.webkit.org/show_bug.cgi?id=123506
|
||||||
if (output.option('webkit')) {
|
if (output.option('webkit')) {
|
||||||
var g = output.parent(1);
|
var g = output.parent(1);
|
||||||
@@ -773,11 +754,12 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_New, function(output) {
|
PARENS(AST_New, function(output) {
|
||||||
|
if (need_constructor_parens(this, output)) return false;
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (!need_constructor_parens(this, output)
|
// (new foo)(bar)
|
||||||
&& (p instanceof AST_PropAccess // (new Date).getTime(), (new Date)["getTime"]()
|
if (p instanceof AST_Call) return p.expression === this;
|
||||||
|| p instanceof AST_Call && p.expression === this)) // (new foo)(bar)
|
// (new Date).getTime(), (new Date)["getTime"]()
|
||||||
return true;
|
return p instanceof AST_PropAccess;
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Number, function(output) {
|
PARENS(AST_Number, function(output) {
|
||||||
@@ -786,36 +768,29 @@ function OutputStream(options) {
|
|||||||
var value = this.value;
|
var value = this.value;
|
||||||
// https://github.com/mishoo/UglifyJS/issues/115
|
// https://github.com/mishoo/UglifyJS/issues/115
|
||||||
// https://github.com/mishoo/UglifyJS/pull/1009
|
// https://github.com/mishoo/UglifyJS/pull/1009
|
||||||
if (value < 0 || /^0/.test(make_num(value))) {
|
return value < 0 || /^0/.test(make_num(value));
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS([ AST_Assign, AST_Conditional ], function(output) {
|
PARENS([ AST_Assign, AST_Conditional ], function(output) {
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
// !(a = false) → true
|
|
||||||
if (p instanceof AST_Unary)
|
|
||||||
return true;
|
|
||||||
// 1 + (a = 2) + 3 → 6, side effect setting a = 2
|
// 1 + (a = 2) + 3 → 6, side effect setting a = 2
|
||||||
if (p instanceof AST_Binary && !(p instanceof AST_Assign))
|
if (p instanceof AST_Binary) return !(p instanceof AST_Assign);
|
||||||
return true;
|
|
||||||
// (a = func)() —or— new (a = Object)()
|
// (a = func)() —or— new (a = Object)()
|
||||||
if (p instanceof AST_Call && p.expression === this)
|
if (p instanceof AST_Call) return p.expression === this;
|
||||||
return true;
|
|
||||||
// (a = foo) ? bar : baz
|
// (a = foo) ? bar : baz
|
||||||
if (p instanceof AST_Conditional && p.condition === this)
|
if (p instanceof AST_Conditional) return p.condition === this;
|
||||||
return true;
|
|
||||||
// (a = foo)["prop"] —or— (a = foo).prop
|
// (a = foo)["prop"] —or— (a = foo).prop
|
||||||
if (p instanceof AST_PropAccess && p.expression === this)
|
if (p instanceof AST_PropAccess) return p.expression === this;
|
||||||
return true;
|
// !(a = false) → true
|
||||||
|
if (p instanceof AST_Unary) return true;
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ PRINTERS ]----- */
|
/* -----[ PRINTERS ]----- */
|
||||||
|
|
||||||
DEFPRINT(AST_Directive, function(self, output) {
|
DEFPRINT(AST_Directive, function(output) {
|
||||||
var quote = self.quote;
|
var quote = this.quote;
|
||||||
var value = self.value;
|
var value = this.value;
|
||||||
switch (output.option("quote_style")) {
|
switch (output.option("quote_style")) {
|
||||||
case 0:
|
case 0:
|
||||||
case 2:
|
case 2:
|
||||||
@@ -828,7 +803,7 @@ function OutputStream(options) {
|
|||||||
output.print(quote + value + quote);
|
output.print(quote + value + quote);
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Debugger, function(self, output) {
|
DEFPRINT(AST_Debugger, function(output) {
|
||||||
output.print("debugger");
|
output.print("debugger");
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
@@ -864,21 +839,21 @@ function OutputStream(options) {
|
|||||||
force_statement(this.body, output);
|
force_statement(this.body, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
DEFPRINT(AST_Statement, function(self, output) {
|
DEFPRINT(AST_Statement, function(output) {
|
||||||
self.body.print(output);
|
this.body.print(output);
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Toplevel, function(self, output) {
|
DEFPRINT(AST_Toplevel, function(output) {
|
||||||
display_body(self.body, true, output, true);
|
display_body(this.body, true, output, true);
|
||||||
output.print("");
|
output.print("");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_LabeledStatement, function(self, output) {
|
DEFPRINT(AST_LabeledStatement, function(output) {
|
||||||
self.label.print(output);
|
this.label.print(output);
|
||||||
output.colon();
|
output.colon();
|
||||||
self.body.print(output);
|
this.body.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_SimpleStatement, function(self, output) {
|
DEFPRINT(AST_SimpleStatement, function(output) {
|
||||||
self.body.print(output);
|
this.body.print(output);
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
function print_braced_empty(self, output) {
|
function print_braced_empty(self, output) {
|
||||||
@@ -895,13 +870,14 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
} else print_braced_empty(self, output);
|
} else print_braced_empty(self, output);
|
||||||
}
|
}
|
||||||
DEFPRINT(AST_BlockStatement, function(self, output) {
|
DEFPRINT(AST_BlockStatement, function(output) {
|
||||||
print_braced(self, output);
|
print_braced(this, output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_EmptyStatement, function(self, output) {
|
DEFPRINT(AST_EmptyStatement, function(output) {
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Do, function(self, output) {
|
DEFPRINT(AST_Do, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("do");
|
output.print("do");
|
||||||
output.space();
|
output.space();
|
||||||
make_block(self.body, output);
|
make_block(self.body, output);
|
||||||
@@ -913,7 +889,8 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
output.semicolon();
|
output.semicolon();
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_While, function(self, output) {
|
DEFPRINT(AST_While, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("while");
|
output.print("while");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
@@ -922,7 +899,8 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
self._do_print_body(output);
|
self._do_print_body(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_For, function(self, output) {
|
DEFPRINT(AST_For, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("for");
|
output.print("for");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
@@ -951,7 +929,8 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
self._do_print_body(output);
|
self._do_print_body(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_ForIn, function(self, output) {
|
DEFPRINT(AST_ForIn, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("for");
|
output.print("for");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
@@ -964,7 +943,8 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
self._do_print_body(output);
|
self._do_print_body(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_With, function(self, output) {
|
DEFPRINT(AST_With, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("with");
|
output.print("with");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
@@ -975,7 +955,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ functions ]----- */
|
/* -----[ functions ]----- */
|
||||||
AST_Lambda.DEFMETHOD("_do_print", function(output, nokeyword) {
|
DEFPRINT(AST_Lambda, function(output, nokeyword) {
|
||||||
var self = this;
|
var self = this;
|
||||||
if (!nokeyword) {
|
if (!nokeyword) {
|
||||||
output.print("function");
|
output.print("function");
|
||||||
@@ -993,32 +973,23 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
print_braced(self, output, true);
|
print_braced(self, output, true);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Lambda, function(self, output) {
|
|
||||||
self._do_print(output);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* -----[ jumps ]----- */
|
/* -----[ jumps ]----- */
|
||||||
function print_jump(output, kind, target) {
|
function print_jump(kind, prop) {
|
||||||
output.print(kind);
|
return function(output) {
|
||||||
if (target) {
|
output.print(kind);
|
||||||
output.space();
|
var target = this[prop];
|
||||||
target.print(output);
|
if (target) {
|
||||||
}
|
output.space();
|
||||||
output.semicolon();
|
target.print(output);
|
||||||
|
}
|
||||||
|
output.semicolon();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
DEFPRINT(AST_Return, print_jump("return", "value"));
|
||||||
DEFPRINT(AST_Return, function(self, output) {
|
DEFPRINT(AST_Throw, print_jump("throw", "value"));
|
||||||
print_jump(output, "return", self.value);
|
DEFPRINT(AST_Break, print_jump("break", "label"));
|
||||||
});
|
DEFPRINT(AST_Continue, print_jump("continue", "label"));
|
||||||
DEFPRINT(AST_Throw, function(self, output) {
|
|
||||||
print_jump(output, "throw", self.value);
|
|
||||||
});
|
|
||||||
DEFPRINT(AST_Break, function(self, output) {
|
|
||||||
print_jump(output, "break", self.label);
|
|
||||||
});
|
|
||||||
DEFPRINT(AST_Continue, function(self, output) {
|
|
||||||
print_jump(output, "continue", self.label);
|
|
||||||
});
|
|
||||||
|
|
||||||
/* -----[ if ]----- */
|
/* -----[ if ]----- */
|
||||||
function make_then(self, output) {
|
function make_then(self, output) {
|
||||||
@@ -1047,7 +1018,8 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
force_statement(self.body, output);
|
force_statement(self.body, output);
|
||||||
}
|
}
|
||||||
DEFPRINT(AST_If, function(self, output) {
|
DEFPRINT(AST_If, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("if");
|
output.print("if");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
@@ -1069,7 +1041,8 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ switch ]----- */
|
/* -----[ switch ]----- */
|
||||||
DEFPRINT(AST_Switch, function(self, output) {
|
DEFPRINT(AST_Switch, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("switch");
|
output.print("switch");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
@@ -1087,28 +1060,30 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
AST_SwitchBranch.DEFMETHOD("_do_print_body", function(output) {
|
function print_branch_body(self, output) {
|
||||||
output.newline();
|
output.newline();
|
||||||
this.body.forEach(function(stmt) {
|
self.body.forEach(function(stmt) {
|
||||||
output.indent();
|
output.indent();
|
||||||
stmt.print(output);
|
stmt.print(output);
|
||||||
output.newline();
|
output.newline();
|
||||||
});
|
});
|
||||||
});
|
}
|
||||||
DEFPRINT(AST_Default, function(self, output) {
|
DEFPRINT(AST_Default, function(output) {
|
||||||
output.print("default:");
|
output.print("default:");
|
||||||
self._do_print_body(output);
|
print_branch_body(this, output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Case, function(self, output) {
|
DEFPRINT(AST_Case, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("case");
|
output.print("case");
|
||||||
output.space();
|
output.space();
|
||||||
self.expression.print(output);
|
self.expression.print(output);
|
||||||
output.print(":");
|
output.print(":");
|
||||||
self._do_print_body(output);
|
print_branch_body(self, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ exceptions ]----- */
|
/* -----[ exceptions ]----- */
|
||||||
DEFPRINT(AST_Try, function(self, output) {
|
DEFPRINT(AST_Try, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("try");
|
output.print("try");
|
||||||
output.space();
|
output.space();
|
||||||
print_braced(self, output);
|
print_braced(self, output);
|
||||||
@@ -1121,7 +1096,8 @@ function OutputStream(options) {
|
|||||||
self.bfinally.print(output);
|
self.bfinally.print(output);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Catch, function(self, output) {
|
DEFPRINT(AST_Catch, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("catch");
|
output.print("catch");
|
||||||
output.space();
|
output.space();
|
||||||
output.with_parens(function() {
|
output.with_parens(function() {
|
||||||
@@ -1130,13 +1106,14 @@ function OutputStream(options) {
|
|||||||
output.space();
|
output.space();
|
||||||
print_braced(self, output);
|
print_braced(self, output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Finally, function(self, output) {
|
DEFPRINT(AST_Finally, function(output) {
|
||||||
output.print("finally");
|
output.print("finally");
|
||||||
output.space();
|
output.space();
|
||||||
print_braced(self, output);
|
print_braced(this, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
DEFPRINT(AST_Var, function(self, output) {
|
DEFPRINT(AST_Var, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("var");
|
output.print("var");
|
||||||
output.space();
|
output.space();
|
||||||
self.definitions.forEach(function(def, i) {
|
self.definitions.forEach(function(def, i) {
|
||||||
@@ -1161,7 +1138,8 @@ function OutputStream(options) {
|
|||||||
node.print(output, parens);
|
node.print(output, parens);
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPRINT(AST_VarDef, function(self, output) {
|
DEFPRINT(AST_VarDef, function(output) {
|
||||||
|
var self = this;
|
||||||
self.name.print(output);
|
self.name.print(output);
|
||||||
if (self.value) {
|
if (self.value) {
|
||||||
output.space();
|
output.space();
|
||||||
@@ -1185,18 +1163,19 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
DEFPRINT(AST_Call, function(self, output) {
|
DEFPRINT(AST_Call, function(output) {
|
||||||
self.expression.print(output);
|
this.expression.print(output);
|
||||||
print_call_args(self, output);
|
print_call_args(this, output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_New, function(self, output) {
|
DEFPRINT(AST_New, function(output) {
|
||||||
|
var self = this;
|
||||||
output.print("new");
|
output.print("new");
|
||||||
output.space();
|
output.space();
|
||||||
self.expression.print(output);
|
self.expression.print(output);
|
||||||
if (need_constructor_parens(self, output)) print_call_args(self, output);
|
if (need_constructor_parens(self, output)) print_call_args(self, output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Sequence, function(self, output) {
|
DEFPRINT(AST_Sequence, function(output) {
|
||||||
self.expressions.forEach(function(node, index) {
|
this.expressions.forEach(function(node, index) {
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
output.comma();
|
output.comma();
|
||||||
if (output.should_break()) {
|
if (output.should_break()) {
|
||||||
@@ -1207,7 +1186,8 @@ function OutputStream(options) {
|
|||||||
node.print(output);
|
node.print(output);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Dot, function(self, output) {
|
DEFPRINT(AST_Dot, function(output) {
|
||||||
|
var self = this;
|
||||||
var expr = self.expression;
|
var expr = self.expression;
|
||||||
expr.print(output);
|
expr.print(output);
|
||||||
var prop = self.property;
|
var prop = self.property;
|
||||||
@@ -1228,35 +1208,38 @@ function OutputStream(options) {
|
|||||||
output.print_name(prop);
|
output.print_name(prop);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Sub, function(self, output) {
|
DEFPRINT(AST_Sub, function(output) {
|
||||||
self.expression.print(output);
|
this.expression.print(output);
|
||||||
output.print("[");
|
output.print("[");
|
||||||
self.property.print(output);
|
this.property.print(output);
|
||||||
output.print("]");
|
output.print("]");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_UnaryPrefix, function(self, output) {
|
DEFPRINT(AST_UnaryPrefix, function(output) {
|
||||||
var op = self.operator;
|
var op = this.operator;
|
||||||
|
var exp = this.expression;
|
||||||
output.print(op);
|
output.print(op);
|
||||||
if (/^[a-z]/i.test(op)
|
if (/^[a-z]/i.test(op)
|
||||||
|| (/[+-]$/.test(op)
|
|| (/[+-]$/.test(op)
|
||||||
&& self.expression instanceof AST_UnaryPrefix
|
&& exp instanceof AST_UnaryPrefix
|
||||||
&& /^[+-]/.test(self.expression.operator))) {
|
&& /^[+-]/.test(exp.operator))) {
|
||||||
output.space();
|
output.space();
|
||||||
}
|
}
|
||||||
self.expression.print(output);
|
exp.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_UnaryPostfix, function(self, output) {
|
DEFPRINT(AST_UnaryPostfix, function(output) {
|
||||||
self.expression.print(output);
|
this.expression.print(output);
|
||||||
output.print(self.operator);
|
output.print(this.operator);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Binary, function(self, output) {
|
DEFPRINT(AST_Binary, function(output) {
|
||||||
|
var self = this;
|
||||||
self.left.print(output);
|
self.left.print(output);
|
||||||
output.space();
|
output.space();
|
||||||
output.print(self.operator);
|
output.print(self.operator);
|
||||||
output.space();
|
output.space();
|
||||||
self.right.print(output);
|
self.right.print(output);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Conditional, function(self, output) {
|
DEFPRINT(AST_Conditional, function(output) {
|
||||||
|
var self = this;
|
||||||
self.condition.print(output);
|
self.condition.print(output);
|
||||||
output.space();
|
output.space();
|
||||||
output.print("?");
|
output.print("?");
|
||||||
@@ -1268,10 +1251,10 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
|
|
||||||
/* -----[ literals ]----- */
|
/* -----[ literals ]----- */
|
||||||
DEFPRINT(AST_Array, function(self, output) {
|
DEFPRINT(AST_Array, function(output) {
|
||||||
output.with_square(function() {
|
var a = this.elements, len = a.length;
|
||||||
var a = self.elements, len = a.length;
|
output.with_square(len > 0 ? function() {
|
||||||
if (len > 0) output.space();
|
output.space();
|
||||||
a.forEach(function(exp, i) {
|
a.forEach(function(exp, i) {
|
||||||
if (i) output.comma();
|
if (i) output.comma();
|
||||||
exp.print(output);
|
exp.print(output);
|
||||||
@@ -1281,12 +1264,13 @@ function OutputStream(options) {
|
|||||||
if (i === len - 1 && exp instanceof AST_Hole)
|
if (i === len - 1 && exp instanceof AST_Hole)
|
||||||
output.comma();
|
output.comma();
|
||||||
});
|
});
|
||||||
if (len > 0) output.space();
|
output.space();
|
||||||
});
|
} : noop);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Object, function(self, output) {
|
DEFPRINT(AST_Object, function(output) {
|
||||||
if (self.properties.length > 0) output.with_block(function() {
|
var props = this.properties;
|
||||||
self.properties.forEach(function(prop, i) {
|
if (props.length > 0) output.with_block(function() {
|
||||||
|
props.forEach(function(prop, i) {
|
||||||
if (i) {
|
if (i) {
|
||||||
output.print(",");
|
output.print(",");
|
||||||
output.newline();
|
output.newline();
|
||||||
@@ -1296,7 +1280,7 @@ function OutputStream(options) {
|
|||||||
});
|
});
|
||||||
output.newline();
|
output.newline();
|
||||||
});
|
});
|
||||||
else print_braced_empty(self, output);
|
else print_braced_empty(this, output);
|
||||||
});
|
});
|
||||||
|
|
||||||
function print_property_name(key, quote, output) {
|
function print_property_name(key, quote, output) {
|
||||||
@@ -1315,47 +1299,48 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFPRINT(AST_ObjectKeyVal, function(self, output) {
|
DEFPRINT(AST_ObjectKeyVal, function(output) {
|
||||||
|
var self = this;
|
||||||
print_property_name(self.key, self.quote, output);
|
print_property_name(self.key, self.quote, output);
|
||||||
output.colon();
|
output.colon();
|
||||||
self.value.print(output);
|
self.value.print(output);
|
||||||
});
|
});
|
||||||
AST_ObjectProperty.DEFMETHOD("_print_getter_setter", function(type, output) {
|
function print_accessor(type) {
|
||||||
output.print(type);
|
return function(output) {
|
||||||
output.space();
|
var self = this;
|
||||||
print_property_name(this.key.name, this.quote, output);
|
output.print(type);
|
||||||
this.value._do_print(output, true);
|
output.space();
|
||||||
});
|
print_property_name(self.key.name, self.quote, output);
|
||||||
DEFPRINT(AST_ObjectSetter, function(self, output) {
|
self.value._codegen(output, true);
|
||||||
self._print_getter_setter("set", output);
|
};
|
||||||
});
|
}
|
||||||
DEFPRINT(AST_ObjectGetter, function(self, output) {
|
DEFPRINT(AST_ObjectGetter, print_accessor("get"));
|
||||||
self._print_getter_setter("get", output);
|
DEFPRINT(AST_ObjectSetter, print_accessor("set"));
|
||||||
});
|
DEFPRINT(AST_Symbol, function(output) {
|
||||||
DEFPRINT(AST_Symbol, function(self, output) {
|
var def = this.definition();
|
||||||
var def = self.definition();
|
output.print_name(def && def.mangled_name || this.name);
|
||||||
output.print_name(def && def.mangled_name || self.name);
|
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Hole, noop);
|
DEFPRINT(AST_Hole, noop);
|
||||||
DEFPRINT(AST_This, function(self, output) {
|
DEFPRINT(AST_This, function(output) {
|
||||||
output.print("this");
|
output.print("this");
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Constant, function(self, output) {
|
DEFPRINT(AST_Constant, function(output) {
|
||||||
output.print(self.value);
|
output.print(this.value);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_String, function(self, output) {
|
DEFPRINT(AST_String, function(output) {
|
||||||
output.print_string(self.value, self.quote);
|
output.print_string(this.value, this.quote);
|
||||||
});
|
});
|
||||||
DEFPRINT(AST_Number, function(self, output) {
|
DEFPRINT(AST_Number, function(output) {
|
||||||
if (use_asm && self.start && self.start.raw != null) {
|
var start = this.start;
|
||||||
output.print(self.start.raw);
|
if (use_asm && start && start.raw != null) {
|
||||||
|
output.print(start.raw);
|
||||||
} else {
|
} else {
|
||||||
output.print(make_num(self.value));
|
output.print(make_num(this.value));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
DEFPRINT(AST_RegExp, function(self, output) {
|
DEFPRINT(AST_RegExp, function(output) {
|
||||||
var regexp = self.value;
|
var regexp = this.value;
|
||||||
var str = regexp.toString();
|
var str = regexp.toString();
|
||||||
var end = str.lastIndexOf("/");
|
var end = str.lastIndexOf("/");
|
||||||
if (regexp.raw_source) {
|
if (regexp.raw_source) {
|
||||||
@@ -1389,7 +1374,7 @@ function OutputStream(options) {
|
|||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
var p = output.parent();
|
var p = output.parent();
|
||||||
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === self)
|
if (p instanceof AST_Binary && /^in/.test(p.operator) && p.left === this)
|
||||||
output.print(" ");
|
output.print(" ");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -110,7 +110,6 @@ function mangle_properties(ast, options) {
|
|||||||
cache: null,
|
cache: null,
|
||||||
debug: false,
|
debug: false,
|
||||||
keep_quoted: false,
|
keep_quoted: false,
|
||||||
only_cache: false,
|
|
||||||
regex: null,
|
regex: null,
|
||||||
reserved: null,
|
reserved: null,
|
||||||
}, true);
|
}, true);
|
||||||
@@ -213,7 +212,6 @@ function mangle_properties(ast, options) {
|
|||||||
|
|
||||||
function can_mangle(name) {
|
function can_mangle(name) {
|
||||||
if (unmangleable[name]) return false;
|
if (unmangleable[name]) return false;
|
||||||
if (options.only_cache) return cache.has(name);
|
|
||||||
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
|
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
48
lib/scope.js
48
lib/scope.js
@@ -96,8 +96,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
|
|
||||||
// pass 1: setup scope chaining and handle definitions
|
// pass 1: setup scope chaining and handle definitions
|
||||||
var self = this;
|
var self = this;
|
||||||
var scope = self.parent_scope = null;
|
|
||||||
var defun = null;
|
var defun = null;
|
||||||
|
var next_def_id = 0;
|
||||||
|
var scope = self.parent_scope = null;
|
||||||
var tw = new TreeWalker(function(node, descend) {
|
var tw = new TreeWalker(function(node, descend) {
|
||||||
if (node instanceof AST_Catch) {
|
if (node instanceof AST_Catch) {
|
||||||
var save_scope = scope;
|
var save_scope = scope;
|
||||||
@@ -149,7 +150,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
scope.def_variable(node).defun = defun;
|
scope.def_variable(node).defun = defun;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
self.next_def_id = 0;
|
self.make_def = function(orig, init) {
|
||||||
|
return new SymbolDef(++next_def_id, this, orig, init);
|
||||||
|
};
|
||||||
self.walk(tw);
|
self.walk(tw);
|
||||||
|
|
||||||
// pass 2: find back references and eval
|
// pass 2: find back references and eval
|
||||||
@@ -240,12 +243,6 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("make_def", function(orig, init) {
|
|
||||||
var top = this;
|
|
||||||
while (top.parent_scope) top = top.parent_scope;
|
|
||||||
return new SymbolDef(++top.next_def_id, this, orig, init);
|
|
||||||
});
|
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("def_global", function(node) {
|
AST_Toplevel.DEFMETHOD("def_global", function(node) {
|
||||||
var globals = this.globals, name = node.name;
|
var globals = this.globals, name = node.name;
|
||||||
if (globals.has(name)) {
|
if (globals.has(name)) {
|
||||||
@@ -259,23 +256,28 @@ AST_Toplevel.DEFMETHOD("def_global", function(node) {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function init_scope_vars(scope, parent) {
|
||||||
|
scope.cname = -1; // the current index for mangling functions/variables
|
||||||
|
scope.enclosed = []; // variables from this or outer scope(s) that are referenced from this or inner scopes
|
||||||
|
scope.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
||||||
|
scope.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
||||||
|
scope.parent_scope = parent; // the parent scope (null if this is the top level)
|
||||||
|
scope.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
||||||
|
scope.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
||||||
|
if (parent) scope.make_def = parent.make_def; // top-level tracking of SymbolDef instances
|
||||||
|
}
|
||||||
|
|
||||||
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
AST_Scope.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
||||||
this.variables = new Dictionary(); // map name to AST_SymbolVar (variables defined in this scope; includes functions)
|
init_scope_vars(this, parent_scope);
|
||||||
this.functions = new Dictionary(); // map name to AST_SymbolDefun (functions defined in this scope)
|
|
||||||
this.uses_with = false; // will be set to true if this or some nested scope uses the `with` statement
|
|
||||||
this.uses_eval = false; // will be set to true if this or nested scope uses the global `eval`
|
|
||||||
this.parent_scope = parent_scope; // the parent scope
|
|
||||||
this.enclosed = []; // a list of variables from this or outer scope(s) that are referenced from this or inner scopes
|
|
||||||
this.cname = -1; // the current index for mangling functions/variables
|
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Lambda.DEFMETHOD("init_scope_vars", function() {
|
AST_Lambda.DEFMETHOD("init_scope_vars", function(parent_scope) {
|
||||||
AST_Scope.prototype.init_scope_vars.apply(this, arguments);
|
init_scope_vars(this, parent_scope);
|
||||||
this.uses_arguments = false;
|
this.uses_arguments = false;
|
||||||
this.def_variable(new AST_SymbolFunarg({
|
this.def_variable(new AST_SymbolFunarg({
|
||||||
name: "arguments",
|
name: "arguments",
|
||||||
start: this.start,
|
start: this.start,
|
||||||
end: this.end
|
end: this.end,
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -387,18 +389,10 @@ AST_Symbol.DEFMETHOD("unmangleable", function(options) {
|
|||||||
// labels are always mangleable
|
// labels are always mangleable
|
||||||
AST_Label.DEFMETHOD("unmangleable", return_false);
|
AST_Label.DEFMETHOD("unmangleable", return_false);
|
||||||
|
|
||||||
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;
|
return this.thedef;
|
||||||
});
|
});
|
||||||
|
|
||||||
AST_Symbol.DEFMETHOD("global", function() {
|
|
||||||
return this.definition().global;
|
|
||||||
});
|
|
||||||
|
|
||||||
function _default_mangler_options(options) {
|
function _default_mangler_options(options) {
|
||||||
options = defaults(options, {
|
options = defaults(options, {
|
||||||
eval : false,
|
eval : false,
|
||||||
@@ -558,8 +552,8 @@ AST_Sequence.DEFMETHOD("tail_node", function() {
|
|||||||
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
|
AST_Toplevel.DEFMETHOD("compute_char_frequency", function(options) {
|
||||||
options = _default_mangler_options(options);
|
options = _default_mangler_options(options);
|
||||||
base54.reset();
|
base54.reset();
|
||||||
|
var fn = AST_Symbol.prototype.add_source_map;
|
||||||
try {
|
try {
|
||||||
var fn = AST_Symbol.prototype.add_source_map;
|
|
||||||
AST_Symbol.prototype.add_source_map = function() {
|
AST_Symbol.prototype.add_source_map = function() {
|
||||||
if (!this.unmangleable(options)) base54.consider(this.name, -1);
|
if (!this.unmangleable(options)) base54.consider(this.name, -1);
|
||||||
};
|
};
|
||||||
|
|||||||
52
lib/utils.js
52
lib/utils.js
@@ -112,51 +112,29 @@ function return_this() { return this; }
|
|||||||
function return_null() { return null; }
|
function return_null() { return null; }
|
||||||
|
|
||||||
var List = (function() {
|
var List = (function() {
|
||||||
function List(a, f, backwards) {
|
function List(a, f) {
|
||||||
var ret = [], top = [], i;
|
var ret = [];
|
||||||
function doit() {
|
for (var i = 0; i < a.length; i++) {
|
||||||
var val = f(a[i], i);
|
var val = f(a[i], i);
|
||||||
var is_last = val instanceof Last;
|
if (val === skip) continue;
|
||||||
if (is_last) val = val.v;
|
if (val instanceof Splice) {
|
||||||
if (val instanceof AtTop) {
|
ret.push.apply(ret, val.v);
|
||||||
val = val.v;
|
|
||||||
if (val instanceof Splice) {
|
|
||||||
top.push.apply(top, backwards ? val.v.slice().reverse() : val.v);
|
|
||||||
} else {
|
|
||||||
top.push(val);
|
|
||||||
}
|
|
||||||
} else if (val !== skip) {
|
|
||||||
if (val instanceof Splice) {
|
|
||||||
ret.push.apply(ret, backwards ? val.v.slice().reverse() : val.v);
|
|
||||||
} else {
|
|
||||||
ret.push(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return is_last;
|
|
||||||
}
|
|
||||||
if (Array.isArray(a)) {
|
|
||||||
if (backwards) {
|
|
||||||
for (i = a.length; --i >= 0;) if (doit()) break;
|
|
||||||
ret.reverse();
|
|
||||||
top.reverse();
|
|
||||||
} else {
|
} else {
|
||||||
for (i = 0; i < a.length; ++i) if (doit()) break;
|
ret.push(val);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
for (i in a) if (HOP(a, i)) if (doit()) break;
|
|
||||||
}
|
}
|
||||||
return top.concat(ret);
|
return ret;
|
||||||
}
|
}
|
||||||
List.is_op = function(val) {
|
List.is_op = function(val) {
|
||||||
return val === skip || val instanceof AtTop || val instanceof Last || val instanceof Splice;
|
return val === skip || val instanceof Splice;
|
||||||
|
};
|
||||||
|
List.splice = function(val) {
|
||||||
|
return new Splice(val);
|
||||||
};
|
};
|
||||||
List.at_top = function(val) { return new AtTop(val); };
|
|
||||||
List.splice = function(val) { return new Splice(val); };
|
|
||||||
List.last = function(val) { return new Last(val); };
|
|
||||||
var skip = List.skip = {};
|
var skip = List.skip = {};
|
||||||
function AtTop(val) { this.v = val; }
|
function Splice(val) {
|
||||||
function Splice(val) { this.v = val; }
|
this.v = val;
|
||||||
function Last(val) { this.v = val; }
|
}
|
||||||
return List;
|
return List;
|
||||||
})();
|
})();
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"version": "3.10.2",
|
"version": "3.10.4",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -8538,3 +8538,25 @@ issue_4051: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "undefined"
|
expect_stdout: "undefined"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4070: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function f() {
|
||||||
|
function g() {}
|
||||||
|
g.p++;
|
||||||
|
return f.p = g.p;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function f() {
|
||||||
|
function g() {}
|
||||||
|
return f.p = ++g.p;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "NaN"
|
||||||
|
}
|
||||||
|
|||||||
@@ -2848,3 +2848,60 @@ issue_4025: {
|
|||||||
"1 1 1",
|
"1 1 1",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forin_var_1: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var k;
|
||||||
|
for (k in [ 1, 2 ])
|
||||||
|
console.log(k);
|
||||||
|
for (k in { PASS: 3 })
|
||||||
|
console.log(k);
|
||||||
|
console.log(k);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
for (var k in [ 1, 2 ])
|
||||||
|
console.log(k);
|
||||||
|
for (k in { PASS: 3 })
|
||||||
|
console.log(k);
|
||||||
|
console.log(k);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"0",
|
||||||
|
"1",
|
||||||
|
"PASS",
|
||||||
|
"PASS",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
forin_var_2: {
|
||||||
|
options = {
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
switch (0) {
|
||||||
|
case function() {
|
||||||
|
for (a in 0);
|
||||||
|
}:
|
||||||
|
var b = 0;
|
||||||
|
}
|
||||||
|
for (var c = 0; a;);
|
||||||
|
var a;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
switch (0) {
|
||||||
|
case function() {
|
||||||
|
for (a in 0);
|
||||||
|
}:
|
||||||
|
}
|
||||||
|
for (; a;);
|
||||||
|
var a;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|||||||
@@ -2869,3 +2869,42 @@ issue_4035: {
|
|||||||
"true",
|
"true",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4067: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function(a) {
|
||||||
|
(function(b) {
|
||||||
|
b[0] += 0;
|
||||||
|
console.log(+a);
|
||||||
|
})(a);
|
||||||
|
})([]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(a) {
|
||||||
|
(function(b) {
|
||||||
|
b[0] += 0;
|
||||||
|
console.log(+a);
|
||||||
|
})(a);
|
||||||
|
})([]);
|
||||||
|
}
|
||||||
|
expect_stdout: "NaN"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4077: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log((a = []) - (a[0]++, 1) || "PASS");
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log((a = []) - (a[0]++, 1) || "PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1483,8 +1483,7 @@ issue_2663_2: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
(function() {
|
(function() {
|
||||||
var i;
|
for (var i in { a: 1, b: 2, c: 3 })
|
||||||
for (i in { a: 1, b: 2, c: 3 })
|
|
||||||
j = i, console.log(j);
|
j = i, console.log(j);
|
||||||
var j;
|
var j;
|
||||||
})();
|
})();
|
||||||
|
|||||||
@@ -277,8 +277,8 @@ join_object_assignments_forin: {
|
|||||||
}
|
}
|
||||||
expect: {
|
expect: {
|
||||||
console.log(function() {
|
console.log(function() {
|
||||||
var o = { a: "PASS" };
|
var o = { a: "PASS" }, a;
|
||||||
for (var a in o)
|
for (a in o)
|
||||||
return o[a];
|
return o[a];
|
||||||
}());
|
}());
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -756,7 +756,37 @@ empty_for_in_side_effects: {
|
|||||||
expect_warnings: [
|
expect_warnings: [
|
||||||
"WARN: Dropping unused variable b [test/compress/loops.js:4,16]",
|
"WARN: Dropping unused variable b [test/compress/loops.js:4,16]",
|
||||||
"INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
|
"INFO: Dropping unused loop variable a [test/compress/loops.js:1,17]",
|
||||||
"WARN: Side effects in object of for-in loop [test/compress/loops.js:1,17]",
|
"WARN: Side effects in object of for-in loop [test/compress/loops.js:2,17]",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
empty_for_in_prop_init: {
|
||||||
|
options = {
|
||||||
|
loops: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function f() {
|
||||||
|
var a = "bar";
|
||||||
|
for ((a, f)[a] in console.log("foo"));
|
||||||
|
return a;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
var a = "bar";
|
||||||
|
console.log("foo");
|
||||||
|
return a;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"foo",
|
||||||
|
"bar",
|
||||||
|
]
|
||||||
|
expect_warnings: [
|
||||||
|
"INFO: Dropping unused loop variable f [test/compress/loops.js:3,21]",
|
||||||
|
"WARN: Side effects in object of for-in loop [test/compress/loops.js:3,30]",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -933,3 +963,128 @@ issue_3634_2: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "1"
|
expect_stdout: "1"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4075: {
|
||||||
|
options = {
|
||||||
|
loops: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "FAIL";
|
||||||
|
(function() {
|
||||||
|
for (a in { PASS: 0 });
|
||||||
|
})()
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "FAIL";
|
||||||
|
(function() {
|
||||||
|
for (a in { PASS: 0 });
|
||||||
|
})()
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4082: {
|
||||||
|
options = {
|
||||||
|
keep_fargs: "strict",
|
||||||
|
loops: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = "PASS";
|
||||||
|
(function(a) {
|
||||||
|
for (a in "foo")
|
||||||
|
var b;
|
||||||
|
})();
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = "PASS";
|
||||||
|
(function(a) {
|
||||||
|
for (a in "foo");
|
||||||
|
})();
|
||||||
|
console.log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4084: {
|
||||||
|
options = {
|
||||||
|
keep_fargs: "strict",
|
||||||
|
loops: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
function f(a) {
|
||||||
|
var b = a++;
|
||||||
|
for (a in "foo");
|
||||||
|
}
|
||||||
|
f();
|
||||||
|
return typeof a;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
(function() {
|
||||||
|
0;
|
||||||
|
})();
|
||||||
|
return typeof a;
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4091_1: {
|
||||||
|
options = {
|
||||||
|
loops: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
for (var e in 42);
|
||||||
|
}
|
||||||
|
console.log(e && e);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
var e;
|
||||||
|
}
|
||||||
|
console.log(e && e);
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4091_2: {
|
||||||
|
options = {
|
||||||
|
loops: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
for (e in 42);
|
||||||
|
var e;
|
||||||
|
}
|
||||||
|
console.log(e && e);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
throw "FAIL";
|
||||||
|
} catch (e) {
|
||||||
|
var e;
|
||||||
|
}
|
||||||
|
console.log(e && e);
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|||||||
@@ -637,6 +637,22 @@ evaluate_7_unsafe_math: {
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
evaluate_8_unsafe_math: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
unsafe_math: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = [ "42" ];
|
||||||
|
console.log(a * (1 / 7));
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = [ "42" ];
|
||||||
|
console.log(+a / 7);
|
||||||
|
}
|
||||||
|
expect_stdout: "6"
|
||||||
|
}
|
||||||
|
|
||||||
NaN_redefined: {
|
NaN_redefined: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
|
|||||||
@@ -877,7 +877,7 @@ for_init_var: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
forin: {
|
forin_1: {
|
||||||
options = {
|
options = {
|
||||||
sequences: true,
|
sequences: true,
|
||||||
}
|
}
|
||||||
@@ -895,6 +895,49 @@ forin: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
forin_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
sequences: true,
|
||||||
|
toplevel: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var o = {
|
||||||
|
p: 1,
|
||||||
|
q: 2,
|
||||||
|
};
|
||||||
|
var k = "k";
|
||||||
|
for ((console.log("exp"), o)[function() {
|
||||||
|
console.log("prop");
|
||||||
|
return k;
|
||||||
|
}()] in function() {
|
||||||
|
console.log("obj");
|
||||||
|
return o;
|
||||||
|
}())
|
||||||
|
console.log(o.k, o[o.k]);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var o = {
|
||||||
|
p: 1,
|
||||||
|
q: 2,
|
||||||
|
};
|
||||||
|
for ((console.log("exp"), o)[console.log("prop"), "k"] in console.log("obj"), o)
|
||||||
|
console.log(o.k, o[o.k]);
|
||||||
|
}
|
||||||
|
expect_stdout: [
|
||||||
|
"obj",
|
||||||
|
"exp",
|
||||||
|
"prop",
|
||||||
|
"p 1",
|
||||||
|
"exp",
|
||||||
|
"prop",
|
||||||
|
"q 2",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
call: {
|
call: {
|
||||||
options = {
|
options = {
|
||||||
sequences: true,
|
sequences: true,
|
||||||
@@ -1112,3 +1155,25 @@ issue_3703: {
|
|||||||
}
|
}
|
||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4079: {
|
||||||
|
options = {
|
||||||
|
sequences: true,
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
typeof (0, A);
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
A;
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,9 +1,3 @@
|
|||||||
var o = this;
|
UNUSED: {
|
||||||
|
console.log(0 - .1 - .1 - .1);
|
||||||
for (var k in o) L17060: {
|
|
||||||
a++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var a;
|
|
||||||
|
|
||||||
console.log(k);
|
|
||||||
|
|||||||
@@ -1,15 +1,12 @@
|
|||||||
// (beautified)
|
// (beautified)
|
||||||
var o = this;
|
console.log(0 - 1 - .1 - .1);
|
||||||
|
// output: -1.2000000000000002
|
||||||
for (var k in o) {}
|
|
||||||
|
|
||||||
var a;
|
|
||||||
|
|
||||||
console.log(k);
|
|
||||||
// output: a
|
|
||||||
//
|
//
|
||||||
// minify: k
|
// minify: -1.2
|
||||||
//
|
//
|
||||||
// options: {
|
// options: {
|
||||||
|
// "compress": {
|
||||||
|
// "unsafe_math": true
|
||||||
|
// },
|
||||||
// "mangle": false
|
// "mangle": false
|
||||||
// }
|
// }
|
||||||
@@ -24,6 +24,9 @@ describe("test/reduce.js", function() {
|
|||||||
});
|
});
|
||||||
it("Should eliminate unreferenced labels", function() {
|
it("Should eliminate unreferenced labels", function() {
|
||||||
var result = reduce_test(read("test/input/reduce/label.js"), {
|
var result = reduce_test(read("test/input/reduce/label.js"), {
|
||||||
|
compress: {
|
||||||
|
unsafe_math: true,
|
||||||
|
},
|
||||||
mangle: false,
|
mangle: false,
|
||||||
}, {
|
}, {
|
||||||
verbose: false,
|
verbose: false,
|
||||||
|
|||||||
@@ -112,19 +112,18 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
// no structural AST changes before this point.
|
// no structural AST changes before this point.
|
||||||
if (node.start._permute >= REPLACEMENTS.length) return;
|
if (node.start._permute >= REPLACEMENTS.length) return;
|
||||||
|
|
||||||
if (parent instanceof U.AST_Assign
|
// ignore lvalues
|
||||||
&& parent.left === node
|
if (parent instanceof U.AST_Assign && parent.left === node) return;
|
||||||
|| parent instanceof U.AST_Unary
|
if (parent instanceof U.AST_Unary && parent.expression === node) switch (parent.operator) {
|
||||||
&& parent.expression === node
|
case "++":
|
||||||
&& ["++", "--", "delete"].indexOf(parent.operator) >= 0) {
|
case "--":
|
||||||
// ignore lvalues
|
case "delete":
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if ((parent instanceof U.AST_For || parent instanceof U.AST_ForIn)
|
// preserve for (var xxx; ...)
|
||||||
&& parent.init === node && node instanceof U.AST_Var) {
|
if (parent instanceof U.AST_For && parent.init === node && node instanceof U.AST_Var) return node;
|
||||||
// preserve for (var ...)
|
// preserve for (xxx in ...)
|
||||||
return node;
|
if (parent instanceof U.AST_ForIn && parent.init === node) return node;
|
||||||
}
|
|
||||||
|
|
||||||
// node specific permutations with no parent logic
|
// node specific permutations with no parent logic
|
||||||
|
|
||||||
@@ -452,6 +451,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
node.start = JSON.parse(JSON.stringify(node.start));
|
node.start = JSON.parse(JSON.stringify(node.start));
|
||||||
node.start._permute = 0;
|
node.start._permute = 0;
|
||||||
}));
|
}));
|
||||||
|
var before_iterations = testcase;
|
||||||
for (var c = 0; c < max_iterations; ++c) {
|
for (var c = 0; c < max_iterations; ++c) {
|
||||||
if (verbose && pass == 1 && c % 25 == 0) {
|
if (verbose && pass == 1 && c % 25 == 0) {
|
||||||
log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes");
|
log("// reduce test pass " + pass + ", iteration " + c + ": " + testcase.length + " bytes");
|
||||||
@@ -494,7 +494,7 @@ module.exports = function reduce_test(testcase, minify_options, reduce_options)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (c == 0) break;
|
if (before_iterations === testcase) break;
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
|
log("// reduce test pass " + pass + ": " + testcase.length + " bytes");
|
||||||
}
|
}
|
||||||
@@ -557,7 +557,7 @@ function try_beautify(testcase, minify_options, expected, result_cache, timeout)
|
|||||||
code: testcase,
|
code: testcase,
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
var actual = run_code(result.code, toplevel, result_cache, timeout);
|
var actual = run_code(result.code, toplevel, result_cache, timeout).result;
|
||||||
if (!sandbox.same_stdout(expected, actual)) return {
|
if (!sandbox.same_stdout(expected, actual)) return {
|
||||||
code: testcase,
|
code: testcase,
|
||||||
};
|
};
|
||||||
@@ -650,7 +650,15 @@ function wrap_with_console_log(node) {
|
|||||||
|
|
||||||
function run_code(code, toplevel, result_cache, timeout) {
|
function run_code(code, toplevel, result_cache, timeout) {
|
||||||
var key = crypto.createHash("sha1").update(code).digest("base64");
|
var key = crypto.createHash("sha1").update(code).digest("base64");
|
||||||
return result_cache[key] || (result_cache[key] = sandbox.run_code(code, toplevel, timeout));
|
var value = result_cache[key];
|
||||||
|
if (!value) {
|
||||||
|
var start = Date.now();
|
||||||
|
result_cache[key] = value = {
|
||||||
|
result: sandbox.run_code(code, toplevel, timeout),
|
||||||
|
elapsed: Date.now() - start,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
function compare_run_code(code, minify_options, result_cache, max_timeout) {
|
function compare_run_code(code, minify_options, result_cache, max_timeout) {
|
||||||
@@ -658,21 +666,19 @@ function compare_run_code(code, minify_options, result_cache, max_timeout) {
|
|||||||
if (minified.error) return minified;
|
if (minified.error) return minified;
|
||||||
|
|
||||||
var toplevel = sandbox.has_toplevel(minify_options);
|
var toplevel = sandbox.has_toplevel(minify_options);
|
||||||
var elapsed = Date.now();
|
var unminified = run_code(code, toplevel, result_cache, max_timeout);
|
||||||
var unminified_result = run_code(code, toplevel, result_cache, max_timeout);
|
var timeout = Math.min(100 * unminified.elapsed, max_timeout);
|
||||||
elapsed = Date.now() - elapsed;
|
var minified_result = run_code(minified.code, toplevel, result_cache, timeout).result;
|
||||||
var timeout = Math.min(100 * elapsed, max_timeout);
|
|
||||||
var minified_result = run_code(minified.code, toplevel, result_cache, timeout);
|
|
||||||
|
|
||||||
if (sandbox.same_stdout(unminified_result, minified_result)) {
|
if (sandbox.same_stdout(unminified.result, minified_result)) {
|
||||||
return is_timed_out(unminified_result) && is_timed_out(minified_result) && {
|
return is_timed_out(unminified.result) && is_timed_out(minified_result) && {
|
||||||
timed_out: true,
|
timed_out: true,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
unminified_result: unminified_result,
|
unminified_result: unminified.result,
|
||||||
minified_result: minified_result,
|
minified_result: minified_result,
|
||||||
elapsed: elapsed,
|
elapsed: unminified.elapsed,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ process.on("beforeExit", function() {
|
|||||||
if (queued > 3) {
|
if (queued > 3) {
|
||||||
process.stdout.write("0");
|
process.stdout.write("0");
|
||||||
} else if (now - earliest > 0 && total > 1) {
|
} else if (now - earliest > 0 && total > 1) {
|
||||||
process.stdout.write(Math.min(20 * (now - earliest) / (total - 1), 6300000).toFixed(0));
|
process.stdout.write(Math.min(20 * (now - earliest) / (total - 1), 18000000).toFixed(0));
|
||||||
} else {
|
} else {
|
||||||
process.stdout.write("3600000");
|
process.stdout.write("3600000");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -496,11 +496,16 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
|||||||
var label = createLabel(canBreak, canContinue);
|
var label = createLabel(canBreak, canContinue);
|
||||||
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||||
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
|
||||||
var optElementVar = "";
|
var key = rng(10) ? "key" + loop : getVarName();
|
||||||
if (rng(5) > 1) {
|
return [
|
||||||
optElementVar = "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[key" + loop + "]; ";
|
"{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ",
|
||||||
}
|
label.target + " for (",
|
||||||
return "{var expr" + loop + " = " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; " + label.target + " for (var key" + loop + " in expr" + loop + ") {" + optElementVar + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}}";
|
/^key/.test(key) ? "var " : "",
|
||||||
|
key + " in expr" + loop + ") {",
|
||||||
|
rng(5) > 1 ? "c = 1 + c; var " + createVarName(MANDATORY) + " = expr" + loop + "[" + key + "]; " : "",
|
||||||
|
createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
|
||||||
|
"}}",
|
||||||
|
].join("");
|
||||||
case STMT_SEMI:
|
case STMT_SEMI:
|
||||||
return use_strict && rng(20) === 0 ? '"use strict";' : ";";
|
return use_strict && rng(20) === 0 ? '"use strict";' : ";";
|
||||||
case STMT_EXPR:
|
case STMT_EXPR:
|
||||||
|
|||||||
Reference in New Issue
Block a user