Merge branch 'master' into harmony-v3.3.8

This commit is contained in:
alexlamsl
2018-01-21 15:35:55 +08:00
26 changed files with 1071 additions and 211 deletions

View File

@@ -267,11 +267,10 @@ var AST_For = DEFNODE("For", "init condition step", {
}
}, AST_IterationStatement);
var AST_ForIn = DEFNODE("ForIn", "init name object", {
var AST_ForIn = DEFNODE("ForIn", "init object", {
$documentation: "A `for ... in` statement",
$propdoc: {
init: "[AST_Node] the `for/in` initialization code",
name: "[AST_SymbolRef?] the loop variable, only if `init` is AST_Var",
object: "[AST_Node] the object that we're looping through"
},
_walk: function(visitor) {
@@ -319,6 +318,13 @@ var AST_Scope = DEFNODE("Scope", "variables functions uses_with uses_eval parent
self = self.parent_scope;
}
return self;
},
clone: function(deep) {
var node = this._clone(deep);
if (this.variables) node.variables = this.variables.clone();
if (this.functions) node.functions = this.functions.clone();
if (this.enclosed) node.enclosed = this.enclosed.slice();
return node;
}
}, AST_Block);
@@ -863,8 +869,8 @@ var AST_Object = DEFNODE("Object", "properties", {
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
$documentation: "Base class for literal object properties",
$propdoc: {
key: "[string|AST_Node] the property name converted to a string for ObjectKeyVal. For setters, getters and computed property this is an arbitrary AST_Node",
value: "[AST_Node] property value. For setters and getters this is an AST_Accessor."
key: "[string|AST_Node] property name. For ObjectKeyVal this is a string. For getters, setters and computed property this is an AST_Node.",
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
},
_walk: function(visitor) {
return visitor._visit(this, function(){

View File

@@ -89,11 +89,12 @@ function Compressor(options, false_by_default) {
unsafe : false,
unsafe_arrows : false,
unsafe_comps : false,
unsafe_Func : false,
unsafe_Function: false,
unsafe_math : false,
unsafe_methods: false,
unsafe_proto : false,
unsafe_regexp : false,
unsafe_undefined: false,
unused : !false_by_default,
warnings : false,
}, true);
@@ -178,7 +179,8 @@ merge(Compressor.prototype, {
node.process_expression(true);
}
var passes = +this.options.passes || 1;
var last_count = 1 / 0;
var min_count = 1 / 0;
var stopping = false;
var mangle = { ie8: this.option("ie8") };
for (var pass = 0; pass < passes; pass++) {
node.figure_out_scope(mangle);
@@ -190,9 +192,15 @@ merge(Compressor.prototype, {
node.walk(new TreeWalker(function() {
count++;
}));
this.info("pass " + pass + ": last_count: " + last_count + ", count: " + count);
if (count >= last_count) break;
last_count = count;
this.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
if (count < min_count) {
min_count = count;
stopping = false;
} else if (stopping) {
break;
} else {
stopping = true;
}
}
}
if (this.option("expression")) {
@@ -411,14 +419,15 @@ merge(Compressor.prototype, {
}
function read_property(obj, key) {
if (key instanceof AST_Constant) key = key.getValue();
if (key instanceof AST_Node) return null;
key = get_value(key);
if (key instanceof AST_Node) return;
var value;
if (obj instanceof AST_Array) {
var elements = obj.elements;
if (key == "length") return make_node_from_constant(elements.length, obj);
if (typeof key == "number" && key in elements) value = elements[key];
} else if (obj instanceof AST_Object) {
key = "" + key;
var props = obj.properties;
for (var i = props.length; --i >= 0;) {
var prop = props[i];
@@ -918,9 +927,8 @@ merge(Compressor.prototype, {
}
function loop_body(x) {
if (x instanceof AST_Switch) return x;
if (x instanceof AST_For || x instanceof AST_ForIn || x instanceof AST_DWLoop) {
return (x.body instanceof AST_BlockStatement ? x.body : x);
if (x instanceof AST_IterationStatement) {
return x.body instanceof AST_BlockStatement ? x.body : x;
}
return x;
};
@@ -964,7 +972,7 @@ merge(Compressor.prototype, {
sequencesize_2(statements, compressor);
}
if (compressor.option("join_vars")) {
join_consecutive_vars(statements, compressor);
join_consecutive_vars(statements);
}
if (compressor.option("collapse_vars")) {
collapse(statements, compressor);
@@ -1281,14 +1289,31 @@ merge(Compressor.prototype, {
expr.definitions.forEach(extract_candidates);
} else if (expr instanceof AST_DWLoop) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_Exit) {
if (expr.value) extract_candidates(expr.value);
} else if (expr instanceof AST_For) {
if (expr.init) extract_candidates(expr.init);
if (expr.condition) extract_candidates(expr.condition);
if (expr.step) extract_candidates(expr.step);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_ForIn) {
extract_candidates(expr.object);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
} else if (expr instanceof AST_If) {
extract_candidates(expr.condition);
if (!(expr.body instanceof AST_Block)) {
extract_candidates(expr.body);
}
if (expr.alternative && !(expr.alternative instanceof AST_Block)) {
extract_candidates(expr.alternative);
}
} else if (expr instanceof AST_Sequence) {
expr.expressions.forEach(extract_candidates);
} else if (expr instanceof AST_SimpleStatement) {
@@ -1315,10 +1340,12 @@ merge(Compressor.prototype, {
if (parent instanceof AST_Call) return node;
if (parent instanceof AST_Case) return node;
if (parent instanceof AST_Conditional) return node;
if (parent instanceof AST_Definitions) return find_stop(parent, level + 1);
if (parent instanceof AST_Exit) return node;
if (parent instanceof AST_If) return node;
if (parent instanceof AST_IterationStatement) return node;
if (parent instanceof AST_Sequence) return find_stop(parent, level + 1);
if (parent instanceof AST_SimpleStatement) return find_stop(parent, level + 1);
if (parent instanceof AST_Switch) return node;
if (parent instanceof AST_VarDef) return node;
return null;
@@ -1754,37 +1781,34 @@ merge(Compressor.prototype, {
for (var i = 0; i < statements.length; i++) {
var stat = statements[i];
if (prev) {
if (stat instanceof AST_For && !(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
if (stat instanceof AST_Exit) {
stat.value = cons_seq(stat.value || make_node(AST_Undefined, stat).transform(compressor));
} else if (stat instanceof AST_For) {
if (!(stat.init instanceof AST_Definitions)) {
var abort = false;
prev.body.walk(new TreeWalker(function(node) {
if (abort || node instanceof AST_Scope) return true;
if (node instanceof AST_Binary && node.operator == "in") {
abort = true;
return true;
}
}));
if (!abort) {
if (stat.init) stat.init = cons_seq(stat.init);
else {
stat.init = prev.body;
n--;
CHANGED = true;
}
}
}
}
else if (stat instanceof AST_If) {
} else if (stat instanceof AST_ForIn) {
stat.object = cons_seq(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = cons_seq(stat.condition);
}
else if (stat instanceof AST_With) {
} else if (stat instanceof AST_Switch) {
stat.expression = cons_seq(stat.expression);
}
else if (stat instanceof AST_Exit && stat.value) {
stat.value = cons_seq(stat.value);
}
else if (stat instanceof AST_Exit) {
stat.value = cons_seq(make_node(AST_Undefined, stat).transform(compressor));
}
else if (stat instanceof AST_Switch) {
} else if (stat instanceof AST_With) {
stat.expression = cons_seq(stat.expression);
}
}
@@ -1840,6 +1864,12 @@ merge(Compressor.prototype, {
prop = prop.evaluate(compressor);
}
if (prop instanceof AST_Node) break;
prop = "" + prop;
if (compressor.has_directive("use strict")) {
if (!all(def.value.properties, function(node) {
return node.key != prop && node.key.name != prop;
})) break;
}
def.value.properties.push(make_node(AST_ObjectKeyVal, node, {
key: prop,
value: node.right
@@ -1850,7 +1880,7 @@ merge(Compressor.prototype, {
return trimmed && exprs;
}
function join_consecutive_vars(statements, compressor) {
function join_consecutive_vars(statements) {
var defs;
for (var i = 0, j = -1, len = statements.length; i < len; i++) {
var stat = statements[i];
@@ -1867,18 +1897,7 @@ merge(Compressor.prototype, {
defs = stat;
}
} else if (stat instanceof AST_Exit) {
var exprs = join_object_assignments(prev, stat.value);
if (exprs) {
CHANGED = true;
if (exprs.length) {
stat.value = make_sequence(stat.value, exprs);
} else if (stat.value instanceof AST_Sequence) {
stat.value = stat.value.tail_node().left;
} else {
stat.value = stat.value.left;
}
}
statements[++j] = stat;
stat.value = extract_object_assignments(stat.value);
} else if (stat instanceof AST_For) {
var exprs = join_object_assignments(prev, stat.init);
if (exprs) {
@@ -1900,6 +1919,10 @@ merge(Compressor.prototype, {
} else {
statements[++j] = stat;
}
} else if (stat instanceof AST_ForIn) {
stat.object = extract_object_assignments(stat.object);
} else if (stat instanceof AST_If) {
stat.condition = extract_object_assignments(stat.condition);
} else if (stat instanceof AST_SimpleStatement) {
var exprs = join_object_assignments(prev, stat.body);
if (exprs) {
@@ -1908,11 +1931,31 @@ merge(Compressor.prototype, {
stat.body = make_sequence(stat.body, exprs);
}
statements[++j] = stat;
} else if (stat instanceof AST_Switch) {
stat.expression = extract_object_assignments(stat.expression);
} else if (stat instanceof AST_With) {
stat.expression = extract_object_assignments(stat.expression);
} else {
statements[++j] = stat;
}
}
statements.length = j + 1;
function extract_object_assignments(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;
}
}
}
@@ -1944,6 +1987,18 @@ merge(Compressor.prototype, {
}));
};
function get_value(key) {
if (key instanceof AST_Constant) {
return key.getValue();
}
if (key instanceof AST_UnaryPrefix
&& key.operator == "void"
&& key.expression instanceof AST_Constant) {
return;
}
return key;
}
function is_undefined(node, compressor) {
return node.is_undefined
|| node instanceof AST_Undefined
@@ -2192,6 +2247,95 @@ merge(Compressor.prototype, {
return (first_in_statement(compressor) ? best_of_statement : best_of_expression)(ast1, ast2);
}
function convert_to_predicate(obj) {
for (var key in obj) {
obj[key] = makePredicate(obj[key]);
}
}
var object_fns = [
"constructor",
"toString",
"valueOf",
];
var native_fns = {
Array: [
"indexOf",
"join",
"lastIndexOf",
"slice",
].concat(object_fns),
Boolean: object_fns,
Number: [
"toExponential",
"toFixed",
"toPrecision",
].concat(object_fns),
Object: object_fns,
RegExp: [
"test",
].concat(object_fns),
String: [
"charAt",
"charCodeAt",
"concat",
"indexOf",
"italics",
"lastIndexOf",
"match",
"replace",
"search",
"slice",
"split",
"substr",
"substring",
"trim",
].concat(object_fns),
};
convert_to_predicate(native_fns);
var static_fns = {
Array: [
"isArray",
],
Math: [
"abs",
"acos",
"asin",
"atan",
"ceil",
"cos",
"exp",
"floor",
"log",
"round",
"sin",
"sqrt",
"tan",
"atan2",
"pow",
"max",
"min",
],
Number: [
"isFinite",
"isNaN",
],
Object: [
"create",
"getOwnPropertyDescriptor",
"getOwnPropertyNames",
"getPrototypeOf",
"isExtensible",
"isFrozen",
"isSealed",
"keys",
],
String: [
"fromCharCode",
],
};
convert_to_predicate(static_fns);
// methods to evaluate a constant expression
(function(def){
// If the node has been successfully reduced to a constant,
@@ -2234,7 +2378,10 @@ merge(Compressor.prototype, {
var elements = [];
for (var i = 0, len = this.elements.length; i < len; i++) {
var element = this.elements[i];
if (element instanceof AST_Function) continue;
if (element instanceof AST_Function) {
elements.push(element);
continue;
}
var value = element._eval(compressor, depth);
if (element === value) return this;
elements.push(value);
@@ -2364,13 +2511,9 @@ merge(Compressor.prototype, {
Array: Array,
Math: Math,
Number: Number,
Object: Object,
String: String,
};
function convert_to_predicate(obj) {
for (var key in obj) {
obj[key] = makePredicate(obj[key]);
}
}
var static_values = {
Math: [
"E",
@@ -2411,77 +2554,6 @@ merge(Compressor.prototype, {
}
return this;
});
var object_fns = [
"constructor",
"toString",
"valueOf",
];
var native_fns = {
Array: [
"indexOf",
"join",
"lastIndexOf",
"slice",
].concat(object_fns),
Boolean: object_fns,
Number: [
"toExponential",
"toFixed",
"toPrecision",
].concat(object_fns),
RegExp: [
"test",
].concat(object_fns),
String: [
"charAt",
"charCodeAt",
"concat",
"indexOf",
"italics",
"lastIndexOf",
"match",
"replace",
"search",
"slice",
"split",
"substr",
"substring",
"trim",
].concat(object_fns),
};
convert_to_predicate(native_fns);
var static_fns = {
Array: [
"isArray",
],
Math: [
"abs",
"acos",
"asin",
"atan",
"ceil",
"cos",
"exp",
"floor",
"log",
"round",
"sin",
"sqrt",
"tan",
"atan2",
"pow",
"max",
"min"
],
Number: [
"isFinite",
"isNaN",
],
String: [
"fromCharCode",
],
};
convert_to_predicate(static_fns);
def(AST_Call, function(compressor, depth) {
var exp = this.expression;
if (compressor.option("unsafe") && exp instanceof AST_PropAccess) {
@@ -2506,7 +2578,16 @@ merge(Compressor.prototype, {
if (arg === value) return this;
args.push(value);
}
return val[key].apply(val, args);
try {
return val[key].apply(val, args);
} catch (ex) {
compressor.warn("Error evaluating {code} [{file}:{line},{col}]", {
code: this.print_to_string(),
file: this.start.file,
line: this.start.line,
col: this.start.col
});
}
}
return this;
});
@@ -2600,9 +2681,34 @@ merge(Compressor.prototype, {
if (compressor.option("unsafe")) {
var expr = this.expression;
if (is_undeclared_ref(expr) && global_pure_fns(expr.name)) return true;
if (expr instanceof AST_Dot
&& is_undeclared_ref(expr.expression)
&& (static_fns[expr.expression.name] || return_false)(expr.property)) {
return true;
}
}
return this.pure || !compressor.pure_funcs(this);
});
AST_Node.DEFMETHOD("is_call_pure", return_false);
AST_Dot.DEFMETHOD("is_call_pure", function(compressor) {
if (!compressor.option("unsafe")) return;
var expr = this.expression;
var fns = return_false;
if (expr instanceof AST_Array) {
fns = native_fns.Array;
} else if (expr.is_boolean()) {
fns = native_fns.Boolean;
} else if (expr.is_number(compressor)) {
fns = native_fns.Number;
} else if (expr instanceof AST_RegExp) {
fns = native_fns.RegExp;
} else if (expr.is_string(compressor)) {
fns = native_fns.String;
} else if (!this.may_throw_on_access(compressor)) {
fns = native_fns.Object;
}
return fns(this.property);
});
// determine if expression has side effects
(function(def){
@@ -2623,8 +2729,12 @@ merge(Compressor.prototype, {
return any(this.body, compressor);
});
def(AST_Call, function(compressor){
return !this.is_expr_pure(compressor)
|| any(this.args, compressor);
if (!this.is_expr_pure(compressor)
&& (!this.expression.is_call_pure(compressor)
|| this.expression.has_side_effects(compressor))) {
return true;
}
return any(this.args, compressor);
});
def(AST_Switch, function(compressor){
return this.expression.has_side_effects(compressor)
@@ -3480,9 +3590,7 @@ merge(Compressor.prototype, {
if (node instanceof AST_PropAccess && node.expression instanceof AST_SymbolRef) {
var defs = defs_by_id[node.expression.definition().id];
if (defs) {
var key = node.property;
if (key instanceof AST_Node) key = key.getValue();
var def = defs.get(key);
var def = defs.get(get_value(node.property));
var sym = make_node(AST_SymbolRef, node, {
name: def.name,
scope: node.expression.scope,
@@ -3534,6 +3642,12 @@ merge(Compressor.prototype, {
def(AST_This, return_null);
def(AST_Call, function(compressor, first_in_statement){
if (!this.is_expr_pure(compressor)) {
if (this.expression.is_call_pure(compressor)) {
var exprs = this.args.slice();
exprs.unshift(this.expression.expression);
exprs = trim(exprs, compressor, first_in_statement);
return exprs && make_sequence(this, exprs);
}
if (is_func_expr(this.expression)
&& (!this.expression.name || !this.expression.name.definition().references.length)) {
var node = this.clone();
@@ -4269,11 +4383,13 @@ merge(Compressor.prototype, {
return self;
} else if (exp instanceof AST_Dot) switch(exp.property) {
case "toString":
if (self.args.length == 0) return make_node(AST_Binary, self, {
left: make_node(AST_String, self, { value: "" }),
operator: "+",
right: exp.expression
}).optimize(compressor);
if (self.args.length == 0 && !exp.expression.may_throw_on_access(compressor)) {
return make_node(AST_Binary, self, {
left: make_node(AST_String, self, { value: "" }),
operator: "+",
right: exp.expression
}).optimize(compressor);
}
break;
case "join":
if (exp.expression instanceof AST_Array) EXIT: {
@@ -4383,7 +4499,7 @@ merge(Compressor.prototype, {
break;
}
}
if (compressor.option("unsafe_Func")
if (compressor.option("unsafe_Function")
&& is_undeclared_ref(exp)
&& exp.name == "Function") {
// new Function() => function(){}
@@ -5336,6 +5452,7 @@ merge(Compressor.prototype, {
fixed = make_node(AST_ClassExpression, fixed, fixed);
}
if (fixed instanceof AST_Defun) {
fixed._squeezed = true;
fixed = make_node(AST_Function, fixed, fixed);
}
var value;
@@ -5423,7 +5540,7 @@ merge(Compressor.prototype, {
}
OPT(AST_Undefined, function(self, compressor){
if (compressor.option("unsafe")) {
if (compressor.option("unsafe_undefined")) {
var undef = find_variable(compressor, "undefined");
if (undef) {
var ref = make_node(AST_SymbolRef, self, {
@@ -5953,6 +6070,7 @@ merge(Compressor.prototype, {
if (def) {
return def.optimize(compressor);
}
if (is_lhs(self, compressor.parent())) return self;
if (compressor.option("unsafe_proto")
&& self.expression instanceof AST_Dot
&& self.expression.property == "prototype") {
@@ -5991,7 +6109,6 @@ merge(Compressor.prototype, {
break;
}
}
if (is_lhs(self, compressor.parent())) return self;
var sub = self.flatten_object(self.property, compressor);
if (sub) return sub.optimize(compressor);
var ev = self.evaluate(compressor);

View File

@@ -472,6 +472,11 @@ function OutputStream(options) {
return OUTPUT;
};
function has_nlb() {
var index = OUTPUT.lastIndexOf("\n");
return /^ *$/.test(OUTPUT.slice(index + 1));
}
function prepend_comments(node) {
var self = this;
var start = node.start;
@@ -521,7 +526,7 @@ function OutputStream(options) {
comments = comments.filter(comment_filter, node);
if (comments.length == 0) return;
var last_nlb = /(^|\n) *$/.test(OUTPUT);
var last_nlb = has_nlb();
comments.forEach(function(c, i) {
if (!last_nlb) {
if (c.nlb) {
@@ -568,7 +573,7 @@ function OutputStream(options) {
print("\n");
indent();
need_newline_indented = false;
} else if (c.nlb && (i > 0 || !/(^|\n) *$/.test(OUTPUT))) {
} else if (c.nlb && (i > 0 || !has_nlb())) {
print("\n");
indent();
} else if (i > 0 || !tail) {
@@ -1677,11 +1682,8 @@ function OutputStream(options) {
function print_property_name(key, quote, output) {
if (output.option("quote_keys")) {
output.print_string(key + "");
} else if ((typeof key == "number"
|| !output.option("beautify")
&& +key + "" == key)
&& parseFloat(key) >= 0) {
output.print_string(key);
} else if ("" + +key == key && key >= 0) {
output.print(make_num(key));
} else if (RESERVED_WORDS(key) ? !output.option("ie8") : is_identifier_string(key)) {
if (quote && output.option("keep_quoted_props")) {

View File

@@ -1276,12 +1276,10 @@ function parse($TEXT, options) {
};
function for_in(init) {
var lhs = init instanceof AST_Definitions ? init.definitions[0].name : null;
var obj = expression(true);
expect(")");
return new AST_ForIn({
init : init,
name : lhs,
object : obj,
body : in_loop(statement)
});
@@ -2226,7 +2224,7 @@ function parse($TEXT, options) {
a.push(new AST_ObjectKeyVal({
start: start,
quote: start.quote,
key: name,
key: name instanceof AST_Node ? name : "" + name,
value: value,
end: prev()
}));
@@ -2283,7 +2281,7 @@ function parse($TEXT, options) {
if (typeof name === "string" || typeof name === "number") {
return new AST_SymbolMethod({
start: token,
name: name,
name: "" + name,
end: prev()
});
} else if (name === null) {

View File

@@ -303,6 +303,13 @@ Dictionary.prototype = {
ret.push(f(this._values[i], i.substr(1)));
return ret;
},
clone: function() {
var ret = new Dictionary();
for (var i in this._values)
ret._values[i] = this._values[i];
ret._size = this._size;
return ret;
},
toObject: function() { return this._values }
};
Dictionary.fromObject = function(obj) {