operator: "+",
expression: make_ref(exp, fixed)
}),
- right: make_node(AST_Number, node, {
- value: 1
- })
+ right: make_node(AST_Number, node, { value: 1 }),
});
};
d.fixed.assigns = fixed && fixed.assigns ? fixed.assigns.slice() : [];
exp.fixed = function() {
return make_node(AST_UnaryPrefix, node, {
operator: "+",
- expression: make_ref(exp, fixed)
+ expression: make_ref(exp, fixed),
});
};
exp.fixed.assigns = fixed && fixed.assigns;
var fixed = this.definition().fixed;
if (fixed) {
if (this.fixed) fixed = this.fixed;
- return fixed instanceof AST_Node ? fixed : fixed();
+ return (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
}
fixed = fixed === 0 && this.fixed;
if (!fixed) return fixed;
- var value = fixed instanceof AST_Node ? fixed : fixed();
+ var value = (fixed instanceof AST_Node ? fixed : fixed()).tail_node();
return value.is_constant() && value;
});
function merge_sequence(array, node) {
if (node instanceof AST_Sequence) {
- array.push.apply(array, node.expressions);
+ [].push.apply(array, node.expressions);
} else {
array.push(node);
}
return true;
}
+ // Certain combination of unused name + side effect leads to invalid AST:
+ // https://github.com/mishoo/UglifyJS/issues/44
+ // https://github.com/mishoo/UglifyJS/issues/1838
+ // https://github.com/mishoo/UglifyJS/issues/3371
+ // We fix it at this stage by moving the `var` outside the `for`.
+ function patch_for_init(node, in_list) {
+ var block;
+ if (node.init instanceof AST_BlockStatement) {
+ block = node.init;
+ node.init = block.body.pop();
+ block.body.push(node);
+ }
+ if (node.init instanceof AST_Defun) {
+ if (!block) block = make_node(AST_BlockStatement, node, { body: [ node ] });
+ block.body.splice(-1, 0, node.init);
+ node.init = null;
+ } else if (node.init instanceof AST_SimpleStatement) {
+ node.init = node.init.body;
+ } else if (is_empty(node.init)) {
+ node.init = null;
+ }
+ if (!block) return;
+ return in_list ? List.splice(block.body) : block;
+ }
+
function tighten_body(statements, compressor) {
var in_lambda = last_of(compressor, function(node) {
return node instanceof AST_Lambda;
var def = candidate.name.definition();
if (def.references.length - def.replaced == 1 && !compressor.exposed(def)) {
def.replaced++;
- return maintain_this_binding(compressor, parent, node, candidate.value);
+ return maintain_this_binding(compressor, parent, node, rvalue);
}
return make_node(AST_Assign, candidate, {
operator: "=",
left: make_node(AST_SymbolRef, candidate.name, candidate.name),
- right: candidate.value,
+ right: rvalue,
});
}
var assign = candidate;
if (!(assign instanceof AST_Assign)) break;
assign = assign.right;
}
- return candidate;
+ assign = candidate.clone();
+ assign.right = rvalue;
+ return assign;
}
// These node types have child nodes that execute sequentially,
// but are otherwise not safe to scan into or beyond them.
}
// Skip (non-executed) functions and (leading) default case in switch statements
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
- }, patch_sequence);
+ }, function(node) {
+ return patch_sequence(node, multi_replacer);
+ });
while (--stat_index >= 0) {
// Treat parameters as collapsible in IIFE, i.e.
// function(a, b){ ... }(x());
var well_defined = true;
var lvalues = get_lvalues(candidate);
var lhs_local = is_lhs_local(lhs);
- var rvalue = get_rvalue(candidate);
+ var rhs_value = get_rvalue(candidate);
+ var rvalue;
+ if (rhs_value instanceof AST_Sequence
+ && !(candidate instanceof AST_Assign && candidate.operator != "=")) {
+ rvalue = rhs_value.tail_node();
+ } else {
+ rvalue = rhs_value;
+ }
if (!side_effects) side_effects = value_has_side_effects();
var check_destructured = in_try || !lhs_local ? function(node) {
return node instanceof AST_Destructured;
value_def.single_use = false;
changed = true;
}
- if (replaced && !remove_candidate(candidate)) statements.splice(stat_index, 1);
+ if (replaced) remove_candidate(candidate);
}
}
return changed;
return;
}
if (remaining < 1) return;
+ rhs = rhs.tail_node();
var value = rhs instanceof AST_Assign && rhs.operator == "=" ? rhs.left : rhs;
if (!(value instanceof AST_SymbolRef)) return;
var def = value.definition();
}
function remove_candidate(expr) {
+ var value = rvalue === rhs_value ? null : make_sequence(rhs_value, rhs_value.expressions.slice(0, -1));
var index = expr.name_index;
if (index >= 0) {
var argname = scope.argnames[index];
if (argname instanceof AST_DefaultValue) {
- argname.value = make_node(AST_Number, argname, {
- value: 0
- });
+ argname.value = value || make_node(AST_Number, argname, { value: 0 });
argname.name.definition().fixed = false;
} else {
var args = compressor.parent().args;
if (args[index]) {
- args[index] = make_node(AST_Number, args[index], {
- value: 0
- });
+ args[index] = value || make_node(AST_Number, args[index], { value: 0 });
argname.definition().fixed = false;
}
}
- return true;
+ return;
}
var end = hit_stack.length - 1;
if (hit_stack[end - 1].body === hit_stack[end]) end--;
if (value_def) value_def.replaced++;
node = node.clone();
node.value = null;
- return node;
+ return value ? List.splice([ value, node ]) : node;
+ }
+ if (!value) return in_list ? List.skip : null;
+ return is_statement(node) ? make_node(AST_SimpleStatement, value, { body: value }) : value;
+ }, function(node, in_list) {
+ if (node instanceof AST_Definitions) {
+ var body = [], defns = node.definitions;
+ for (var index = 0, pos = 0; index < defns.length; index++) {
+ var defn = defns[index];
+ if (defn instanceof AST_VarDef) continue;
+ flush();
+ pos = index + 1;
+ body.push(make_node(AST_SimpleStatement, defn, { body: defn }));
+ }
+ if (pos == 0) return;
+ flush();
+ if (body.length == 1) return body[0];
+ return in_list ? List.splice(body) : make_node(AST_BlockStatement, node, { body: body });
+ }
+ if (node instanceof AST_For) return patch_for_init(node, in_list);
+ return patch_sequence(node, this);
+
+ function flush() {
+ if (pos < index) {
+ var cropped = node.clone();
+ cropped.definitions = defns.slice(pos, index);
+ body.push(cropped);
+ }
}
- return in_list ? List.skip : null;
- }, patch_sequence);
+ });
abort = false;
hit = false;
hit_index = 0;
- return statements[stat_index].transform(tt);
+ if (!(statements[stat_index] = statements[stat_index].transform(tt))) statements.splice(stat_index, 1);
}
- function patch_sequence(node) {
+ function patch_sequence(node, tt) {
if (node instanceof AST_Sequence) switch (node.expressions.length) {
case 0: return null;
- case 1: return maintain_this_binding(compressor, this.parent(), node, node.expressions[0]);
+ case 1: return maintain_this_binding(compressor, tt.parent(), node, node.expressions[0]);
}
}
} else if (stat instanceof AST_If) {
stat.condition = join_assigns_expr(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
- var exprs = join_assigns(prev, stat.body);
+ var exprs = join_assigns(prev, stat.body), next;
if (exprs) {
changed = true;
if (!exprs.length) continue;
stat.body = make_sequence(stat.body, exprs);
+ } else if (prev instanceof AST_Definitions
+ && (next = statements[i + 1])
+ && prev.TYPE == next.TYPE
+ && (next = next.definitions[0]).value) {
+ changed = true;
+ next.value = make_sequence(stat, [ stat.body, next.value ]);
+ continue;
}
} else if (stat instanceof AST_Switch) {
stat.expression = join_assigns_expr(stat.expression);
value = null;
trim_defns.push(def);
}
- var old_def;
+ var old_def, fn;
if (!value && !(node instanceof AST_Let)) {
if (parent instanceof AST_ExportDeclaration) {
flush();
} else if (compressor.option("functions")
&& !compressor.option("ie")
&& drop_sym
+ && value
&& var_defs[sym.id] == 1
&& sym.assignments == 0
- && value instanceof AST_LambdaExpression
+ && (fn = value.tail_node()) instanceof AST_LambdaExpression
&& !is_arguments(sym)
- && !is_arrow(value)
- && assigned_once(value, sym.references)
- && can_declare_defun(value)
- && (old_def = rename_def(value, def.name.name)) !== false) {
+ && !is_arrow(fn)
+ && assigned_once(fn, sym.references)
+ && can_declare_defun(fn)
+ && (old_def = rename_def(fn, def.name.name)) !== false) {
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
var ctor;
- switch (value.CTOR) {
+ switch (fn.CTOR) {
case AST_AsyncFunction:
ctor = AST_AsyncDefun;
break;
ctor = AST_GeneratorDefun;
break;
}
- var defun = make_node(ctor, def, value);
+ var defun = make_node(ctor, def, fn);
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
var name_def = def.name.scope.resolve().def_function(defun.name);
if (old_def) old_def.forEach(function(node) {
node.reference();
});
body.push(defun);
+ if (value !== fn) [].push.apply(side_effects, value.expressions.slice(0, -1));
} else {
if (drop_sym
&& var_defs[sym.id] > 1
head.push(def);
}
} else {
- value = value && !value.single_use && value.drop_side_effect_free(compressor);
+ value = value && value.drop_side_effect_free(compressor);
if (value) {
AST_Node.warn("Side effects in initialization of unused variable {name} [{file}:{line},{col}]", template(def.name));
side_effects.push(value);
}
}
default:
+ var seq;
+ if (tail.length > 0 && (seq = tail[0].value) instanceof AST_Sequence) {
+ tail[0].value = seq.tail_node();
+ body.push(make_node(AST_SimpleStatement, node, {
+ body: make_sequence(seq, seq.expressions.slice(0, -1)),
+ }));
+ }
node.definitions = head.concat(tail);
body.push(node);
}
if (side_effects.length > 0) {
- body.push(make_node(AST_SimpleStatement, node, {
- body: make_sequence(node, side_effects)
- }));
+ body.push(make_node(AST_SimpleStatement, node, { body: make_sequence(node, side_effects) }));
}
return insert_statements(body, node, in_list);
}
}
}, function(node, in_list) {
if (node instanceof AST_BlockStatement) return trim_block(node, tt.parent(), in_list);
- // Certain combination of unused name + side effect leads to invalid AST:
- // https://github.com/mishoo/UglifyJS/issues/44
- // https://github.com/mishoo/UglifyJS/issues/1838
- // https://github.com/mishoo/UglifyJS/issues/3371
- // We fix it at this stage by moving the `var` outside the `for`.
- if (node instanceof AST_For) {
- var block;
- if (node.init instanceof AST_BlockStatement) {
- block = node.init;
- node.init = block.body.pop();
- block.body.push(node);
- }
- if (node.init instanceof AST_Defun) {
- if (!block) {
- block = make_node(AST_BlockStatement, node, {
- body: [ node ]
- });
- }
- block.body.splice(-1, 0, node.init);
- node.init = null;
- } else if (node.init instanceof AST_SimpleStatement) {
- node.init = node.init.body;
- } else if (is_empty(node.init)) {
- node.init = null;
- }
- return !block ? node : in_list ? List.splice(block.body) : block;
- }
+ if (node instanceof AST_For) return patch_for_init(node, in_list);
if (node instanceof AST_ForIn) {
if (!drop_vars || !compressor.option("loops")) return;
if (!is_empty(node.body)) return;
var body = [], var_defs = [], refs = [];
var body_exprs = sequencesize(self.body, body, var_defs, refs);
var alt_exprs = sequencesize(self.alternative, body, var_defs, refs);
- if (body_exprs && alt_exprs) {
+ if (body_exprs instanceof AST_BlockStatement || alt_exprs instanceof AST_BlockStatement) {
+ if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
+ body.push(self);
+ if (body_exprs instanceof AST_BlockStatement) self.body = body_exprs;
+ if (alt_exprs instanceof AST_BlockStatement) self.alternative = alt_exprs;
+ return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
+ } else if (body_exprs && alt_exprs) {
if (var_defs.length > 0) body.push(make_node(AST_Var, self, { definitions: var_defs }));
if (body_exprs.length == 0) {
body.push(make_node(AST_SimpleStatement, self.condition, {
body: alt_exprs.length > 0 ? make_node(AST_Binary, self, {
- operator : "||",
- left : self.condition,
- right : make_sequence(self.alternative, alt_exprs)
- }).transform(compressor) : self.condition.clone()
+ operator: "||",
+ left: self.condition,
+ right: make_sequence(self.alternative, alt_exprs),
+ }).transform(compressor) : self.condition.clone(),
}).optimize(compressor));
} else if (alt_exprs.length == 0) {
if (self_condition_length === negated_length && !negated_is_best
}
body.push(make_node(AST_SimpleStatement, self, {
body: make_node(AST_Binary, self, {
- operator : negated_is_best ? "||" : "&&",
- left : negated_is_best ? negated : self.condition,
- right : make_sequence(self.body, body_exprs)
- }).transform(compressor)
+ operator: negated_is_best ? "||" : "&&",
+ left: negated_is_best ? negated : self.condition,
+ right: make_sequence(self.body, body_exprs),
+ }).transform(compressor),
}).optimize(compressor));
} else {
body.push(make_node(AST_SimpleStatement, self, {
body: make_node(AST_Conditional, self, {
- condition : self.condition,
- consequent : make_sequence(self.body, body_exprs),
- alternative : make_sequence(self.alternative, alt_exprs)
- })
+ condition: self.condition,
+ consequent: make_sequence(self.body, body_exprs),
+ alternative: make_sequence(self.alternative, alt_exprs),
+ }),
}).optimize(compressor));
}
refs.forEach(function(ref) {
ref.definition().references.push(ref);
});
- return make_node(AST_BlockStatement, self, {
- body: body
- }).optimize(compressor);
+ return make_node(AST_BlockStatement, self, { body: body }).optimize(compressor);
}
- if (is_empty(self.body)) {
- self = make_node(AST_If, self, {
- condition: negated,
- body: self.alternative,
- alternative: null
- });
- }
- if (self.body instanceof AST_Exit
- && self.alternative instanceof AST_Exit
- && self.body.TYPE == self.alternative.TYPE) {
+ if (is_empty(self.body)) self = make_node(AST_If, self, {
+ condition: negated,
+ body: self.alternative,
+ alternative: null,
+ });
+ if (self.alternative instanceof AST_Exit && self.body.TYPE == self.alternative.TYPE) {
var exit = make_node(self.body.CTOR, self, {
value: make_node(AST_Conditional, self, {
- condition : self.condition,
- consequent : self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
- alternative : self.alternative.value || make_node(AST_Undefined, self.alternative).transform(compressor)
- })
+ condition: self.condition,
+ consequent: self.body.value || make_node(AST_Undefined, self.body).transform(compressor),
+ alternative: self.alternative.value
+ || make_node(AST_Undefined, self.alternative).transform(compressor),
+ }),
});
- if (exit instanceof AST_Return) {
- exit.in_bool = self.body.in_bool || self.alternative.in_bool;
- }
+ if (exit instanceof AST_Return) exit.in_bool = self.body.in_bool || self.alternative.in_bool;
return exit;
}
- if (self.body instanceof AST_If
- && !self.body.alternative
- && !self.alternative) {
+ if (self.body instanceof AST_If && !self.body.alternative && !self.alternative) {
self = make_node(AST_If, self, {
condition: make_node(AST_Binary, self.condition, {
operator: "&&",
left: self.condition,
- right: self.body.condition
+ right: self.body.condition,
}),
body: self.body.body,
- alternative: null
+ alternative: null,
});
}
- if (aborts(self.body)) {
- if (self.alternative) {
- var alt = self.alternative;
- self.alternative = null;
- return make_node(AST_BlockStatement, self, {
- body: [ self, alt ]
- }).optimize(compressor);
- }
+ if (aborts(self.body) && self.alternative) {
+ var alt = self.alternative;
+ self.alternative = null;
+ return make_node(AST_BlockStatement, self, { body: [ self, alt ] }).optimize(compressor);
}
if (aborts(self.alternative)) {
var body = self.body;
self.body = self.alternative;
self.condition = negated_is_best ? negated : self.condition.negate(compressor);
self.alternative = null;
- return make_node(AST_BlockStatement, self, {
- body: [ self, body ]
- }).optimize(compressor);
+ return make_node(AST_BlockStatement, self, { body: [ self, body ] }).optimize(compressor);
}
if (compressor.option("typeofs")) mark_locally_defined(self.condition, self.body, self.alternative);
return self;
var exprs = [];
for (var i = 0; i < stat.body.length; i++) {
var line = stat.body[i];
+ if (line instanceof AST_EmptyStatement) continue;
+ if (line instanceof AST_Exit) {
+ if (exprs.length == 0) return;
+ line = line.clone();
+ exprs.push(line.value || make_node(AST_Undefined, line).transform(compressor));
+ line.value = make_sequence(stat, exprs);
+ var block = stat.clone();
+ block.body = block.body.slice(i + 1);
+ block.body.unshift(line);
+ return block;
+ }
if (line instanceof AST_LambdaDefinition) {
defuns.push(line);
- } else if (line instanceof AST_EmptyStatement) {
- continue;
} else if (line instanceof AST_SimpleStatement) {
if (!compressor.option("sequences") && exprs.length > 0) return;
exprs.push(line.body);
args[pos++] = make_sequence(call, side_effects);
side_effects = [];
} else {
- args[pos++] = make_node(AST_Number, args[i], {
- value: 0
- });
+ args[pos++] = make_node(AST_Number, args[i], { value: 0 });
continue;
}
}
for (var i = 0; i < len; i++) {
var line = fn.body[i];
if (line instanceof AST_Var) {
- var assigned = var_assigned || !declarations_only(line);
- if (assigned) {
+ if (var_assigned) {
+ if (!stat) continue;
+ if (!(stat instanceof AST_SimpleStatement)) return false;
+ if (!declarations_only(line)) stat = null;
+ } else if (!declarations_only(line)) {
+ if (stat && !(stat instanceof AST_SimpleStatement)) return false;
+ stat = null;
var_assigned = true;
- if (stat) return false;
}
} else if (line instanceof AST_AsyncDefun
|| line instanceof AST_Defun
function flatten_vars(decls, expressions) {
var args = [ insert, 0 ];
- var decl_var = [], expr_var = [], expr_loop = [];
+ var decl_var = [], expr_var = [], expr_loop = [], exprs = [];
for (var i = 0; i < fn.body.length; i++) {
var stat = fn.body[i];
if (stat instanceof AST_LambdaDefinition) {
}
continue;
}
- if (!(stat instanceof AST_Var)) continue;
+ if (!(stat instanceof AST_Var)) {
+ if (stat instanceof AST_SimpleStatement) exprs.push(stat.body);
+ continue;
+ }
for (var j = 0; j < stat.definitions.length; j++) {
var var_def = stat.definitions[j];
var name = flatten_var(var_def.name);
- append_var(decl_var, expr_var, name, var_def.value);
+ var value = var_def.value;
+ if (value && exprs.length > 0) {
+ exprs.push(value);
+ value = make_sequence(var_def, exprs);
+ exprs = [];
+ }
+ append_var(decl_var, expr_var, name, value);
if (in_loop && !arg_used.has(name.name)) {
var def = fn.variables.get(name.name);
var sym = make_node(AST_SymbolRef, name, name);
}
function pop_seq(node) {
- if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, {
- value: 0
- });
+ if (!(node instanceof AST_Sequence)) return make_node(AST_Number, node, { value: 0 });
return make_sequence(node, node.expressions.slice(0, -1));
}
});
var exp = self.expression.expression;
if (is_undeclared_ref(exp)) switch (exp.name) {
case "Array":
- self.expression = make_node(AST_Array, self.expression, {
- elements: []
- });
+ self.expression = make_node(AST_Array, self.expression, { elements: [] });
break;
case "Function":
self.expression = make_node(AST_Function, self.expression, {
argnames: [],
- body: []
+ body: [],
}).init_vars(exp.scope);
break;
case "Number":
- self.expression = make_node(AST_Number, self.expression, {
- value: 0
- });
+ self.expression = make_node(AST_Number, self.expression, { value: 0 });
break;
case "Object":
- self.expression = make_node(AST_Object, self.expression, {
- properties: []
- });
+ self.expression = make_node(AST_Object, self.expression, { properties: [] });
break;
case "RegExp":
- self.expression = make_node(AST_RegExp, self.expression, {
- value: /t/
- });
+ self.expression = make_node(AST_RegExp, self.expression, { value: /t/ });
break;
case "String":
- self.expression = make_node(AST_String, self.expression, {
- value: ""
- });
+ self.expression = make_node(AST_String, self.expression, { value: "" });
break;
}
}