Compare commits

..

17 Commits

Author SHA1 Message Date
Alex Lam S.L
ea999b0e92 v3.4.9 2018-08-31 04:28:21 +00:00
Alex Lam S.L
ce7e220de4 fix corner case in conditionals (#3244) 2018-08-30 15:59:05 +08:00
Alex Lam S.L
2bdaca10ae enhance conditionals (#3243) 2018-08-30 01:06:34 +08:00
Alex Lam S.L
aa0029204e fix corner case in reduce_vars (#3241)
fixes #3240
2018-08-29 22:14:25 +08:00
Alex Lam S.L
f352bcec3a fix corner case in collapse_vars (#3239)
fixes #3238
2018-08-29 11:34:34 +08:00
Alex Lam S.L
08514030f4 v3.4.8 2018-08-23 15:27:34 +08:00
Alex Lam S.L
694ca5d045 fix corner case in unused (#3234)
fixes #3233
2018-08-23 06:03:39 +08:00
Alex Lam S.L
57fb58b263 enhance if_return (#3232) 2018-08-21 18:34:16 +08:00
alexlamsl
18c1c9b38a update dependencies
- commander@2.17.1
2018-08-14 17:06:09 +08:00
Alex Lam S.L
5c1ae3662d v3.4.7 2018-08-09 14:47:24 +00:00
Alex Lam S.L
cfebeb2f63 fix corner case in mangle workaround for Safari (#3230)
fixes #3227
2018-08-09 17:34:28 +08:00
Alex Lam S.L
fc78423f1d clean up webkit quirks (#3229) 2018-08-08 16:15:45 +08:00
Alex Lam S.L
2a5277b391 v3.4.6 2018-07-27 11:35:26 +00:00
Alex Lam S.L
d47547dc71 fix corner case in join_vars (#3224) 2018-07-27 19:34:44 +08:00
Alex Lam S.L
304db15a20 fix corner case in ie8 & rename (#3223) 2018-07-26 16:35:43 +08:00
Alex Lam S.L
7cf72b8d66 fix corner case in global_defs (#3218)
fixes #3217
2018-07-19 18:14:36 +08:00
Alex Lam S.L
cea685f8d9 fix corner case in ie8 (#3216)
fixes #3215
2018-07-19 14:45:36 +08:00
23 changed files with 1532 additions and 495 deletions

View File

@@ -118,10 +118,8 @@ var AST_Node = DEFNODE("Node", "start end", {
}
}, null);
AST_Node.warn_function = null;
AST_Node.warn = function(txt, props) {
if (AST_Node.warn_function)
AST_Node.warn_function(string_template(txt, props));
if (AST_Node.warn_function) AST_Node.warn_function(string_template(txt, props));
};
/* -----[ statements ]----- */
@@ -207,8 +205,7 @@ var AST_LabeledStatement = DEFNODE("LabeledStatement", "label", {
var label = node.label;
var def = this.label;
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_LoopControl
&& node.label && node.label.thedef === def) {
if (node instanceof AST_LoopControl && node.label && node.label.thedef === def) {
node.label.thedef = label;
label.references.push(node);
}

View File

@@ -48,51 +48,51 @@ function Compressor(options, false_by_default) {
return new Compressor(options, false_by_default);
TreeTransformer.call(this, this.before, this.after);
this.options = defaults(options, {
arguments : !false_by_default,
booleans : !false_by_default,
collapse_vars : !false_by_default,
comparisons : !false_by_default,
conditionals : !false_by_default,
dead_code : !false_by_default,
directives : !false_by_default,
drop_console : false,
drop_debugger : !false_by_default,
evaluate : !false_by_default,
expression : false,
global_defs : {},
hoist_funs : false,
hoist_props : !false_by_default,
hoist_vars : false,
ie8 : false,
if_return : !false_by_default,
inline : !false_by_default,
join_vars : !false_by_default,
keep_fargs : true,
keep_fnames : false,
keep_infinity : false,
loops : !false_by_default,
negate_iife : !false_by_default,
passes : 1,
properties : !false_by_default,
pure_getters : !false_by_default && "strict",
pure_funcs : null,
reduce_funcs : !false_by_default,
reduce_vars : !false_by_default,
sequences : !false_by_default,
side_effects : !false_by_default,
switches : !false_by_default,
top_retain : null,
toplevel : !!(options && options["top_retain"]),
typeofs : !false_by_default,
unsafe : false,
unsafe_comps : false,
unsafe_Function: false,
unsafe_math : false,
unsafe_proto : false,
unsafe_regexp : false,
arguments : !false_by_default,
booleans : !false_by_default,
collapse_vars : !false_by_default,
comparisons : !false_by_default,
conditionals : !false_by_default,
dead_code : !false_by_default,
directives : !false_by_default,
drop_console : false,
drop_debugger : !false_by_default,
evaluate : !false_by_default,
expression : false,
global_defs : false,
hoist_funs : false,
hoist_props : !false_by_default,
hoist_vars : false,
ie8 : false,
if_return : !false_by_default,
inline : !false_by_default,
join_vars : !false_by_default,
keep_fargs : true,
keep_fnames : false,
keep_infinity : false,
loops : !false_by_default,
negate_iife : !false_by_default,
passes : 1,
properties : !false_by_default,
pure_getters : !false_by_default && "strict",
pure_funcs : null,
reduce_funcs : !false_by_default,
reduce_vars : !false_by_default,
sequences : !false_by_default,
side_effects : !false_by_default,
switches : !false_by_default,
top_retain : null,
toplevel : !!(options && options["top_retain"]),
typeofs : !false_by_default,
unsafe : false,
unsafe_comps : false,
unsafe_Function : false,
unsafe_math : false,
unsafe_proto : false,
unsafe_regexp : false,
unsafe_undefined: false,
unused : !false_by_default,
warnings : false,
unused : !false_by_default,
warnings : false,
}, true);
var global_defs = this.options["global_defs"];
if (typeof global_defs == "object") for (var key in global_defs) {
@@ -149,6 +149,7 @@ merge(Compressor.prototype, {
return false;
},
compress: function(node) {
node = node.resolve_defines(this);
if (this.option("expression")) {
node.process_expression(true);
}
@@ -449,8 +450,7 @@ merge(Compressor.prototype, {
return value instanceof AST_Node && def.fixed.parent_scope === scope;
}
return all(def.orig, function(sym) {
return !(sym instanceof AST_SymbolDefun
|| sym instanceof AST_SymbolLambda);
return !(sym instanceof AST_SymbolDefun || sym instanceof AST_SymbolLambda);
});
}
@@ -1264,6 +1264,9 @@ merge(Compressor.prototype, {
if (node instanceof AST_Exit) {
return side_effects || lhs instanceof AST_PropAccess || may_modify(lhs);
}
if (node instanceof AST_Function) {
return compressor.option("ie8") && node.name && node.name.name in lvalues;
}
if (node instanceof AST_PropAccess) {
return side_effects || node.expression.may_throw_on_access(compressor);
}
@@ -1478,14 +1481,26 @@ merge(Compressor.prototype, {
}
function get_rhs(expr) {
if (!(candidate instanceof AST_Assign && candidate.operator == "=")) return;
return candidate.right;
return candidate instanceof AST_Assign && candidate.operator == "=" && candidate.right;
}
function get_rvalue(expr) {
return expr[expr instanceof AST_Assign ? "right" : "value"];
}
function invariant(expr) {
if (expr instanceof AST_Array) return false;
if (expr instanceof AST_Binary && lazy_op[expr.operator]) {
return invariant(expr.left) && invariant(expr.right);
}
if (expr instanceof AST_Call) return false;
if (expr instanceof AST_Conditional) {
return invariant(expr.consequent) && invariant(expr.alternative);
}
if (expr instanceof AST_Object) return false;
return !expr.has_side_effects(compressor);
}
function foldable(expr) {
if (expr instanceof AST_SymbolRef) {
var value = expr.evaluate(compressor);
@@ -1498,7 +1513,7 @@ merge(Compressor.prototype, {
return rhs_fuzzy_match(expr.evaluate(compressor), rhs_exact_match);
}
if (!(lhs instanceof AST_SymbolRef)) return false;
if (expr.has_side_effects(compressor)) return false;
if (!invariant(expr)) return false;
var circular;
var def = lhs.definition();
expr.walk(new TreeWalker(function(node) {
@@ -1610,10 +1625,7 @@ merge(Compressor.prototype, {
if (def.orig.length == 1 && def.orig[0] instanceof AST_SymbolDefun) return false;
if (def.scope !== scope) return true;
return !all(def.references, function(ref) {
var s = ref.scope;
// "block" scope within AST_Catch
if (s.TYPE == "Scope") s = s.parent_scope;
return s === scope;
return ref.scope.resolve() === scope;
});
}
@@ -1698,6 +1710,19 @@ merge(Compressor.prototype, {
continue;
}
if (ab && !stat.alternative && stat.body instanceof AST_BlockStatement && next instanceof AST_Jump) {
var negated = stat.condition.negate(compressor);
if (negated.print_to_string().length <= stat.condition.print_to_string().length) {
CHANGED = true;
stat = stat.clone();
stat.condition = negated;
statements[j] = stat.body;
stat.body = next;
statements[i] = stat.transform(compressor);
continue;
}
}
var ab = aborts(stat.alternative);
if (can_merge_flow(ab)) {
if (ab.label) {
@@ -1999,7 +2024,7 @@ merge(Compressor.prototype, {
statements.length = n;
}
function join_object_assignments(defn, body) {
function join_assigns(defn, body) {
var exprs;
if (body instanceof AST_Assign) {
exprs = [ body ];
@@ -2009,7 +2034,7 @@ merge(Compressor.prototype, {
if (!exprs) return;
if (defn instanceof AST_Definitions) {
var def = defn.definitions[defn.definitions.length - 1];
if (trim_object_assignments(def.name, def.value, exprs)) return exprs;
if (trim_assigns(def.name, def.value, exprs)) return exprs;
}
for (var i = exprs.length - 1; --i >= 0;) {
var expr = exprs[i];
@@ -2017,12 +2042,12 @@ merge(Compressor.prototype, {
if (expr.operator != "=") continue;
if (!(expr.left instanceof AST_SymbolRef)) continue;
var tail = exprs.slice(i + 1);
if (!trim_object_assignments(expr.left, expr.right, tail)) continue;
if (!trim_assigns(expr.left, expr.right, tail)) continue;
return exprs.slice(0, i + 1).concat(tail);
}
}
function trim_object_assignments(name, value, exprs) {
function trim_assigns(name, value, exprs) {
if (!(value instanceof AST_Object)) return;
var trimmed = false;
do {
@@ -2073,9 +2098,9 @@ merge(Compressor.prototype, {
defs = stat;
}
} else if (stat instanceof AST_Exit) {
stat.value = extract_object_assignments(stat.value);
stat.value = join_assigns_expr(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
var exprs = join_assigns(prev, stat.init);
if (exprs) {
CHANGED = true;
stat.init = exprs.length ? make_sequence(stat.init, exprs) : null;
@@ -2096,11 +2121,11 @@ merge(Compressor.prototype, {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
stat.object = join_assigns_expr(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
stat.condition = join_assigns_expr(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
var exprs = join_assigns(prev, stat.body);
if (exprs) {
CHANGED = true;
if (!exprs.length) continue;
@@ -2108,29 +2133,23 @@ merge(Compressor.prototype, {
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
stat.expression = join_assigns_expr(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
stat.expression = join_assigns_expr(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(value) {
function join_assigns_expr(value) {
statements[++j] = stat;
var exprs = join_object_assignments(prev, value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
return make_sequence(value, exprs);
} else if (value instanceof AST_Sequence) {
return value.tail_node().left;
} else {
return value.left;
}
}
return value;
var exprs = join_assigns(prev, value);
if (!exprs) return value;
CHANGED = true;
var tail = value.tail_node();
if (exprs[exprs.length - 1] !== tail) exprs.push(tail.left);
return make_sequence(value, exprs);
}
}
}
@@ -2410,22 +2429,6 @@ merge(Compressor.prototype, {
}
(function(def) {
AST_Node.DEFMETHOD("resolve_defines", function(compressor) {
if (!compressor.option("global_defs")) return;
var def = this._find_defs(compressor, "");
if (def) {
var node, parent = this, level = 0;
do {
node = parent;
parent = compressor.parent(level++);
} while (parent instanceof AST_PropAccess && parent.expression === node);
if (is_lhs(node, parent)) {
compressor.warn('global_defs ' + this.print_to_string() + ' redefined [{file}:{line},{col}]', this.start);
} else {
return def;
}
}
});
function to_node(value, orig) {
if (value instanceof AST_Node) return make_node(value.CTOR, orig, value);
if (Array.isArray(value)) return make_node(AST_Array, orig, {
@@ -2447,25 +2450,43 @@ merge(Compressor.prototype, {
}
return make_node_from_constant(value, orig);
}
function warn(compressor, node) {
compressor.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start);
}
AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
if (!compressor.option("global_defs")) return this;
this.figure_out_scope({ ie8: compressor.option("ie8") });
return this.transform(new TreeTransformer(function(node) {
var def = node._find_defs(compressor, "");
if (!def) return;
var level = 0, child = node, parent;
while (parent = this.parent(level++)) {
if (!(parent instanceof AST_PropAccess)) break;
if (parent.expression !== child) break;
child = parent;
}
if (is_lhs(child, parent)) {
warn(compressor, node);
return;
}
return def;
}));
});
def(AST_Node, noop);
def(AST_Dot, function(compressor, suffix) {
return this.expression._find_defs(compressor, "." + this.property + suffix);
});
def(AST_SymbolDeclaration, function(compressor) {
if (!this.global()) return;
if (HOP(compressor.option("global_defs"), this.name)) warn(compressor, this);
});
def(AST_SymbolRef, function(compressor, suffix) {
if (!this.global()) return;
var name;
var defines = compressor.option("global_defs");
if (defines && HOP(defines, (name = this.name + suffix))) {
var node = to_node(defines[name], this);
var top = compressor.find_parent(AST_Toplevel);
node.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef) {
node.scope = top;
node.thedef = top.def_global(node);
}
}));
return node;
}
var name = this.name + suffix;
if (HOP(defines, name)) return to_node(defines[name], this);
});
})(function(node, func) {
node.DEFMETHOD("_find_defs", func);
@@ -2704,35 +2725,30 @@ merge(Compressor.prototype, {
if (right === this.right) return this;
var result;
switch (this.operator) {
case "&&" : result = left && right; break;
case "||" : result = left || right; break;
case "|" : result = left | right; break;
case "&" : result = left & right; break;
case "^" : result = left ^ right; break;
case "+" : result = left + right; break;
case "*" : result = left * right; break;
case "/" : result = left / right; break;
case "%" : result = left % right; break;
case "-" : result = left - right; break;
case "<<" : result = left << right; break;
case ">>" : result = left >> right; break;
case ">>>" : result = left >>> right; break;
case "==" : result = left == right; break;
case "===" : result = left === right; break;
case "!=" : result = left != right; break;
case "!==" : result = left !== right; break;
case "<" : result = left < right; break;
case "<=" : result = left <= right; break;
case ">" : result = left > right; break;
case ">=" : result = left >= right; break;
default:
return this;
case "&&" : result = left && right; break;
case "||" : result = left || right; break;
case "|" : result = left | right; break;
case "&" : result = left & right; break;
case "^" : result = left ^ right; break;
case "+" : result = left + right; break;
case "*" : result = left * right; break;
case "/" : result = left / right; break;
case "%" : result = left % right; break;
case "-" : result = left - right; break;
case "<<" : result = left << right; break;
case ">>" : result = left >> right; break;
case ">>>": result = left >>> right; break;
case "==" : result = left == right; break;
case "===": result = left === right; break;
case "!=" : result = left != right; break;
case "!==": result = left !== right; break;
case "<" : result = left < right; break;
case "<=" : result = left <= right; break;
case ">" : result = left > right; break;
case ">=" : result = left >= right; break;
default : return this;
}
if (isNaN(result) && compressor.find_parent(AST_With)) {
// leave original expression as is
return this;
}
return result;
return isNaN(result) && compressor.find_parent(AST_With) ? this : result;
});
def(AST_Conditional, function(compressor, cached, depth) {
var condition = this.condition._eval(compressor, cached, depth);
@@ -2976,19 +2992,21 @@ merge(Compressor.prototype, {
// determine if expression has side effects
(function(def) {
def(AST_Node, return_true);
def(AST_EmptyStatement, return_false);
def(AST_Constant, return_false);
def(AST_This, return_false);
function any(list, compressor) {
for (var i = list.length; --i >= 0;)
if (list[i].has_side_effects(compressor))
return true;
return false;
}
def(AST_Node, return_true);
def(AST_Array, function(compressor) {
return any(this.elements, compressor);
});
def(AST_Assign, return_true);
def(AST_Binary, function(compressor) {
return this.left.has_side_effects(compressor)
|| this.right.has_side_effects(compressor);
});
def(AST_Block, function(compressor) {
return any(this.body, compressor);
});
@@ -3000,19 +3018,24 @@ merge(Compressor.prototype, {
}
return any(this.args, compressor);
});
def(AST_Switch, function(compressor) {
return this.expression.has_side_effects(compressor)
|| any(this.body, compressor);
});
def(AST_Case, function(compressor) {
return this.expression.has_side_effects(compressor)
|| any(this.body, compressor);
});
def(AST_Try, function(compressor) {
return any(this.body, compressor)
|| this.bcatch && this.bcatch.has_side_effects(compressor)
|| this.bfinally && this.bfinally.has_side_effects(compressor);
def(AST_Conditional, function(compressor) {
return this.condition.has_side_effects(compressor)
|| this.consequent.has_side_effects(compressor)
|| this.alternative.has_side_effects(compressor);
});
def(AST_Constant, return_false);
def(AST_Definitions, function(compressor) {
return any(this.definitions, compressor);
});
def(AST_Dot, function(compressor) {
return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor);
});
def(AST_EmptyStatement, return_false);
def(AST_If, function(compressor) {
return this.condition.has_side_effects(compressor)
|| this.body && this.body.has_side_effects(compressor)
@@ -3021,41 +3044,13 @@ merge(Compressor.prototype, {
def(AST_LabeledStatement, function(compressor) {
return this.body.has_side_effects(compressor);
});
def(AST_SimpleStatement, function(compressor) {
return this.body.has_side_effects(compressor);
});
def(AST_Lambda, return_false);
def(AST_Binary, function(compressor) {
return this.left.has_side_effects(compressor)
|| this.right.has_side_effects(compressor);
});
def(AST_Assign, return_true);
def(AST_Conditional, function(compressor) {
return this.condition.has_side_effects(compressor)
|| this.consequent.has_side_effects(compressor)
|| this.alternative.has_side_effects(compressor);
});
def(AST_Unary, function(compressor) {
return unary_side_effects[this.operator]
|| this.expression.has_side_effects(compressor);
});
def(AST_SymbolRef, function(compressor) {
return !this.is_declared(compressor);
});
def(AST_SymbolDeclaration, return_false);
def(AST_Object, function(compressor) {
return any(this.properties, compressor);
});
def(AST_ObjectProperty, function(compressor) {
return this.value.has_side_effects(compressor);
});
def(AST_Array, function(compressor) {
return any(this.elements, compressor);
});
def(AST_Dot, function(compressor) {
return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor);
});
def(AST_Sub, function(compressor) {
return this.expression.may_throw_on_access(compressor)
|| this.expression.has_side_effects(compressor)
@@ -3064,8 +3059,26 @@ merge(Compressor.prototype, {
def(AST_Sequence, function(compressor) {
return any(this.expressions, compressor);
});
def(AST_Definitions, function(compressor) {
return any(this.definitions, compressor);
def(AST_SimpleStatement, function(compressor) {
return this.body.has_side_effects(compressor);
});
def(AST_Switch, function(compressor) {
return this.expression.has_side_effects(compressor)
|| any(this.body, compressor);
});
def(AST_SymbolDeclaration, return_false);
def(AST_SymbolRef, function(compressor) {
return !this.is_declared(compressor);
});
def(AST_This, return_false);
def(AST_Try, function(compressor) {
return any(this.body, compressor)
|| this.bcatch && this.bcatch.has_side_effects(compressor)
|| this.bfinally && this.bfinally.has_side_effects(compressor);
});
def(AST_Unary, function(compressor) {
return unary_side_effects[this.operator]
|| this.expression.has_side_effects(compressor);
});
def(AST_VarDef, function(compressor) {
return this.value;
@@ -3324,13 +3337,14 @@ merge(Compressor.prototype, {
} else if (node instanceof AST_Unary && node.write_only) {
sym = node.expression;
}
if (/strict/.test(compressor.option("pure_getters"))) {
while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) {
if (sym instanceof AST_Sub) props.unshift(sym.property);
sym = sym.expression;
}
if (!/strict/.test(compressor.option("pure_getters"))) return sym instanceof AST_SymbolRef && sym;
while (sym instanceof AST_PropAccess && !sym.expression.may_throw_on_access(compressor)) {
if (sym instanceof AST_Sub) props.unshift(sym.property);
sym = sym.expression;
}
return sym;
return sym instanceof AST_SymbolRef && all(sym.definition().orig, function(sym) {
return !(sym instanceof AST_SymbolLambda);
}) && sym;
};
var in_use = [];
var in_use_ids = Object.create(null); // avoid expensive linear scans of in_use
@@ -3412,12 +3426,20 @@ merge(Compressor.prototype, {
init.walk(tw);
});
}
var drop_fn_name = compressor.option("keep_fnames") ? return_false : compressor.option("ie8") ? function(def) {
return !compressor.exposed(def) && !def.references.length;
} : function(def) {
// any declarations with same name will overshadow
// name of this anonymous function and can therefore
// never be used anywhere
return !(def.id in in_use_ids) || def.orig.length > 1;
};
// pass 3: we should drop declarations not in_use
var tt = new TreeTransformer(function(node, descend, in_list) {
var parent = tt.parent();
if (drop_vars) {
var props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef) {
if (sym) {
var def = sym.definition();
var in_use = def.id in in_use_ids;
var value = null;
@@ -3439,15 +3461,8 @@ merge(Compressor.prototype, {
}
}
if (scope !== self) return;
if (node instanceof AST_Function
&& node.name
&& !compressor.option("ie8")
&& !compressor.option("keep_fnames")) {
var def = node.name.definition();
// any declarations with same name will overshadow
// name of this anonymous function and can therefore
// never be used anywhere
if (!(def.id in in_use_ids) || def.orig.length > 1) node.name = null;
if (node instanceof AST_Function && node.name && drop_fn_name(node.name.definition())) {
node.name = null;
}
if (node instanceof AST_Lambda && !(node instanceof AST_Accessor)) {
var trim = !compressor.option("keep_fargs");
@@ -3623,8 +3638,7 @@ merge(Compressor.prototype, {
function scan_ref_scoped(node, descend) {
var node_def, props = [], sym = assign_as_unused(node, props);
if (sym instanceof AST_SymbolRef
&& self.variables.get(sym.name) === (node_def = sym.definition())) {
if (sym && self.variables.get(sym.name) === (node_def = sym.definition())) {
props.forEach(function(prop) {
prop.walk(tw);
});
@@ -5722,14 +5736,10 @@ merge(Compressor.prototype, {
}
OPT(AST_SymbolRef, function(self, compressor) {
var def = self.resolve_defines(compressor);
if (def) {
return def.optimize(compressor);
}
// testing against !self.scope.uses_with first is an optimization
if (!compressor.option("ie8")
&& is_undeclared_ref(self)
&& (!self.scope.uses_with || !compressor.find_parent(AST_With))) {
// testing against `self.scope.uses_with` is an optimization
&& !(self.scope.uses_with && compressor.find_parent(AST_With))) {
switch (self.name) {
case "undefined":
return make_node(AST_Undefined, self).optimize(compressor);
@@ -5741,37 +5751,34 @@ merge(Compressor.prototype, {
}
var parent = compressor.parent();
if (compressor.option("reduce_vars") && is_lhs(self, parent) !== self) {
var d = self.definition();
var def = self.definition();
var fixed = self.fixed_value();
var single_use = d.single_use
&& !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
var single_use = def.single_use && !(parent instanceof AST_Call && parent.is_expr_pure(compressor));
if (single_use && fixed instanceof AST_Lambda) {
if (d.scope !== self.scope
&& (!compressor.option("reduce_funcs")
|| d.escaped == 1
|| fixed.inlined)) {
if (def.scope !== self.scope
&& (!compressor.option("reduce_funcs") || def.escaped == 1 || fixed.inlined)) {
single_use = false;
} else if (recursive_ref(compressor, d)) {
} else if (recursive_ref(compressor, def)) {
single_use = false;
} else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) {
} else if (def.scope !== self.scope || def.orig[0] instanceof AST_SymbolFunarg) {
single_use = fixed.is_constant_expression(self.scope);
if (single_use == "f") {
var scope = self.scope;
do {
if (scope instanceof AST_Defun || scope instanceof AST_Function) {
scope.inlined = true;
}
do if (scope instanceof AST_Defun || scope instanceof AST_Function) {
scope.inlined = true;
} while (scope = scope.parent_scope);
}
}
}
if (single_use && fixed) {
def.single_use = false;
if (fixed instanceof AST_Defun) {
fixed._squeezed = true;
fixed = make_node(AST_Function, fixed, fixed);
fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
}
var value;
if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) {
if (def.recursive_refs > 0) {
value = fixed.clone(true);
var defun_def = value.name.definition();
var lambda_def = value.variables.get(value.name.name);
@@ -5783,9 +5790,13 @@ merge(Compressor.prototype, {
lambda_def = value.def_function(name);
}
value.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef && node.definition() === defun_def) {
if (!(node instanceof AST_SymbolRef)) return;
var def = node.definition();
if (def === defun_def) {
node.thedef = lambda_def;
lambda_def.references.push(node);
} else {
def.single_use = false;
}
}));
} else {
@@ -5794,13 +5805,12 @@ merge(Compressor.prototype, {
}
return value;
}
if (fixed && d.should_replace === undefined) {
if (fixed && def.should_replace === undefined) {
var init;
if (fixed instanceof AST_This) {
if (!(d.orig[0] instanceof AST_SymbolFunarg)
&& all(d.references, function(ref) {
return d.scope === ref.scope;
})) {
if (!(def.orig[0] instanceof AST_SymbolFunarg) && all(def.references, function(ref) {
return def.scope === ref.scope;
})) {
init = fixed;
}
} else {
@@ -5824,18 +5834,18 @@ merge(Compressor.prototype, {
return result === init || result === fixed ? result.clone(true) : result;
};
}
var name_length = d.name.length;
var name_length = def.name.length;
var overhead = 0;
if (compressor.option("unused") && !compressor.exposed(d)) {
overhead = (name_length + 2 + value_length) / (d.references.length - d.assignments);
if (compressor.option("unused") && !compressor.exposed(def)) {
overhead = (name_length + 2 + value_length) / (def.references.length - def.assignments);
}
d.should_replace = value_length <= name_length + overhead ? fn : false;
def.should_replace = value_length <= name_length + overhead ? fn : false;
} else {
d.should_replace = false;
def.should_replace = false;
}
}
if (d.should_replace) {
return d.should_replace();
if (def.should_replace) {
return def.should_replace();
}
}
return self;
@@ -6037,22 +6047,26 @@ merge(Compressor.prototype, {
// |
// v
// exp = foo ? something : something_else;
if (consequent instanceof AST_Assign
&& alternative instanceof AST_Assign
&& consequent.operator == alternative.operator
&& consequent.left.equivalent_to(alternative.left)
&& (!self.condition.has_side_effects(compressor)
|| consequent.operator == "="
&& !consequent.left.has_side_effects(compressor))) {
return make_node(AST_Assign, self, {
operator: consequent.operator,
left: consequent.left,
right: make_node(AST_Conditional, self, {
condition: self.condition,
consequent: consequent.right,
alternative: alternative.right
})
});
var seq_tail = consequent.tail_node();
if (seq_tail instanceof AST_Assign) {
var is_eq = seq_tail.operator == "=";
var alt_tail = is_eq ? alternative.tail_node() : alternative;
if ((is_eq || consequent instanceof AST_Assign)
&& alt_tail instanceof AST_Assign
&& seq_tail.operator == alt_tail.operator
&& seq_tail.left.equivalent_to(alt_tail.left)
&& (!condition.has_side_effects(compressor)
|| is_eq && !seq_tail.left.has_side_effects(compressor))) {
return make_node(AST_Assign, self, {
operator: seq_tail.operator,
left: seq_tail.left,
right: make_node(AST_Conditional, self, {
condition: condition,
consequent: pop_lhs(consequent),
alternative: pop_lhs(alternative)
})
});
}
}
// x ? y(a) : y(b) --> y(x ? a : b)
var arg_index;
@@ -6061,12 +6075,12 @@ merge(Compressor.prototype, {
&& consequent.args.length > 0
&& consequent.args.length == alternative.args.length
&& consequent.expression.equivalent_to(alternative.expression)
&& !self.condition.has_side_effects(compressor)
&& !condition.has_side_effects(compressor)
&& !consequent.expression.has_side_effects(compressor)
&& typeof (arg_index = single_arg_diff()) == "number") {
var node = consequent.clone();
node.args[arg_index] = make_node(AST_Conditional, self, {
condition: self.condition,
condition: condition,
consequent: consequent.args[arg_index],
alternative: alternative.args[arg_index]
});
@@ -6077,7 +6091,7 @@ merge(Compressor.prototype, {
&& consequent.alternative.equivalent_to(alternative)) {
return make_node(AST_Conditional, self, {
condition: make_node(AST_Binary, self, {
left: self.condition,
left: condition,
operator: "&&",
right: consequent.condition
}),
@@ -6088,7 +6102,7 @@ merge(Compressor.prototype, {
// x ? y : y --> x, y
if (consequent.equivalent_to(alternative)) {
return make_sequence(self, [
self.condition,
condition,
consequent
]).optimize(compressor);
}
@@ -6097,7 +6111,7 @@ merge(Compressor.prototype, {
&& consequent.tail_node().equivalent_to(alternative.tail_node())) {
return make_sequence(self, [
make_node(AST_Conditional, self, {
condition: self.condition,
condition: condition,
consequent: pop_seq(consequent),
alternative: pop_seq(alternative)
}),
@@ -6112,7 +6126,7 @@ merge(Compressor.prototype, {
operator: "||",
left: make_node(AST_Binary, self, {
operator: "&&",
left: self.condition,
left: condition,
right: consequent.left
}),
right: alternative
@@ -6122,24 +6136,24 @@ merge(Compressor.prototype, {
if (is_true(self.consequent)) {
if (is_false(self.alternative)) {
// c ? true : false ---> !!c
return booleanize(self.condition);
return booleanize(condition);
}
// c ? true : x ---> !!c || x
return make_node(AST_Binary, self, {
operator: "||",
left: booleanize(self.condition),
left: booleanize(condition),
right: self.alternative
});
}
if (is_false(self.consequent)) {
if (is_true(self.alternative)) {
// c ? false : true ---> !c
return booleanize(self.condition.negate(compressor));
return booleanize(condition.negate(compressor));
}
// c ? false : x ---> !c && x
return make_node(AST_Binary, self, {
operator: "&&",
left: booleanize(self.condition.negate(compressor)),
left: booleanize(condition.negate(compressor)),
right: self.alternative
});
}
@@ -6147,7 +6161,7 @@ merge(Compressor.prototype, {
// c ? x : true ---> !c || x
return make_node(AST_Binary, self, {
operator: "||",
left: booleanize(self.condition.negate(compressor)),
left: booleanize(condition.negate(compressor)),
right: self.consequent
});
}
@@ -6155,7 +6169,7 @@ merge(Compressor.prototype, {
// c ? x : false ---> !!c && x
return make_node(AST_Binary, self, {
operator: "&&",
left: booleanize(self.condition),
left: booleanize(condition),
right: self.consequent
});
}
@@ -6207,6 +6221,13 @@ merge(Compressor.prototype, {
}
}
function pop_lhs(node) {
if (!(node instanceof AST_Sequence)) return node.right;
var exprs = node.expressions.slice();
exprs.push(exprs.pop().right);
return make_sequence(node, exprs);
}
function pop_seq(node) {
if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
value: 0
@@ -6409,10 +6430,6 @@ merge(Compressor.prototype, {
col: self.start.col
});
}
var def = self.resolve_defines(compressor);
if (def) {
return def.optimize(compressor);
}
if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("unsafe_proto")
&& self.expression instanceof AST_Dot
@@ -6468,14 +6485,6 @@ merge(Compressor.prototype, {
}
return self;
});
OPT(AST_VarDef, function(self, compressor) {
var defines = compressor.option("global_defs");
if (defines && HOP(defines, self.name.name)) {
compressor.warn('global_defs ' + self.name.name + ' redefined [{file}:{line},{col}]', self.start);
}
return self;
});
})(function(node, optimizer) {
node.DEFMETHOD("optimize", function(compressor) {
var self = this;

View File

@@ -694,23 +694,15 @@ function OutputStream(options) {
// a function expression needs parens around it when it's provably
// the first token to appear in a statement.
PARENS(AST_Function, function(output) {
if (!output.has_parens() && first_in_statement(output)) {
return true;
}
if (!output.has_parens() && first_in_statement(output)) return true;
if (output.option('webkit')) {
var p = output.parent();
if (p instanceof AST_PropAccess && p.expression === this) {
return true;
}
if (p instanceof AST_PropAccess && p.expression === this) return true;
}
if (output.option('wrap_iife')) {
var p = output.parent();
return p instanceof AST_Call && p.expression === this;
if (p instanceof AST_Call && p.expression === this) return true;
}
return false;
});
// same goes for an object literal, because otherwise it would be
@@ -784,17 +776,17 @@ function OutputStream(options) {
});
PARENS(AST_Call, function(output) {
var p = output.parent(), p1;
if (p instanceof AST_New && p.expression === this)
return true;
// workaround for Safari bug.
var p = output.parent();
if (p instanceof AST_New && p.expression === this) return true;
// https://bugs.webkit.org/show_bug.cgi?id=123506
return this.expression instanceof AST_Function
&& p instanceof AST_PropAccess
&& p.expression === this
&& (p1 = output.parent(1)) instanceof AST_Assign
&& p1.left === p;
if (output.option('webkit')) {
var g = output.parent(1);
return this.expression instanceof AST_Function
&& p instanceof AST_PropAccess
&& p.expression === this
&& g instanceof AST_Assign
&& g.left === p;
}
});
PARENS(AST_New, function(output) {
@@ -1161,7 +1153,7 @@ function OutputStream(options) {
def.print(output);
});
var p = output.parent();
if (p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
if (p && p.init !== self || !(p instanceof AST_For || p instanceof AST_ForIn)) output.semicolon();
});
function parenthesize_for_noin(node, output, noin) {

View File

@@ -53,25 +53,30 @@ function find_builtins(reserved) {
"-Infinity",
"undefined",
].forEach(add);
[ Object, Array, Function, Number,
String, Boolean, Error, Math,
Date, RegExp
[
Array,
Boolean,
Date,
Error,
Function,
Math,
Number,
Object,
RegExp,
String,
].forEach(function(ctor) {
Object.getOwnPropertyNames(ctor).map(add);
if (ctor.prototype) {
Object.getOwnPropertyNames(ctor.prototype).map(add);
}
});
function add(name) {
push_uniq(reserved, name);
}
}
function reserve_quoted_keys(ast, reserved) {
function add(name) {
push_uniq(reserved, name);
}
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal && node.quote) {
add(node.key);
@@ -79,6 +84,10 @@ function reserve_quoted_keys(ast, reserved) {
addStrings(node.property, add);
}
}));
function add(name) {
push_uniq(reserved, name);
}
}
function addStrings(node, add) {
@@ -127,10 +136,8 @@ function mangle_properties(ast, options) {
// note debug may be enabled as an empty string, which is falsey. Also treat passing 'true'
// the same as passing an empty string.
var debug = options.debug !== false;
var debug_name_suffix;
if (debug) {
debug_name_suffix = (options.debug === true ? "" : options.debug);
}
var debug_suffix;
if (debug) debug_suffix = options.debug === true ? "" : options.debug;
var names_to_mangle = [];
var unmangleable = [];
@@ -139,18 +146,14 @@ function mangle_properties(ast, options) {
ast.walk(new TreeWalker(function(node) {
if (node instanceof AST_ObjectKeyVal) {
add(node.key);
}
else if (node instanceof AST_ObjectProperty) {
} else if (node instanceof AST_ObjectProperty) {
// setter or getter, since KeyVal is handled above
add(node.key.name);
}
else if (node instanceof AST_Dot) {
} else if (node instanceof AST_Dot) {
add(node.property);
}
else if (node instanceof AST_Sub) {
} else if (node instanceof AST_Sub) {
addStrings(node.property, add);
}
else if (node instanceof AST_Call
} else if (node instanceof AST_Call
&& node.expression.print_to_string() == "Object.defineProperty") {
addStrings(node.args[1], add);
}
@@ -160,18 +163,14 @@ function mangle_properties(ast, options) {
return ast.transform(new TreeTransformer(function(node) {
if (node instanceof AST_ObjectKeyVal) {
node.key = mangle(node.key);
}
else if (node instanceof AST_ObjectProperty) {
} else if (node instanceof AST_ObjectProperty) {
// setter or getter
node.key.name = mangle(node.key.name);
}
else if (node instanceof AST_Dot) {
} else if (node instanceof AST_Dot) {
node.property = mangle(node.property);
}
else if (!options.keep_quoted && node instanceof AST_Sub) {
} else if (!options.keep_quoted && node instanceof AST_Sub) {
node.property = mangleStrings(node.property);
}
else if (node instanceof AST_Call
} else if (node instanceof AST_Call
&& node.expression.print_to_string() == "Object.defineProperty") {
node.args[1] = mangleStrings(node.args[1]);
}
@@ -182,9 +181,7 @@ function mangle_properties(ast, options) {
function can_mangle(name) {
if (unmangleable.indexOf(name) >= 0) return false;
if (reserved.indexOf(name) >= 0) return false;
if (options.only_cache) {
return cache.has(name);
}
if (options.only_cache) return cache.has(name);
if (/^-?[0-9]+(\.[0-9]+)?(e[+-][0-9]+)?$/.test(name)) return false;
return true;
}
@@ -192,42 +189,29 @@ function mangle_properties(ast, options) {
function should_mangle(name) {
if (regex && !regex.test(name)) return false;
if (reserved.indexOf(name) >= 0) return false;
return cache.has(name)
|| names_to_mangle.indexOf(name) >= 0;
return cache.has(name) || names_to_mangle.indexOf(name) >= 0;
}
function add(name) {
if (can_mangle(name))
push_uniq(names_to_mangle, name);
if (!should_mangle(name)) {
push_uniq(unmangleable, name);
}
if (can_mangle(name)) push_uniq(names_to_mangle, name);
if (!should_mangle(name)) push_uniq(unmangleable, name);
}
function mangle(name) {
if (!should_mangle(name)) {
return name;
}
var mangled = cache.get(name);
if (!mangled) {
if (debug) {
// debug mode: use a prefix and suffix to preserve readability, e.g. o.foo -> o._$foo$NNN_.
var debug_mangled = "_$" + name + "$" + debug_name_suffix + "_";
if (can_mangle(debug_mangled)) {
mangled = debug_mangled;
}
var debug_mangled = "_$" + name + "$" + debug_suffix + "_";
if (can_mangle(debug_mangled)) mangled = debug_mangled;
}
// either debug mode is off, or it is on and we could not use the mangled name
if (!mangled) {
do {
mangled = base54(++cname);
} while (!can_mangle(mangled));
}
if (!mangled) do {
mangled = base54(++cname);
} while (!can_mangle(mangled));
cache.set(name, mangled);
}
return mangled;
@@ -238,11 +222,9 @@ function mangle_properties(ast, options) {
if (node instanceof AST_Sequence) {
var last = node.expressions.length - 1;
node.expressions[last] = mangleStrings(node.expressions[last]);
}
else if (node instanceof AST_String) {
} else if (node instanceof AST_String) {
node.value = mangle(node.value);
}
else if (node instanceof AST_Conditional) {
} else if (node instanceof AST_Conditional) {
node.consequent = mangleStrings(node.consequent);
node.alternative = mangleStrings(node.alternative);
}

View File

@@ -118,11 +118,10 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
descend();
scope = save_scope;
defun = save_defun;
return true; // don't descend again in TreeWalker
return true;
}
if (node instanceof AST_With) {
for (var s = scope; s; s = s.parent_scope)
s.uses_with = true;
for (var s = scope; s; s = s.parent_scope) s.uses_with = true;
return;
}
if (node instanceof AST_Symbol) {
@@ -132,18 +131,14 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
node.thedef = node;
node.references = [];
}
if (node instanceof AST_SymbolDefun || options.ie8 && node instanceof AST_SymbolLambda) {
// Careful here, the scope where this should be defined is
// the parent scope. The reason is that we enter a new
// scope when we encounter the AST_Defun node (which is
// instanceof AST_Scope) but we get to the symbol a bit
// later.
(node.scope = defun.parent_scope).def_function(node, defun);
}
else if (node instanceof AST_SymbolLambda) {
defun.def_function(node, node.name == "arguments" ? undefined : defun);
}
else if (node instanceof AST_SymbolVar) {
if (node instanceof AST_SymbolDefun) {
// This should be defined in the parent scope, as we encounter the
// AST_Defun node before getting to its AST_Symbol.
(node.scope = defun.parent_scope.resolve()).def_function(node, defun);
} else if (node instanceof AST_SymbolLambda) {
var def = defun.def_function(node, node.name == "arguments" ? undefined : defun);
if (options.ie8) def.defun = defun.parent_scope.resolve();
} else if (node instanceof AST_SymbolVar) {
defun.def_variable(node, node.TYPE == "SymbolVar" ? null : undefined);
if (defun !== scope) {
node.mark_enclosed(options);
@@ -153,8 +148,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
}
node.reference(options);
}
}
else if (node instanceof AST_SymbolCatch) {
} else if (node instanceof AST_SymbolCatch) {
scope.def_variable(node).defun = defun;
}
});
@@ -162,9 +156,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
// pass 2: find back references and eval
self.globals = new Dictionary();
var tw = new TreeWalker(function(node, descend) {
if (node instanceof AST_LoopControl && node.label) {
node.label.thedef.references.push(node);
var tw = new TreeWalker(function(node) {
if (node instanceof AST_LoopControl) {
if (node.label) node.label.thedef.references.push(node);
return true;
}
if (node instanceof AST_SymbolRef) {
@@ -185,35 +179,43 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
return true;
}
// ensure mangling works if catch reuses a scope variable
var def;
if (node instanceof AST_SymbolCatch && (def = node.definition().redefined())) {
var s = node.scope;
while (s) {
if (node instanceof AST_SymbolCatch) {
var def = node.definition().redefined();
if (def) for (var s = node.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, def);
if (s === def.scope) break;
s = s.parent_scope;
}
return true;
}
});
self.walk(tw);
// pass 3: fix up any scoping issue with IE8
if (options.ie8) {
self.walk(new TreeWalker(function(node, descend) {
if (node instanceof AST_SymbolCatch) {
var name = node.name;
var refs = node.thedef.references;
var scope = node.thedef.defun;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) {
ref.thedef = def;
ref.reference(options);
});
node.thedef = def;
node.reference(options);
return true;
if (options.ie8) self.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolCatch) {
redefine(node, node.thedef.defun);
return true;
}
if (node instanceof AST_SymbolLambda) {
var def = node.thedef;
if (def.orig.length == 1) {
redefine(node, node.scope.parent_scope);
node.thedef.init = def.init;
}
}));
return true;
}
}));
function redefine(node, scope) {
var name = node.name;
var refs = node.thedef.references;
var def = scope.find_variable(name) || self.globals.get(name) || scope.def_variable(node);
refs.forEach(function(ref) {
ref.thedef = def;
ref.reference(options);
});
node.thedef = def;
node.reference(options);
}
});
@@ -252,8 +254,7 @@ AST_Lambda.DEFMETHOD("init_scope_vars", function() {
AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
var def = this.definition();
var s = this.scope;
while (s) {
for (var s = this.scope; s; s = s.parent_scope) {
push_uniq(s.enclosed, def);
if (options.keep_fnames) {
s.functions.each(function(d) {
@@ -261,7 +262,6 @@ AST_Symbol.DEFMETHOD("mark_enclosed", function(options) {
});
}
if (s === def.scope) break;
s = s.parent_scope;
}
});
@@ -298,6 +298,12 @@ AST_Scope.DEFMETHOD("def_variable", function(symbol, init) {
return symbol.thedef = def;
});
AST_Lambda.DEFMETHOD("resolve", return_this);
AST_Scope.DEFMETHOD("resolve", function() {
return this.parent_scope;
});
AST_Toplevel.DEFMETHOD("resolve", return_this);
function names_in_use(scope, options) {
var names = scope.names_in_use;
if (!names) {
@@ -314,14 +320,6 @@ function next_mangled_name(scope, options, def) {
var in_use = names_in_use(scope, options);
var holes = scope.cname_holes;
var names = Object.create(null);
// #179, #326
// in Safari strict mode, something like (function x(x){...}) is a syntax error;
// a function expression's argument cannot shadow the function expression's name
if (scope instanceof AST_Function && scope.name && def.orig[0] instanceof AST_SymbolFunarg) {
var tricky_def = scope.name.definition();
// the function's mangled_name is null when keep_fnames is true
names[tricky_def.mangled_name || tricky_def.name] = true;
}
var scopes = [ scope ];
def.references.forEach(function(sym) {
var scope = sym.scope;
@@ -410,7 +408,7 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
var save_nesting = lname;
descend();
lname = save_nesting;
return true; // don't descend again in TreeWalker
return true;
}
if (node instanceof AST_Scope) {
descend();
@@ -422,7 +420,9 @@ AST_Toplevel.DEFMETHOD("mangle_names", function(options) {
}
if (node instanceof AST_Label) {
var name;
do name = base54(++lname); while (!is_identifier(name));
do {
name = base54(++lname);
} while (!is_identifier(name));
node.mangled_name = name;
return true;
}

View File

@@ -172,9 +172,8 @@ function string_template(text, props) {
}
function remove(array, el) {
for (var i = array.length; --i >= 0;) {
if (array[i] === el) array.splice(i, 1);
}
var index = array.indexOf(el);
if (index >= 0) array.splice(index, 1);
}
function makePredicate(words) {

View File

@@ -3,7 +3,7 @@
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
"license": "BSD-2-Clause",
"version": "3.4.5",
"version": "3.4.9",
"engines": {
"node": ">=0.8.0"
},
@@ -23,7 +23,7 @@
"LICENSE"
],
"dependencies": {
"commander": "~2.16.0",
"commander": "~2.17.1",
"source-map": "~0.6.1"
},
"devDependencies": {

View File

@@ -5730,3 +5730,280 @@ issue_3096: {
}
expect_stdout: "ab"
}
issue_3215_1: {
options = {
collapse_vars: true,
evaluate: true,
ie8: false,
inline: true,
passes: 2,
side_effects: true,
unused: true,
}
input: {
console.log(function a() {
var a = 42;
return typeof a;
}());
}
expect: {
console.log("number");
}
expect_stdout: "number"
}
issue_3215_2: {
options = {
collapse_vars: true,
evaluate: true,
ie8: true,
inline: true,
passes: 2,
side_effects: true,
unused: true,
}
input: {
console.log(function a() {
var a = 42;
return typeof a;
}());
}
expect: {
console.log(function a() {
var a = 42;
return typeof a;
}());
}
expect_stdout: "number"
}
issue_3215_3: {
options = {
collapse_vars: true,
evaluate: true,
ie8: false,
inline: true,
passes: 2,
side_effects: true,
unused: true,
}
input: {
console.log(function() {
var a = 42;
(function a() {});
return typeof a;
}());
}
expect: {
console.log("number");
}
expect_stdout: "number"
}
issue_3215_4: {
options = {
collapse_vars: true,
evaluate: true,
ie8: true,
inline: true,
passes: 2,
side_effects: true,
unused: true,
}
input: {
console.log(function() {
var a = 42;
(function a() {});
return typeof a;
}());
}
expect: {
console.log(function() {
var a = 42;
(function a() {});
return typeof a;
}());
}
expect_stdout: "number"
}
issue_3238_1: {
options = {
collapse_vars: true,
unsafe: true,
}
input: {
function f(a) {
var b, c;
if (a) {
b = Object.create(null);
c = Object.create(null);
}
return b === c;
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
var b, c;
if (a) {
b = Object.create(null);
c = Object.create(null);
}
return b === c;
}
console.log(f(0), f(1));
}
expect_stdout: "true false"
}
issue_3238_2: {
options = {
collapse_vars: true,
unsafe: true,
}
input: {
function f(a) {
var b, c;
if (a) {
b = Error();
c = Error();
}
return b === c;
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
var b, c;
if (a) {
b = Error();
c = Error();
}
return b === c;
}
console.log(f(0), f(1));
}
expect_stdout: "true false"
}
issue_3238_3: {
options = {
collapse_vars: true,
unsafe: true,
}
input: {
function f(a) {
var b, c;
if (a) {
b = new Date();
c = new Date();
}
return b === c;
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
var b, c;
if (a) {
b = new Date();
c = new Date();
}
return b === c;
}
console.log(f(0), f(1));
}
expect_stdout: "true false"
}
issue_3238_4: {
options = {
collapse_vars: true,
unsafe: true,
}
input: {
function f(a) {
var b, c;
if (a) {
b = a && {};
c = a && {};
}
return b === c;
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
var b, c;
if (a) {
b = a && {};
c = a && {};
}
return b === c;
}
console.log(f(0), f(1));
}
expect_stdout: "true false"
}
issue_3238_5: {
options = {
collapse_vars: true,
unsafe: true,
}
input: {
function f(a) {
var b, c;
if (a) {
b = a ? [] : 42;
c = a ? [] : 42;
}
return b === c;
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
var b, c;
if (a) {
b = a ? [] : 42;
c = a ? [] : 42;
}
return b === c;
}
console.log(f(0), f(1));
}
expect_stdout: "true false"
}
issue_3238_6: {
options = {
collapse_vars: true,
unsafe: true,
}
input: {
function f(a) {
var b, c;
if (a) {
b = a && 0 || [];
c = a && 0 || [];
}
return b === c;
}
console.log(f(0), f(1));
}
expect: {
function f(a) {
var b, c;
if (a) {
b = a && 0 || [];
c = a && 0 || [];
}
return b === c;
}
console.log(f(0), f(1));
}
expect_stdout: "true false"
}

View File

@@ -1292,3 +1292,95 @@ to_and_or: {
}
expect_stdout: true
}
cond_seq_assign_1: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f(a) {
var t;
if (a) {
t = "foo";
t = "bar";
} else {
console.log(t);
t = 42;
}
console.log(t);
}
f(f);
f();
}
expect: {
function f(a) {
var t;
t = a ? (t = "foo", "bar") : (console.log(t), 42),
console.log(t);
}
f(f),
f();
}
expect_stdout: [
"bar",
"undefined",
"42",
]
}
cond_seq_assign_2: {
options = {
conditionals: true,
sequences: true,
}
input: {
function f(a) {
var t;
if (a) {
t = "foo";
a = "bar";
} else {
console.log(t);
t = 42;
}
console.log(t);
}
f(f);
f();
}
expect: {
function f(a) {
var t;
a ? (t = "foo", a = "bar") : (console.log(t), t = 42),
console.log(t);
}
f(f),
f();
}
expect_stdout: [
"foo",
"undefined",
"42",
]
}
cond_seq_assign_3: {
options = {
conditionals: true,
}
input: {
var c = 0;
if (this)
c = 1 + c, c = c + 1;
else
c = 1 + c, c = c + 1;
console.log(c);
}
expect: {
var c = 0;
this, c = 1 + c, c += 1;
console.log(c);
}
expect_stdout: "2"
}

View File

@@ -1982,3 +1982,26 @@ issue_3192: {
"foo bar",
]
}
issue_3233: {
options = {
pure_getters: "strict",
side_effects: true,
unused: true,
}
input: {
var a = function b() {
b.c = "PASS";
};
a();
console.log(a.c);
}
expect: {
var a = function b() {
b.c = "PASS";
};
a();
console.log(a.c);
}
expect_stdout: "PASS"
}

View File

@@ -236,32 +236,6 @@ issue_203: {
expect_stdout: "42"
}
no_webkit: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
issue_2084: {
options = {
collapse_vars: true,

View File

@@ -142,7 +142,6 @@ mixed: {
}
expect_warnings: [
"WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:4,22]",
"WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:5,22]",
"WARN: global_defs CONFIG.VALUE redefined [test/compress/global_defs.js:7,8]",
]
}
@@ -197,3 +196,23 @@ issue_2167: {
doWork();
}
}
issue_3217: {
options = {
collapse_vars: true,
global_defs: {
"@o": "{fn:function(){var a=42;console.log(a)}}",
},
inline: true,
properties: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
o.fn();
}
expect: {
console.log(42);
}
}

View File

@@ -678,6 +678,7 @@ issue_3206_1: {
input: {
console.log(function() {
var foo = function bar() {};
var baz = function moo() {};
return "function" == typeof bar;
}());
}
@@ -700,6 +701,7 @@ issue_3206_2: {
input: {
console.log(function() {
var foo = function bar() {};
var baz = function moo() {};
return "function" == typeof bar;
}());
}
@@ -711,3 +713,151 @@ issue_3206_2: {
}
expect_stdout: "false"
}
issue_3215_1: {
mangle = {
ie8: false,
}
input: {
console.log(function foo() {
var bar = function bar(name) {
return "PASS";
};
try {
"moo";
} catch (e) {
bar = function bar(name) {
return "FAIL";
};
}
return bar;
}()());
}
expect: {
console.log(function n() {
var o = function n(o) {
return "PASS";
};
try {
"moo";
} catch (n) {
o = function n(o) {
return "FAIL";
};
}
return o;
}()());
}
expect_stdout: "PASS"
}
issue_3215_2: {
mangle = {
ie8: true,
}
input: {
console.log(function foo() {
var bar = function bar(name) {
return "PASS";
};
try {
"moo";
} catch (e) {
bar = function bar(name) {
return "FAIL";
};
}
return bar;
}()());
}
expect: {
console.log(function foo() {
var o = function o(n) {
return "PASS";
};
try {
"moo";
} catch (n) {
o = function o(n) {
return "FAIL";
};
}
return o;
}()());
}
expect_stdout: "PASS"
}
issue_3215_3: {
mangle = {
ie8: false,
}
input: {
console.log(function foo() {
var bar = function bar(name) {
return "FAIL";
};
try {
moo;
} catch (e) {
bar = function bar(name) {
return "PASS";
};
}
return bar;
}()());
}
expect: {
console.log(function n() {
var o = function n(o) {
return "FAIL";
};
try {
moo;
} catch (n) {
o = function n(o) {
return "PASS";
};
}
return o;
}()());
}
expect_stdout: "PASS"
}
issue_3215_4: {
mangle = {
ie8: true,
}
input: {
console.log(function foo() {
var bar = function bar(name) {
return "FAIL";
};
try {
moo;
} catch (e) {
bar = function bar(name) {
return "PASS";
};
}
return bar;
}()());
}
expect: {
console.log(function foo() {
var o = function o(n) {
return "FAIL";
};
try {
moo;
} catch (n) {
o = function o(n) {
return "PASS";
};
}
return o;
}()());
}
expect_stdout: "PASS"
}

View File

@@ -396,3 +396,151 @@ if_if_return_return: {
}
}
}
if_body_return_1: {
options = {
if_return: true,
}
input: {
var c = "PASS";
function f(a, b) {
if (a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect: {
var c = "PASS";
function f(a, b) {
if (a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect_stdout: [
"true",
"true",
"42",
"PASS",
]
}
if_body_return_2: {
options = {
if_return: true,
}
input: {
var c = "PASS";
function f(a, b) {
if (0 + a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect: {
var c = "PASS";
function f(a, b) {
if (0 + a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect_stdout: [
"true",
"true",
"42",
"PASS",
]
}
if_body_return_3: {
options = {
if_return: true,
}
input: {
var c = "PASS";
function f(a, b) {
if (1 == a) {
if (b) throw new Error(c);
return 42;
}
return true;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect: {
var c = "PASS";
function f(a, b) {
if (1 != a) return true;
if (b) throw new Error(c);
return 42;
}
console.log(f(0, 0));
console.log(f(0, 1));
console.log(f(1, 0));
try {
f(1, 1);
console.log("FAIL");
} catch (e) {
console.log(e.message);
}
}
expect_stdout: [
"true",
"true",
"42",
"PASS",
]
}

View File

@@ -1832,3 +1832,33 @@ issue_3188_3: {
}
expect_stdout: "PASS"
}
join_expr: {
options = {
evaluate: true,
join_vars: true,
}
input: {
var c = "FAIL";
(function() {
var a = 0;
switch ((a = {}) && (a.b = 0)) {
case 0:
c = "PASS";
}
})();
console.log(c);
}
expect: {
var c = "FAIL";
(function() {
var a = 0;
switch (a = { b: 0 }, a.b) {
case 0:
c = "PASS";
}
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -5231,11 +5231,11 @@ defun_catch_4: {
try {
throw 42;
} catch (a) {
function a() {}
console.log(a);
}
}
expect_stdout: true
expect_stdout: "42"
node_version: "<=4"
}
defun_catch_5: {
@@ -5257,10 +5257,10 @@ defun_catch_5: {
throw 42;
} catch (a) {
console.log(a);
function a() {}
}
}
expect_stdout: true
expect_stdout: "42"
node_version: "<=4"
}
defun_catch_6: {
@@ -5354,6 +5354,7 @@ issue_2774: {
issue_2799_1: {
options = {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
@@ -6429,3 +6430,159 @@ issue_3140_5: {
}
expect_stdout: "1"
}
issue_3240_1: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
f(1);
function f(a) {
console.log(a);
var g = function() {
f(a - 1);
};
if (a) g();
}
})();
}
expect: {
(function() {
(function f(a) {
console.log(a);
var g = function() {
f(a - 1);
};
if (a) g();
})(1);
})();
}
expect_stdout: [
"1",
"0",
]
}
issue_3240_2: {
options = {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
f(1);
function f(a) {
console.log(a);
var g = function() {
f(a - 1);
};
if (a) g();
}
})();
}
expect: {
(function() {
(function f(a) {
console.log(a);
if (a) (function() {
f(a - 1);
})();
})(1);
})();
}
expect_stdout: [
"1",
"0",
]
}
issue_3240_3: {
options = {
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
f();
function f(b) {
if (!f.a) f.a = 0;
console.log(f.a.toString());
var g = function() {
(b ? function() {} : function() {
f.a++;
f(1);
})();
};
g();
}
})();
}
expect: {
(function() {
(function f(b) {
if (!f.a) f.a = 0;
console.log(f.a.toString());
var g = function() {
(b ? function() {} : function() {
f.a++;
f(1);
})();
};
g();
})();
})();
}
expect_stdout: [
"0",
"1",
]
}
issue_3240_4: {
options = {
passes: 2,
reduce_funcs: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
f();
function f(b) {
if (!f.a) f.a = 0;
console.log(f.a.toString());
var g = function() {
(b ? function() {} : function() {
f.a++;
f(1);
})();
};
g();
}
})();
}
expect: {
(function() {
(function f(b) {
if (!f.a) f.a = 0;
console.log(f.a.toString());
(function() {
(b ? function() {} : function() {
f.a++;
f(1);
})();
})();
})();
})();
}
expect_stdout: [
"0",
"1",
]
}

View File

@@ -534,3 +534,80 @@ function_catch_catch_ie8: {
"undefined",
]
}
function_do_catch_ie8: {
rename = true
options = {
ie8: true,
side_effects: true,
unused: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
var a = 1, b = 1, c = 0;
function d(e) {
var f, g, h, i;
do {
try {
try {
var j = function q(){}();
} catch (r) {
--a && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy");
var k, l, m, n, o;
--m;
--n;
--o;
}
try {
i[1];
} catch (s) {
var p;
switch (function t() {
c++;
}()) {
case j + --p:
}
}
} catch (u) {}
} while (--i);
b--;
}
d();
console.log(b, c);
}
expect: {
var t = 1, u = 1, y = 0;
function c(c) {
var d;
do {
try {
try {
var e = void 0;
} catch (i) {
--t && w("ddddddddeeeeeeegggggggggiiiiilllllllnnnnntuuuuuuuuyyyyyyy");
0;
0;
0;
}
try {
d[1];
} catch (l) {
var g;
switch(function x() {
y++;
}()) {
case e + --g:
}
}
} catch (n) {}
} while (--d);
u--;
}
c();
console.log(u, y);
}
expect_stdout: "0 1"
}

109
test/compress/webkit.js Normal file
View File

@@ -0,0 +1,109 @@
lambda_call_dot_assign: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
return {};
}().a = 1);
}
expect_exact: "console.log(function(){return{}}().a=1);"
expect_stdout: "1"
}
lambda_call_dot_assign_webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
return {};
}().a = 1);
}
expect_exact: "console.log((function(){return{}}()).a=1);"
expect_stdout: "1"
}
lambda_dot_assign: {
beautify = {
webkit: false,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log(function(){1+1}.a=1);"
expect_stdout: "1"
}
lambda_dot_assign_webkit: {
beautify = {
webkit: true,
}
input: {
console.log(function() {
1 + 1;
}.a = 1);
}
expect_exact: "console.log((function(){1+1}).a=1);"
expect_stdout: "1"
}
lambda_name_mangle: {
mangle = {}
input: {
console.log(typeof function foo(bar) {});
}
expect_exact: "console.log(typeof function o(n){});"
expect_stdout: "function"
}
lambda_name_mangle_ie8: {
mangle = {
ie8: true,
toplevel: true,
}
input: {
console.log(typeof function foo(bar) {});
}
expect_exact: "console.log(typeof function n(o){});"
expect_stdout: "function"
}
function_name_mangle: {
options = {
keep_fnames: true,
reduce_vars: true,
unused: true,
}
mangle = {}
input: {
(function() {
function foo(bar) {}
console.log(typeof foo);
})();
}
expect_exact: "(function(){console.log(typeof function o(n){})})();"
expect_stdout: "function"
}
function_name_mangle_ie8: {
options = {
keep_fnames: true,
reduce_vars: true,
unused: true,
}
mangle = {
ie8: true,
toplevel: true,
}
input: {
(function() {
function foo(bar) {}
console.log(typeof foo);
})();
}
expect_exact: "(function(){console.log(typeof function n(o){})})();"
expect_stdout: "function"
}

View File

@@ -23,10 +23,9 @@ module.exports = function(url, callback) {
var options = parse(url);
options.rejectUnauthorized = false;
require(options.protocol.slice(0, -1)).get(options, function(res) {
if (res.statusCode !== 200) return callback(res);
res.pipe(fs.createWriteStream(local(url)).on("close", function() {
callback(null, read(url));
}));
if (res.statusCode !== 200) return callback(res.statusCode);
res.pipe(fs.createWriteStream(local(url)));
callback(null, res);
});
}).on("open", function() {
callback(null, result);

View File

@@ -25,30 +25,35 @@ if (typeof phantom == "undefined") {
request.resume();
var url = site + request.url;
fetch(url, function(err, res) {
if (err) throw err;
response.writeHead(200, {
"Content-Type": {
css: "text/css",
js: "application/javascript",
png: "image/png"
}[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
});
if (/\.js$/.test(url)) {
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
res.pipe(uglifyjs.stdin);
if (err) {
if (typeof err != "number") throw err;
response.writeHead(err);
response.end();
} else {
res.pipe(response);
response.writeHead(200, {
"Content-Type": {
css: "text/css",
js: "application/javascript",
png: "image/png"
}[url.slice(url.lastIndexOf(".") + 1)] || "text/html; charset=utf-8"
});
if (/\.js$/.test(url)) {
var stderr = "";
var uglifyjs = child_process.fork("bin/uglifyjs", args, {
silent: true
}).on("exit", function(code) {
console.log("uglifyjs", url.slice(site.length + 1), args.join(" "));
console.log(stderr);
if (code) throw new Error("uglifyjs failed with code " + code);
});
uglifyjs.stderr.on("data", function(data) {
stderr += data;
}).setEncoding("utf8");
uglifyjs.stdout.pipe(response);
res.pipe(uglifyjs.stdin);
} else {
res.pipe(response);
}
}
});
}).listen();

View File

@@ -651,7 +651,7 @@ describe("bin/uglifyjs", function() {
});
});
it("Should work with explicit --no-rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -mc --no-rename";
var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2 --no-rename";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "function f(n){return function(n){return n}(n)}\n");
@@ -659,7 +659,7 @@ describe("bin/uglifyjs", function() {
});
});
it("Should work with implicit --rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -mc";
var command = uglifyjscmd + " test/input/rename/input.js -mc passes=2";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "function f(n){return n}\n");
@@ -667,7 +667,7 @@ describe("bin/uglifyjs", function() {
});
});
it("Should work with implicit --no-rename", function(done) {
var command = uglifyjscmd + " test/input/rename/input.js -c";
var command = uglifyjscmd + " test/input/rename/input.js -c passes=2";
exec(command, function(err, stdout, stderr) {
if (err) throw err;
assert.strictEqual(stdout, "function f(x){return function(x){return x}(x)}\n");

View File

@@ -1,21 +1,6 @@
var semver = require("semver");
var vm = require("vm");
function createContext() {
return vm.createContext(Object.defineProperty({}, "console", {
value: {
log: function(msg) {
if (arguments.length == 1 && typeof msg == "string") {
return console.log("%s", msg);
}
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
}
}));
}
function safe_log(arg, level) {
if (arg) switch (typeof arg) {
case "function":
@@ -25,21 +10,21 @@ function safe_log(arg, level) {
arg.constructor.toString();
if (level--) for (var key in arg) {
var desc = Object.getOwnPropertyDescriptor(arg, key);
if (!desc || !desc.get) {
arg[key] = safe_log(arg[key], level);
}
if (!desc || !desc.get) arg[key] = safe_log(arg[key], level);
}
}
return arg;
}
function strip_func_ids(text) {
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
function log(msg) {
if (arguments.length == 1 && typeof msg == "string") return console.log("%s", msg);
return console.log.apply(console, [].map.call(arguments, function(arg) {
return safe_log(arg, 3);
}));
}
var context;
var FUNC_TOSTRING = [
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String].forEach(function(f) {",
var func_toString = new vm.Script([
"[ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(f) {",
" f.toString = Function.prototype.toString;",
"});",
"Function.prototype.toString = function() {",
@@ -59,7 +44,15 @@ var FUNC_TOSTRING = [
' return "function(){}";',
" };",
"}();",
]).join("\n");
]).join("\n"));
function createContext() {
var ctx = vm.createContext(Object.defineProperty({}, "console", { value: { log: log } }));
func_toString.runInContext(ctx);
return ctx;
}
var context;
exports.run_code = function(code, reuse) {
var stdout = "";
var original_write = process.stdout.write;
@@ -69,7 +62,6 @@ exports.run_code = function(code, reuse) {
try {
if (!reuse || !context) context = createContext();
vm.runInContext([
FUNC_TOSTRING,
"!function() {",
code,
"}();",
@@ -86,6 +78,11 @@ exports.run_code = function(code, reuse) {
}
}
};
function strip_func_ids(text) {
return ("" + text).replace(/F[0-9]{6}N/g, "<F<>N>");
}
exports.same_stdout = semver.satisfies(process.version, "0.12") ? function(expected, actual) {
if (typeof expected != typeof actual) return false;
if (typeof expected != "string") {

View File

@@ -16,6 +16,7 @@
},
{},
{
"ie8": true,
"toplevel": true
},
{