Compare commits
19 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e390e7e124 | ||
|
|
6fd5b5b371 | ||
|
|
fba27bfb71 | ||
|
|
41310e6404 | ||
|
|
91fc1c82b5 | ||
|
|
810cd40356 | ||
|
|
1cbd07e789 | ||
|
|
b82de04775 | ||
|
|
4bbeb09f7c | ||
|
|
c2f6fd5fde | ||
|
|
af4ea3ff69 | ||
|
|
e7643248a3 | ||
|
|
68091dbf69 | ||
|
|
cbf7269296 | ||
|
|
d8563caba7 | ||
|
|
2e0ad40fe6 | ||
|
|
5d12abc41b | ||
|
|
79e5c3f564 | ||
|
|
607f87c5cd |
@@ -276,7 +276,9 @@ function convert_ast(fn) {
|
||||
function run() {
|
||||
var content = options.sourceMap && options.sourceMap.content;
|
||||
if (content && content != "inline") {
|
||||
UglifyJS.AST_Node.info("Using input source map: " + content);
|
||||
UglifyJS.AST_Node.info("Using input source map: {content}", {
|
||||
content : content,
|
||||
});
|
||||
options.sourceMap.content = read_file(content, content);
|
||||
}
|
||||
try {
|
||||
|
||||
48
lib/ast.js
48
lib/ast.js
@@ -137,17 +137,17 @@ var AST_Node = DEFNODE("Node", "start end", {
|
||||
}, null);
|
||||
|
||||
(AST_Node.log_function = function(fn, verbose) {
|
||||
var printed = Object.create(null);
|
||||
if (fn) {
|
||||
AST_Node.info = verbose ? function(text, props) {
|
||||
log("INFO: " + string_template(text, props));
|
||||
} : noop;
|
||||
AST_Node.warn = function(text, props) {
|
||||
log("WARN: " + string_template(text, props));
|
||||
};
|
||||
} else {
|
||||
if (!fn) {
|
||||
AST_Node.info = AST_Node.warn = noop;
|
||||
return;
|
||||
}
|
||||
var printed = Object.create(null);
|
||||
AST_Node.info = verbose ? function(text, props) {
|
||||
log("INFO: " + string_template(text, props));
|
||||
} : noop;
|
||||
AST_Node.warn = function(text, props) {
|
||||
log("WARN: " + string_template(text, props));
|
||||
};
|
||||
|
||||
function log(msg) {
|
||||
if (printed[msg]) return;
|
||||
@@ -1015,24 +1015,28 @@ var AST_Object = DEFNODE("Object", "properties", {
|
||||
var AST_ObjectProperty = DEFNODE("ObjectProperty", "key value", {
|
||||
$documentation: "Base class for literal object properties",
|
||||
$propdoc: {
|
||||
key: "[string|AST_SymbolAccessor] property name. For ObjectKeyVal this is a string. For getters and setters this is an AST_SymbolAccessor.",
|
||||
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor."
|
||||
key: "[string|AST_Node] property name. For computed property this is an AST_Node.",
|
||||
value: "[AST_Node] property value. For getters and setters this is an AST_Accessor.",
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
if (node.key instanceof AST_Node) node.key.walk(visitor);
|
||||
node.value.walk(visitor);
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
||||
$documentation: "A key: value object property",
|
||||
$propdoc: {
|
||||
quote: "[string] the original quote character"
|
||||
},
|
||||
_validate: function() {
|
||||
if (typeof this.key != "string") throw new Error("key must be string");
|
||||
if (typeof this.key != "string") {
|
||||
if (!(this.key instanceof AST_Node)) throw new Error("key must be string or AST_Node");
|
||||
must_be_expression(this, "key");
|
||||
}
|
||||
if (!(this.value instanceof AST_Node)) throw new Error("value must be AST_Node");
|
||||
},
|
||||
});
|
||||
|
||||
var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", null, {
|
||||
$documentation: "A key: value object property",
|
||||
_validate: function() {
|
||||
must_be_expression(this, "value");
|
||||
},
|
||||
}, AST_ObjectProperty);
|
||||
@@ -1040,7 +1044,6 @@ var AST_ObjectKeyVal = DEFNODE("ObjectKeyVal", "quote", {
|
||||
var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||
$documentation: "An object setter property",
|
||||
_validate: function() {
|
||||
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
|
||||
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
||||
},
|
||||
}, AST_ObjectProperty);
|
||||
@@ -1048,7 +1051,6 @@ var AST_ObjectSetter = DEFNODE("ObjectSetter", null, {
|
||||
var AST_ObjectGetter = DEFNODE("ObjectGetter", null, {
|
||||
$documentation: "An object getter property",
|
||||
_validate: function() {
|
||||
if (!(this.key instanceof AST_SymbolAccessor)) throw new Error("key must be AST_SymbolAccessor");
|
||||
if (!(this.value instanceof AST_Accessor)) throw new Error("value must be AST_Accessor");
|
||||
},
|
||||
}, AST_ObjectProperty);
|
||||
@@ -1065,10 +1067,6 @@ var AST_Symbol = DEFNODE("Symbol", "scope name thedef", {
|
||||
},
|
||||
});
|
||||
|
||||
var AST_SymbolAccessor = DEFNODE("SymbolAccessor", null, {
|
||||
$documentation: "The name of a property accessor (setter/getter function)"
|
||||
}, AST_Symbol);
|
||||
|
||||
var AST_SymbolDeclaration = DEFNODE("SymbolDeclaration", "init", {
|
||||
$documentation: "A declaration symbol (symbol in var, function name or argument, symbol in catch)",
|
||||
}, AST_Symbol);
|
||||
|
||||
299
lib/compress.js
299
lib/compress.js
@@ -192,7 +192,11 @@ merge(Compressor.prototype, {
|
||||
node.walk(new TreeWalker(function() {
|
||||
count++;
|
||||
}));
|
||||
AST_Node.info("pass " + pass + ": last_count: " + min_count + ", count: " + count);
|
||||
AST_Node.info("pass {pass}: last_count: {min_count}, count: {count}", {
|
||||
pass: pass,
|
||||
min_count: min_count,
|
||||
count: count,
|
||||
});
|
||||
if (count < min_count) {
|
||||
min_count = count;
|
||||
stopping = false;
|
||||
@@ -1162,7 +1166,9 @@ merge(Compressor.prototype, {
|
||||
|
||||
function as_statement_array(thing) {
|
||||
if (thing === null) return [];
|
||||
if (thing instanceof AST_BlockStatement) return thing.body;
|
||||
if (thing instanceof AST_BlockStatement) return all(thing.body, function(stat) {
|
||||
return !(stat instanceof AST_Const || stat instanceof AST_Let);
|
||||
}) ? thing.body : [ thing ];
|
||||
if (thing instanceof AST_EmptyStatement) return [];
|
||||
if (thing instanceof AST_Statement) return [ thing ];
|
||||
throw new Error("Can't convert thing to statement array");
|
||||
@@ -1340,11 +1346,11 @@ merge(Compressor.prototype, {
|
||||
replaced++;
|
||||
}
|
||||
CHANGED = abort = true;
|
||||
AST_Node.info("Collapsing {name} [{file}:{line},{col}]", {
|
||||
name: node.print_to_string(),
|
||||
AST_Node.info("Collapsing {node} [{file}:{line},{col}]", {
|
||||
node: node,
|
||||
file: node.start.file,
|
||||
line: node.start.line,
|
||||
col: node.start.col
|
||||
col: node.start.col,
|
||||
});
|
||||
if (candidate instanceof AST_UnaryPostfix) {
|
||||
if (lhs instanceof AST_SymbolRef) lhs.definition().fixed = false;
|
||||
@@ -1420,8 +1426,9 @@ merge(Compressor.prototype, {
|
||||
if (!--replaced) abort = true;
|
||||
if (is_lhs(node, multi_replacer.parent())) return node;
|
||||
def.replaced++;
|
||||
value_def.replaced--;
|
||||
return rvalue.clone();
|
||||
var ref = rvalue.clone();
|
||||
value_def.references.push(ref);
|
||||
return ref;
|
||||
}
|
||||
// Skip (non-executed) functions and (leading) default case in switch statements
|
||||
if (node instanceof AST_Default || node instanceof AST_Scope) return node;
|
||||
@@ -1552,12 +1559,17 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
|
||||
if (node instanceof AST_DWLoop) return true;
|
||||
if (node instanceof AST_LoopControl) return true;
|
||||
if (node instanceof AST_SymbolRef) {
|
||||
if (node.is_declared(compressor) ? node.fixed_value() || all(node.definition().orig, function(sym) {
|
||||
return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
|
||||
}) : parent instanceof AST_Assign && parent.operator == "=" && parent.left === node) return false;
|
||||
if (!replace_all) return true;
|
||||
scan_rhs = false;
|
||||
return false;
|
||||
}
|
||||
if (node instanceof AST_Try) return true;
|
||||
if (node instanceof AST_With) return true;
|
||||
if (replace_all) return false;
|
||||
return node instanceof AST_SymbolRef
|
||||
&& !node.is_declared(compressor)
|
||||
&& !(parent instanceof AST_Assign && parent.operator == "=" && parent.left === node);
|
||||
return false;
|
||||
}
|
||||
|
||||
function in_conditional(node, parent) {
|
||||
@@ -1706,6 +1718,8 @@ merge(Compressor.prototype, {
|
||||
extract_candidates(expr.condition);
|
||||
extract_candidates(expr.consequent);
|
||||
extract_candidates(expr.alternative);
|
||||
} else if (expr instanceof AST_Definitions) {
|
||||
expr.definitions.forEach(extract_candidates);
|
||||
} else if (expr instanceof AST_Dot) {
|
||||
extract_candidates(expr.expression);
|
||||
} else if (expr instanceof AST_DWLoop) {
|
||||
@@ -1737,11 +1751,10 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
} else if (expr instanceof AST_Object) {
|
||||
expr.properties.forEach(function(prop) {
|
||||
if (prop instanceof AST_ObjectKeyVal) {
|
||||
hit_stack.push(prop);
|
||||
extract_candidates(prop.value);
|
||||
hit_stack.pop();
|
||||
}
|
||||
hit_stack.push(prop);
|
||||
if (prop.key instanceof AST_Node) extract_candidates(prop.key);
|
||||
if (prop instanceof AST_ObjectKeyVal) extract_candidates(prop.value);
|
||||
hit_stack.pop();
|
||||
});
|
||||
} else if (expr instanceof AST_Sequence) {
|
||||
expr.expressions.forEach(extract_candidates);
|
||||
@@ -1759,18 +1772,18 @@ merge(Compressor.prototype, {
|
||||
} else {
|
||||
extract_candidates(expr.expression);
|
||||
}
|
||||
} else if (expr instanceof AST_Var) {
|
||||
expr.definitions.forEach(extract_candidates);
|
||||
} else if (expr instanceof AST_VarDef) {
|
||||
if (expr.value) {
|
||||
var def = expr.name.definition();
|
||||
if (def.references.length > def.replaced) {
|
||||
candidates.push(hit_stack.slice());
|
||||
if (expr.name instanceof AST_SymbolVar) {
|
||||
if (expr.value) {
|
||||
var def = expr.name.definition();
|
||||
if (def.references.length > def.replaced) {
|
||||
candidates.push(hit_stack.slice());
|
||||
}
|
||||
} else {
|
||||
declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
|
||||
}
|
||||
extract_candidates(expr.value);
|
||||
} else {
|
||||
declare_only[expr.name.name] = (declare_only[expr.name.name] || 0) + 1;
|
||||
}
|
||||
if (expr.value) extract_candidates(expr.value);
|
||||
}
|
||||
hit_stack.pop();
|
||||
}
|
||||
@@ -1787,7 +1800,7 @@ merge(Compressor.prototype, {
|
||||
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_ObjectKeyVal) return node;
|
||||
if (parent instanceof AST_ObjectProperty) return node;
|
||||
if (parent instanceof AST_PropAccess) return node;
|
||||
if (parent instanceof AST_Sequence) {
|
||||
return (parent.tail_node() === node ? find_stop : find_stop_unused)(parent, level + 1);
|
||||
@@ -1845,7 +1858,7 @@ merge(Compressor.prototype, {
|
||||
if (parent.condition !== node) return node;
|
||||
return find_stop_value(parent, level + 1);
|
||||
}
|
||||
if (parent instanceof AST_ObjectKeyVal) {
|
||||
if (parent instanceof AST_ObjectProperty) {
|
||||
var obj = scanner.parent(level + 1);
|
||||
return all(obj.properties, function(prop) {
|
||||
return prop instanceof AST_ObjectKeyVal;
|
||||
@@ -1893,7 +1906,7 @@ merge(Compressor.prototype, {
|
||||
if (parent instanceof AST_Exit) return find_stop_unused(parent, level + 1);
|
||||
if (parent instanceof AST_If) return find_stop_unused(parent, level + 1);
|
||||
if (parent instanceof AST_IterationStatement) return node;
|
||||
if (parent instanceof AST_ObjectKeyVal) {
|
||||
if (parent instanceof AST_ObjectProperty) {
|
||||
var obj = scanner.parent(level + 1);
|
||||
return all(obj.properties, function(prop) {
|
||||
return prop instanceof AST_ObjectKeyVal;
|
||||
@@ -2687,9 +2700,12 @@ merge(Compressor.prototype, {
|
||||
if (prop instanceof AST_Node) break;
|
||||
prop = "" + prop;
|
||||
var diff = prop == "__proto__" || compressor.has_directive("use strict") ? function(node) {
|
||||
return node.key != prop && node.key.name != prop;
|
||||
return typeof node.key == "string" && node.key != prop;
|
||||
} : function(node) {
|
||||
return node.key.name != prop;
|
||||
if (node instanceof AST_ObjectGetter || node instanceof AST_ObjectSetter) {
|
||||
return typeof node.key == "string" && node.key != prop;
|
||||
}
|
||||
return true;
|
||||
};
|
||||
if (!all(value.properties, diff)) break;
|
||||
value.properties.push(make_node(AST_ObjectKeyVal, node, {
|
||||
@@ -2799,14 +2815,15 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function extract_declarations_from_unreachable_code(compressor, stat, target) {
|
||||
if (!(stat instanceof AST_Defun)) {
|
||||
if (!(stat instanceof AST_Definitions || stat instanceof AST_Defun)) {
|
||||
AST_Node.warn("Dropping unreachable code [{file}:{line},{col}]", stat.start);
|
||||
}
|
||||
var block;
|
||||
stat.walk(new TreeWalker(function(node, descend) {
|
||||
if (node instanceof AST_Definitions) {
|
||||
AST_Node.warn("Declarations in unreachable code! [{file}:{line},{col}]", node.start);
|
||||
node.remove_initializers(compressor);
|
||||
if (node.remove_initializers(compressor)) {
|
||||
AST_Node.warn("Dropping initialization in unreachable code [{file}:{line},{col}]", node.start);
|
||||
}
|
||||
push(node);
|
||||
return true;
|
||||
}
|
||||
@@ -2973,10 +2990,9 @@ merge(Compressor.prototype, {
|
||||
def(AST_Lambda, return_false);
|
||||
def(AST_Null, return_true);
|
||||
def(AST_Object, function(compressor) {
|
||||
if (!is_strict(compressor)) return false;
|
||||
for (var i = this.properties.length; --i >=0;)
|
||||
if (this.properties[i].value instanceof AST_Accessor) return true;
|
||||
return false;
|
||||
return is_strict(compressor) && !all(this.properties, function(prop) {
|
||||
return prop instanceof AST_ObjectKeyVal;
|
||||
});
|
||||
});
|
||||
def(AST_Sequence, function(compressor) {
|
||||
return this.tail_node()._dot_throw(compressor);
|
||||
@@ -3273,7 +3289,12 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
|
||||
function warn(node) {
|
||||
AST_Node.warn("global_defs " + node.print_to_string() + " redefined [{file}:{line},{col}]", node.start);
|
||||
AST_Node.warn("global_defs {node} redefined [{file}:{line},{col}]", {
|
||||
node: node,
|
||||
file: node.start.file,
|
||||
line: node.start.line,
|
||||
col: node.start.col,
|
||||
});
|
||||
}
|
||||
|
||||
AST_Toplevel.DEFMETHOD("resolve_defines", function(compressor) {
|
||||
@@ -3567,12 +3588,13 @@ merge(Compressor.prototype, {
|
||||
var val = {};
|
||||
for (var i = 0; i < this.properties.length; i++) {
|
||||
var prop = this.properties[i];
|
||||
if (!(prop instanceof AST_ObjectKeyVal)) return this;
|
||||
var key = prop.key;
|
||||
if (key instanceof AST_Symbol) key = key.name;
|
||||
if (prop.value instanceof AST_Function) {
|
||||
if (typeof Object.prototype[key] == "function") return this;
|
||||
continue;
|
||||
if (key instanceof AST_Node) {
|
||||
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
||||
if (key === prop.key) return this;
|
||||
}
|
||||
if (prop.value instanceof AST_Function && typeof Object.prototype[key] == "function") return this;
|
||||
val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
|
||||
if (val[key] === prop.value) return this;
|
||||
}
|
||||
@@ -3878,10 +3900,10 @@ merge(Compressor.prototype, {
|
||||
return val[key].apply(val, args);
|
||||
} catch (ex) {
|
||||
AST_Node.warn("Error evaluating {code} [{file}:{line},{col}]", {
|
||||
code: this.print_to_string(),
|
||||
code: this,
|
||||
file: this.start.file,
|
||||
line: this.start.line,
|
||||
col: this.start.col
|
||||
col: this.start.col,
|
||||
});
|
||||
} finally {
|
||||
if (val instanceof RegExp) val.lastIndex = 0;
|
||||
@@ -4090,7 +4112,8 @@ merge(Compressor.prototype, {
|
||||
return any(this.properties, compressor);
|
||||
});
|
||||
def(AST_ObjectProperty, function(compressor) {
|
||||
return this.value.has_side_effects(compressor);
|
||||
return this.key instanceof AST_Node && this.key.has_side_effects(compressor)
|
||||
|| this.value.has_side_effects(compressor);
|
||||
});
|
||||
def(AST_Sub, function(compressor) {
|
||||
return this.expression.may_throw_on_access(compressor)
|
||||
@@ -4202,7 +4225,8 @@ merge(Compressor.prototype, {
|
||||
return any(this.properties, compressor);
|
||||
});
|
||||
def(AST_ObjectProperty, function(compressor) {
|
||||
return this.value.may_throw(compressor);
|
||||
return this.key instanceof AST_Node && this.key.may_throw(compressor)
|
||||
|| this.value.may_throw(compressor);
|
||||
});
|
||||
def(AST_Return, function(compressor) {
|
||||
return this.value && this.value.may_throw(compressor);
|
||||
@@ -4298,7 +4322,7 @@ merge(Compressor.prototype, {
|
||||
return all(this.properties);
|
||||
});
|
||||
def(AST_ObjectProperty, function() {
|
||||
return this.value.is_constant_expression();
|
||||
return typeof this.key == "string" && this.value.is_constant_expression();
|
||||
});
|
||||
def(AST_Unary, function() {
|
||||
return this.expression.is_constant_expression();
|
||||
@@ -4452,6 +4476,11 @@ merge(Compressor.prototype, {
|
||||
pop();
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Break) {
|
||||
var target = tw.loopcontrol_target(node);
|
||||
if (!(target instanceof AST_IterationStatement)) insert(target);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Conditional) {
|
||||
node.condition.walk(tw);
|
||||
push();
|
||||
@@ -4471,20 +4500,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (node instanceof AST_Continue) {
|
||||
var target = tw.loopcontrol_target(node);
|
||||
if (!(target instanceof AST_Do)) return true;
|
||||
var stack = [];
|
||||
while (!HOP(segment, "block") || segment.block !== target) {
|
||||
stack.push(segment);
|
||||
pop();
|
||||
}
|
||||
segment.loop = "c";
|
||||
push();
|
||||
while (stack.length) {
|
||||
var seg = stack.pop();
|
||||
push();
|
||||
if (HOP(seg, "block")) segment.block = seg.block;
|
||||
if (HOP(seg, "loop")) segment.loop = seg.loop;
|
||||
}
|
||||
if (target instanceof AST_Do) insert(target);
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_Do) {
|
||||
@@ -4493,7 +4509,7 @@ merge(Compressor.prototype, {
|
||||
segment.loop = true;
|
||||
var save = segment;
|
||||
node.body.walk(tw);
|
||||
if (segment.loop == "c") segment = save;
|
||||
if (segment.inserted === node) segment = save;
|
||||
node.condition.walk(tw);
|
||||
pop();
|
||||
return true;
|
||||
@@ -4534,7 +4550,9 @@ merge(Compressor.prototype, {
|
||||
if (node instanceof AST_LabeledStatement) {
|
||||
push();
|
||||
segment.block = node;
|
||||
var save = segment;
|
||||
node.body.walk(tw);
|
||||
if (segment.inserted === node) segment = save;
|
||||
pop();
|
||||
return true;
|
||||
}
|
||||
@@ -4570,7 +4588,10 @@ merge(Compressor.prototype, {
|
||||
segment = save;
|
||||
node.body.forEach(function(branch) {
|
||||
push();
|
||||
segment.block = node;
|
||||
var save = segment;
|
||||
walk_body(branch, tw);
|
||||
if (segment.inserted === node) segment = save;
|
||||
pop();
|
||||
});
|
||||
return true;
|
||||
@@ -4725,6 +4746,27 @@ merge(Compressor.prototype, {
|
||||
});
|
||||
}
|
||||
|
||||
function insert(target) {
|
||||
var stack = [];
|
||||
while (true) {
|
||||
if (HOP(segment, "block")) {
|
||||
var block = segment.block;
|
||||
if (block instanceof AST_LabeledStatement) block = block.body;
|
||||
if (block === target) break;
|
||||
}
|
||||
stack.push(segment);
|
||||
pop();
|
||||
}
|
||||
segment.inserted = segment.block;
|
||||
push();
|
||||
while (stack.length) {
|
||||
var seg = stack.pop();
|
||||
push();
|
||||
if (HOP(seg, "block")) segment.block = seg.block;
|
||||
if (HOP(seg, "loop")) segment.loop = seg.loop;
|
||||
}
|
||||
}
|
||||
|
||||
function must_visit(base, segment) {
|
||||
return base === segment || base.isPrototypeOf(segment);
|
||||
}
|
||||
@@ -4992,7 +5034,7 @@ merge(Compressor.prototype, {
|
||||
var old_def, var_defs = var_defs_by_id.get(sym.id);
|
||||
if (!def.value) {
|
||||
if (var_defs.length > 1) {
|
||||
AST_Node.warn("Dropping duplicated declaration of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
AST_Node.info("Dropping declaration of variable {name} [{file}:{line},{col}]", template(def.name));
|
||||
remove(var_defs, def);
|
||||
sym.eliminated++;
|
||||
} else {
|
||||
@@ -5183,7 +5225,10 @@ merge(Compressor.prototype, {
|
||||
var def = sym.definition();
|
||||
if (!def) return;
|
||||
if (def.id in in_use_ids) return;
|
||||
if (def.scope !== self && self.find_variable(sym) === def) return;
|
||||
if (def.scope !== self) {
|
||||
var d = self.find_variable(sym);
|
||||
if ((d && d.redefined() || d) === def) return;
|
||||
}
|
||||
log(sym, "Dropping unused loop variable {name}");
|
||||
if (for_ins[def.id] === node) delete for_ins[def.id];
|
||||
var body = [];
|
||||
@@ -5226,7 +5271,7 @@ merge(Compressor.prototype, {
|
||||
name: sym.name,
|
||||
file: sym.start.file,
|
||||
line: sym.start.line,
|
||||
col : sym.start.col
|
||||
col : sym.start.col,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -5395,7 +5440,7 @@ merge(Compressor.prototype, {
|
||||
vars.set(def.name.name, def);
|
||||
++vars_found;
|
||||
});
|
||||
var seq = node.to_assignments(compressor);
|
||||
var seq = node.to_assignments();
|
||||
var p = tt.parent();
|
||||
if (p instanceof AST_ForIn && p.init === node) {
|
||||
if (seq) return seq;
|
||||
@@ -5703,7 +5748,7 @@ merge(Compressor.prototype, {
|
||||
return right instanceof AST_Object
|
||||
&& right.properties.length > 0
|
||||
&& all(right.properties, function(prop) {
|
||||
return prop instanceof AST_ObjectKeyVal;
|
||||
return prop instanceof AST_ObjectKeyVal && typeof prop.key == "string";
|
||||
})
|
||||
&& all(def.references, function(ref) {
|
||||
return ref.fixed_value() === right;
|
||||
@@ -5893,12 +5938,14 @@ merge(Compressor.prototype, {
|
||||
return safe_to_drop(this, compressor) ? null : this;
|
||||
});
|
||||
def(AST_Object, function(compressor, first_in_statement) {
|
||||
var values = trim(this.properties, compressor, first_in_statement);
|
||||
var exprs = [];
|
||||
this.properties.forEach(function(prop) {
|
||||
if (prop.key instanceof AST_Node) exprs.push(prop.key);
|
||||
exprs.push(prop.value);
|
||||
});
|
||||
var values = trim(exprs, compressor, first_in_statement);
|
||||
return values && make_sequence(this, values);
|
||||
});
|
||||
def(AST_ObjectProperty, function(compressor, first_in_statement) {
|
||||
return this.value.drop_side_effect_free(compressor, first_in_statement);
|
||||
});
|
||||
def(AST_Sequence, function(compressor, first_in_statement) {
|
||||
var expressions = trim(this.expressions, compressor, first_in_statement);
|
||||
if (!expressions) return null;
|
||||
@@ -6662,35 +6709,35 @@ merge(Compressor.prototype, {
|
||||
this.definitions.forEach(function(def) {
|
||||
def.value = make_node(AST_Undefined, def).optimize(compressor);
|
||||
});
|
||||
return true;
|
||||
});
|
||||
|
||||
AST_Let.DEFMETHOD("remove_initializers", function() {
|
||||
function remove_initializers() {
|
||||
var CHANGED = false;
|
||||
this.definitions.forEach(function(def) {
|
||||
if (!def.value) return;
|
||||
def.value = null;
|
||||
CHANGED = true;
|
||||
});
|
||||
});
|
||||
return CHANGED;
|
||||
}
|
||||
|
||||
AST_Var.DEFMETHOD("remove_initializers", function() {
|
||||
this.definitions.forEach(function(def) {
|
||||
def.value = null;
|
||||
});
|
||||
});
|
||||
AST_Let.DEFMETHOD("remove_initializers", remove_initializers);
|
||||
AST_Var.DEFMETHOD("remove_initializers", remove_initializers);
|
||||
|
||||
AST_Definitions.DEFMETHOD("to_assignments", function(compressor) {
|
||||
var reduce_vars = compressor.option("reduce_vars");
|
||||
var assignments = this.definitions.reduce(function(a, def) {
|
||||
if (def.value) {
|
||||
var name = make_node(AST_SymbolRef, def.name, def.name);
|
||||
a.push(make_node(AST_Assign, def, {
|
||||
AST_Definitions.DEFMETHOD("to_assignments", function() {
|
||||
var assignments = this.definitions.reduce(function(a, defn) {
|
||||
var def = defn.name.definition();
|
||||
if (defn.value) {
|
||||
var name = make_node(AST_SymbolRef, defn.name, defn.name);
|
||||
a.push(make_node(AST_Assign, defn, {
|
||||
operator : "=",
|
||||
left : name,
|
||||
right : def.value
|
||||
right : defn.value
|
||||
}));
|
||||
if (reduce_vars) name.definition().fixed = false;
|
||||
def.references.push(name);
|
||||
}
|
||||
def = def.name.definition();
|
||||
def.eliminated++;
|
||||
def.replaced--;
|
||||
return a;
|
||||
}, []);
|
||||
if (assignments.length == 0) return null;
|
||||
@@ -6827,7 +6874,7 @@ merge(Compressor.prototype, {
|
||||
length: length,
|
||||
file: self.start.file,
|
||||
line: self.start.line,
|
||||
col: self.start.col
|
||||
col: self.start.col,
|
||||
});
|
||||
break;
|
||||
}
|
||||
@@ -6884,10 +6931,10 @@ merge(Compressor.prototype, {
|
||||
}));
|
||||
} catch (ex) {
|
||||
AST_Node.warn("Error converting {expr} [{file}:{line},{col}]", {
|
||||
expr: self.print_to_string(),
|
||||
expr: self,
|
||||
file: self.start.file,
|
||||
line: self.start.line,
|
||||
col: self.start.col
|
||||
col: self.start.col,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -7127,7 +7174,7 @@ merge(Compressor.prototype, {
|
||||
return node;
|
||||
}
|
||||
}
|
||||
var child, in_loop, scope;
|
||||
var insert, in_loop, scope;
|
||||
if (replacing && can_inject_symbols()) {
|
||||
fn._squeezed = true;
|
||||
if (exp !== fn) fn.parent_scope = exp.scope;
|
||||
@@ -7297,7 +7344,7 @@ merge(Compressor.prototype, {
|
||||
|
||||
function can_inject_symbols() {
|
||||
var defined = Object.create(null);
|
||||
var level = 0;
|
||||
var level = 0, child;
|
||||
scope = compressor.self();
|
||||
while (!(scope instanceof AST_Scope)) {
|
||||
if (scope.variables) scope.variables.each(function(def) {
|
||||
@@ -7318,6 +7365,8 @@ merge(Compressor.prototype, {
|
||||
if (scope.fixed_value() instanceof AST_Scope) return false;
|
||||
}
|
||||
}
|
||||
insert = scope.body.indexOf(child) + 1;
|
||||
if (!insert) return false;
|
||||
var safe_to_inject = (!(scope instanceof AST_Toplevel) || compressor.toplevel.vars)
|
||||
&& (exp !== fn || fn.parent_scope.resolve() === compressor.find_parent(AST_Scope));
|
||||
var inline = compressor.option("inline");
|
||||
@@ -7420,7 +7469,7 @@ merge(Compressor.prototype, {
|
||||
return true;
|
||||
}
|
||||
});
|
||||
args.unshift(scope.body.indexOf(child) + 1, 0);
|
||||
args.unshift(insert, 0);
|
||||
if (decls.length) args.push(make_node(AST_Var, fn, {
|
||||
definitions: decls
|
||||
}));
|
||||
@@ -7598,7 +7647,9 @@ merge(Compressor.prototype, {
|
||||
// typeof always returns a non-empty string, thus it's
|
||||
// always true in booleans
|
||||
AST_Node.warn("Boolean expression always true [{file}:{line},{col}]", self.start);
|
||||
return (exp instanceof AST_SymbolRef ? make_node(AST_True, self) : make_sequence(self, [
|
||||
return (exp instanceof AST_SymbolRef && all(exp.definition().orig, function(sym) {
|
||||
return !(sym instanceof AST_SymbolConst || sym instanceof AST_SymbolLet);
|
||||
}) ? make_node(AST_True, self) : make_sequence(self, [
|
||||
exp,
|
||||
make_node(AST_True, self)
|
||||
])).optimize(compressor);
|
||||
@@ -8388,7 +8439,7 @@ merge(Compressor.prototype, {
|
||||
fixed.name = make_node(AST_SymbolLambda, fixed.name, fixed.name);
|
||||
}
|
||||
if (fixed instanceof AST_Lambda) {
|
||||
var scope = self.scope;
|
||||
var scope = self.scope.resolve();
|
||||
fixed.enclosed.forEach(function(def) {
|
||||
if (fixed.variables.has(def.name)) return;
|
||||
if (scope.var_names()[def.name]) return;
|
||||
@@ -9274,22 +9325,21 @@ merge(Compressor.prototype, {
|
||||
var props = expr.properties;
|
||||
for (var i = props.length; --i >= 0;) {
|
||||
var prop = props[i];
|
||||
if ("" + prop.key == key) {
|
||||
if (!all(props, function(prop) {
|
||||
return prop instanceof AST_ObjectKeyVal;
|
||||
})) break;
|
||||
if (!safe_to_flatten(prop.value, compressor)) break;
|
||||
return make_node(AST_Sub, this, {
|
||||
expression: make_node(AST_Array, expr, {
|
||||
elements: props.map(function(prop) {
|
||||
return prop.value;
|
||||
})
|
||||
}),
|
||||
property: make_node(AST_Number, this, {
|
||||
value: i
|
||||
if (prop.key != key) continue;
|
||||
if (!all(props, function(prop) {
|
||||
return prop instanceof AST_ObjectKeyVal && typeof prop.key == "string";
|
||||
})) break;
|
||||
if (!safe_to_flatten(prop.value, compressor)) break;
|
||||
return make_node(AST_Sub, this, {
|
||||
expression: make_node(AST_Array, expr, {
|
||||
elements: props.map(function(prop) {
|
||||
return prop.value;
|
||||
})
|
||||
});
|
||||
}
|
||||
}),
|
||||
property: make_node(AST_Number, this, {
|
||||
value: i
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -9300,7 +9350,7 @@ merge(Compressor.prototype, {
|
||||
prop: self.property,
|
||||
file: self.start.file,
|
||||
line: self.start.line,
|
||||
col: self.start.col
|
||||
col: self.start.col,
|
||||
});
|
||||
}
|
||||
var parent = compressor.parent();
|
||||
@@ -9356,25 +9406,28 @@ merge(Compressor.prototype, {
|
||||
|
||||
OPT(AST_Object, function(self, compressor) {
|
||||
if (!compressor.option("objects") || compressor.has_directive("use strict")) return self;
|
||||
for (var i = self.properties.length; --i >= 0;) {
|
||||
var prop = self.properties[i];
|
||||
var key = prop.key;
|
||||
if (key instanceof AST_Node) key = key.evaluate(compressor);
|
||||
if (typeof key != "string" || /[0-9]+/.test(key)) break;
|
||||
if (key !== prop.key) prop.key = "" + key;
|
||||
}
|
||||
var keys = new Dictionary();
|
||||
var values = [];
|
||||
self.properties.forEach(function(prop) {
|
||||
if (typeof prop.key != "string") {
|
||||
if (prop instanceof AST_ObjectKeyVal && typeof prop.key == "string") {
|
||||
if (prop.value.has_side_effects(compressor)) flush();
|
||||
keys.add(prop.key, prop.value);
|
||||
} else {
|
||||
flush();
|
||||
values.push(prop);
|
||||
return;
|
||||
}
|
||||
if (prop.value.has_side_effects(compressor)) {
|
||||
flush();
|
||||
}
|
||||
keys.add(prop.key, prop.value);
|
||||
});
|
||||
flush();
|
||||
if (self.properties.length != values.length) {
|
||||
return make_node(AST_Object, self, {
|
||||
properties: values
|
||||
});
|
||||
}
|
||||
if (self.properties.length != values.length) return make_node(AST_Object, self, {
|
||||
properties: values
|
||||
});
|
||||
return self;
|
||||
|
||||
function flush() {
|
||||
|
||||
@@ -33,7 +33,9 @@ function read_source_map(name, toplevel) {
|
||||
return to_ascii(match[2]);
|
||||
}
|
||||
}
|
||||
AST_Node.warn("inline source map not found: " + name);
|
||||
AST_Node.warn("inline source map not found: {name}", {
|
||||
name: name,
|
||||
});
|
||||
}
|
||||
|
||||
function parse_source_map(content) {
|
||||
@@ -258,6 +260,7 @@ function minify(files, options) {
|
||||
} catch (ex) {
|
||||
return { error: ex };
|
||||
} finally {
|
||||
AST_Node.log_function();
|
||||
AST_Node.disable_validation();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -115,9 +115,6 @@
|
||||
value : from_moz(M.value)
|
||||
};
|
||||
if (M.kind == "init") return new AST_ObjectKeyVal(args);
|
||||
args.key = new AST_SymbolAccessor({
|
||||
name: args.key
|
||||
});
|
||||
args.value = new AST_Accessor(args.value);
|
||||
if (M.kind == "get") return new AST_ObjectGetter(args);
|
||||
if (M.kind == "set") return new AST_ObjectSetter(args);
|
||||
@@ -385,7 +382,7 @@
|
||||
def_to_moz(AST_ObjectProperty, function To_Moz_Property(M) {
|
||||
var key = {
|
||||
type: "Literal",
|
||||
value: M.key instanceof AST_SymbolAccessor ? M.key.name : M.key
|
||||
value: M.key
|
||||
};
|
||||
var kind;
|
||||
if (M instanceof AST_ObjectKeyVal) {
|
||||
|
||||
@@ -699,6 +699,7 @@ function OutputStream(options) {
|
||||
// (false, true) ? (a = 10, b = 20) : (c = 30)
|
||||
// ==> 20 (side effect, set a := 10 and b := 20)
|
||||
|| p instanceof AST_Conditional
|
||||
// { [(1, 2)]: 3 }[2] ==> 3
|
||||
// { foo: (1, 2) }.foo ==> 2
|
||||
|| p instanceof AST_ObjectProperty
|
||||
// (1, {foo:2}).foo or (1, {foo:2})["foo"] ==> 2
|
||||
@@ -1289,25 +1290,33 @@ function OutputStream(options) {
|
||||
else print_braced_empty(this, output);
|
||||
});
|
||||
|
||||
function print_property_name(key, quote, output) {
|
||||
if (output.option("quote_keys")) {
|
||||
function print_property_key(self, output) {
|
||||
var key = self.key;
|
||||
if (key instanceof AST_Node) {
|
||||
output.with_square(function() {
|
||||
key.print(output);
|
||||
});
|
||||
} else if (output.option("quote_keys")) {
|
||||
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")) {
|
||||
output.print_string(key, quote);
|
||||
} else {
|
||||
output.print_name(key);
|
||||
}
|
||||
} else {
|
||||
output.print_string(key, quote);
|
||||
var quote = self.start && self.start.quote;
|
||||
if (RESERVED_WORDS[key] ? !output.option("ie8") : is_identifier_string(key)) {
|
||||
if (quote && output.option("keep_quoted_props")) {
|
||||
output.print_string(key, quote);
|
||||
} else {
|
||||
output.print_name(key);
|
||||
}
|
||||
} else {
|
||||
output.print_string(key, quote);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEFPRINT(AST_ObjectKeyVal, function(output) {
|
||||
var self = this;
|
||||
print_property_name(self.key, self.quote, output);
|
||||
print_property_key(self, output);
|
||||
output.colon();
|
||||
self.value.print(output);
|
||||
});
|
||||
@@ -1316,7 +1325,7 @@ function OutputStream(options) {
|
||||
var self = this;
|
||||
output.print(type);
|
||||
output.space();
|
||||
print_property_name(self.key.name, self.quote, output);
|
||||
print_property_key(self, output);
|
||||
self.value._codegen(output, true);
|
||||
};
|
||||
}
|
||||
@@ -1488,14 +1497,7 @@ function OutputStream(options) {
|
||||
output.add_mapping(this.start);
|
||||
});
|
||||
|
||||
DEFMAP([
|
||||
AST_ObjectGetter,
|
||||
AST_ObjectSetter,
|
||||
], function(output) {
|
||||
output.add_mapping(this.start, this.key.name);
|
||||
});
|
||||
|
||||
DEFMAP([ AST_ObjectProperty ], function(output) {
|
||||
output.add_mapping(this.start, this.key);
|
||||
if (typeof this.key == "string") output.add_mapping(this.start, this.key);
|
||||
});
|
||||
})();
|
||||
|
||||
118
lib/parse.js
118
lib/parse.js
@@ -753,7 +753,7 @@ function parse($TEXT, options) {
|
||||
}
|
||||
}
|
||||
|
||||
var statement = embed_tokens(function(strict_defun) {
|
||||
var statement = embed_tokens(function() {
|
||||
handle_regexp();
|
||||
switch (S.token.type) {
|
||||
case "string":
|
||||
@@ -844,9 +844,6 @@ function parse($TEXT, options) {
|
||||
return for_();
|
||||
|
||||
case "function":
|
||||
if (!strict_defun && S.input.has_directive("use strict")) {
|
||||
croak("In strict mode code, functions can only be declared at top level or immediately within another function.");
|
||||
}
|
||||
next();
|
||||
return function_(AST_Defun);
|
||||
|
||||
@@ -1038,7 +1035,7 @@ function parse($TEXT, options) {
|
||||
S.input.push_directives_stack();
|
||||
S.in_loop = 0;
|
||||
S.labels = [];
|
||||
var body = block_(true);
|
||||
var body = block_();
|
||||
if (S.input.has_directive("use strict")) {
|
||||
if (name) strict_verify_symbol(name);
|
||||
argnames.forEach(strict_verify_symbol);
|
||||
@@ -1067,12 +1064,12 @@ function parse($TEXT, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function block_(strict_defun) {
|
||||
function block_() {
|
||||
expect("{");
|
||||
var a = [];
|
||||
while (!is("punc", "}")) {
|
||||
if (is("eof")) expect_token("punc", "}");
|
||||
a.push(statement(strict_defun));
|
||||
a.push(statement());
|
||||
}
|
||||
next();
|
||||
return a;
|
||||
@@ -1222,7 +1219,7 @@ function parse($TEXT, options) {
|
||||
var tok = S.token, ret;
|
||||
switch (tok.type) {
|
||||
case "name":
|
||||
ret = _make_symbol(AST_SymbolRef);
|
||||
ret = _make_symbol(AST_SymbolRef, tok);
|
||||
break;
|
||||
case "num":
|
||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||
@@ -1340,51 +1337,62 @@ function parse($TEXT, options) {
|
||||
var first = true, a = [];
|
||||
while (!is("punc", "}")) {
|
||||
if (first) first = false; else expect(",");
|
||||
if (!options.strict && is("punc", "}"))
|
||||
// allow trailing comma
|
||||
break;
|
||||
// allow trailing comma
|
||||
if (!options.strict && is("punc", "}")) break;
|
||||
var start = S.token;
|
||||
var type = start.type;
|
||||
var name = as_property_name();
|
||||
if (type == "name" && !is("punc", ":")) {
|
||||
var key = new AST_SymbolAccessor({
|
||||
start: S.token,
|
||||
name: "" + as_property_name(),
|
||||
end: prev()
|
||||
});
|
||||
if (name == "get") {
|
||||
a.push(new AST_ObjectGetter({
|
||||
start : start,
|
||||
key : key,
|
||||
value : create_accessor(),
|
||||
end : prev()
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
if (name == "set") {
|
||||
a.push(new AST_ObjectSetter({
|
||||
start : start,
|
||||
key : key,
|
||||
value : create_accessor(),
|
||||
end : prev()
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
var key = as_property_key();
|
||||
if (is("punc", "(")) {
|
||||
var func_start = S.token;
|
||||
var func = function_(AST_Function);
|
||||
func.start = func_start;
|
||||
func.end = prev();
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start: start,
|
||||
key: key,
|
||||
value: func,
|
||||
end: prev(),
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
if (!is("punc", ":") && start.type == "name") switch (key) {
|
||||
case "get":
|
||||
a.push(new AST_ObjectGetter({
|
||||
start: start,
|
||||
key: as_property_key(),
|
||||
value: create_accessor(),
|
||||
end: prev(),
|
||||
}));
|
||||
continue;
|
||||
case "set":
|
||||
a.push(new AST_ObjectSetter({
|
||||
start: start,
|
||||
key: as_property_key(),
|
||||
value: create_accessor(),
|
||||
end: prev(),
|
||||
}));
|
||||
continue;
|
||||
default:
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start: start,
|
||||
key: key,
|
||||
value: _make_symbol(AST_SymbolRef, start),
|
||||
end: prev(),
|
||||
}));
|
||||
continue;
|
||||
}
|
||||
expect(":");
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start : start,
|
||||
quote : start.quote,
|
||||
key : "" + name,
|
||||
value : expression(false),
|
||||
end : prev()
|
||||
start: start,
|
||||
key: key,
|
||||
value: expression(false),
|
||||
end: prev(),
|
||||
}));
|
||||
}
|
||||
next();
|
||||
return new AST_Object({ properties: a });
|
||||
});
|
||||
|
||||
function as_property_name() {
|
||||
function as_property_key() {
|
||||
var tmp = S.token;
|
||||
switch (tmp.type) {
|
||||
case "operator":
|
||||
@@ -1395,7 +1403,13 @@ function parse($TEXT, options) {
|
||||
case "keyword":
|
||||
case "atom":
|
||||
next();
|
||||
return tmp.value;
|
||||
return "" + tmp.value;
|
||||
case "punc":
|
||||
if (tmp.value != "[") unexpected();
|
||||
next();
|
||||
var key = expression(false);
|
||||
expect("]");
|
||||
return key;
|
||||
default:
|
||||
unexpected();
|
||||
}
|
||||
@@ -1408,12 +1422,12 @@ function parse($TEXT, options) {
|
||||
return name;
|
||||
}
|
||||
|
||||
function _make_symbol(type) {
|
||||
var name = S.token.value;
|
||||
return new (name == "this" ? AST_This : type)({
|
||||
name : String(name),
|
||||
start : S.token,
|
||||
end : S.token
|
||||
function _make_symbol(type, token) {
|
||||
var name = token.value;
|
||||
return new (name === "this" ? AST_This : type)({
|
||||
name: "" + name,
|
||||
start: token,
|
||||
end: token,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1427,7 +1441,7 @@ function parse($TEXT, options) {
|
||||
if (!noerror) croak("Name expected");
|
||||
return null;
|
||||
}
|
||||
var sym = _make_symbol(type);
|
||||
var sym = _make_symbol(type, S.token);
|
||||
if (S.input.has_directive("use strict") && sym instanceof AST_SymbolDeclaration) {
|
||||
strict_verify_symbol(sym);
|
||||
}
|
||||
@@ -1616,7 +1630,7 @@ function parse($TEXT, options) {
|
||||
var body = [];
|
||||
S.input.push_directives_stack();
|
||||
while (!is("eof"))
|
||||
body.push(statement(true));
|
||||
body.push(statement());
|
||||
S.input.pop_directives_stack();
|
||||
var end = prev();
|
||||
var toplevel = options.toplevel;
|
||||
|
||||
@@ -81,8 +81,8 @@ var builtins = function() {
|
||||
|
||||
function reserve_quoted_keys(ast, reserved) {
|
||||
ast.walk(new TreeWalker(function(node) {
|
||||
if (node instanceof AST_ObjectKeyVal) {
|
||||
if (node.quote) add(node.key);
|
||||
if (node instanceof AST_ObjectProperty) {
|
||||
if (node.start && node.start.quote) add(node.key);
|
||||
} else if (node instanceof AST_Sub) {
|
||||
addStrings(node.property, add);
|
||||
}
|
||||
@@ -165,11 +165,8 @@ function mangle_properties(ast, options) {
|
||||
}
|
||||
} else if (node instanceof AST_Dot) {
|
||||
add(node.property);
|
||||
} else if (node instanceof AST_ObjectKeyVal) {
|
||||
add(node.key);
|
||||
} else if (node instanceof AST_ObjectProperty) {
|
||||
// setter or getter, since KeyVal is handled above
|
||||
add(node.key.name);
|
||||
if (typeof node.key == "string") add(node.key);
|
||||
} else if (node instanceof AST_Sub) {
|
||||
addStrings(node.property, add);
|
||||
}
|
||||
@@ -198,11 +195,8 @@ function mangle_properties(ast, options) {
|
||||
}
|
||||
} else if (node instanceof AST_Dot) {
|
||||
node.property = mangle(node.property);
|
||||
} else if (node instanceof AST_ObjectKeyVal) {
|
||||
node.key = mangle(node.key);
|
||||
} else if (node instanceof AST_ObjectProperty) {
|
||||
// setter or getter
|
||||
node.key.name = mangle(node.key.name);
|
||||
if (typeof node.key == "string") node.key = mangle(node.key);
|
||||
} else if (node instanceof AST_Sub) {
|
||||
if (!options.keep_quoted) mangleStrings(node.property);
|
||||
}
|
||||
|
||||
@@ -164,6 +164,7 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
self.properties = do_list(self.properties, tw);
|
||||
});
|
||||
DEF(AST_ObjectProperty, function(self, tw) {
|
||||
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
||||
self.value = self.value.transform(tw);
|
||||
});
|
||||
})(function(node, descend) {
|
||||
|
||||
@@ -143,8 +143,9 @@ function push_uniq(array, el) {
|
||||
}
|
||||
|
||||
function string_template(text, props) {
|
||||
return text.replace(/\{(.+?)\}/g, function(str, p) {
|
||||
return props && props[p];
|
||||
return text.replace(/\{([^}]+)\}/g, function(str, p) {
|
||||
var value = props[p];
|
||||
return value instanceof AST_Node ? value.print_to_string() : value;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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.11.4",
|
||||
"version": "3.11.6",
|
||||
"engines": {
|
||||
"node": ">=0.8.0"
|
||||
},
|
||||
|
||||
@@ -315,6 +315,7 @@ function test_case(test) {
|
||||
if (test.mangle.properties) U.mangle_properties(output, test.mangle.properties);
|
||||
}
|
||||
var output_code = make_code(output, output_options);
|
||||
U.AST_Node.log_function();
|
||||
if (expect != output_code) {
|
||||
log([
|
||||
"!!! failed",
|
||||
@@ -386,7 +387,7 @@ function test_case(test) {
|
||||
mangle: test.mangle
|
||||
});
|
||||
var actual = stdout[toplevel ? 1 : 0];
|
||||
if (test.expect_stdout === true) {
|
||||
if (test.expect_stdout === true || test.expect_stdout instanceof Error && test.expect_stdout.name === actual.name) {
|
||||
test.expect_stdout = actual;
|
||||
}
|
||||
if (!sandbox.same_stdout(test.expect_stdout, actual)) {
|
||||
|
||||
@@ -8577,3 +8577,28 @@ issue_4242: {
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
issue_4248: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = 0;
|
||||
try {
|
||||
a = 1;
|
||||
b[1];
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var a = 0;
|
||||
try {
|
||||
a = 1;
|
||||
b[1];
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect_stdout: "1"
|
||||
}
|
||||
|
||||
@@ -83,13 +83,11 @@ ifs_3_should_warn: {
|
||||
"WARN: Condition left of && always false [test/compress/conditionals.js:3,12]",
|
||||
"WARN: Condition always false [test/compress/conditionals.js:3,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/conditionals.js:3,34]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/conditionals.js:4,12]",
|
||||
"WARN: + in boolean context always true [test/compress/conditionals.js:10,19]",
|
||||
"WARN: Boolean || always true [test/compress/conditionals.js:10,12]",
|
||||
"WARN: Condition left of || always true [test/compress/conditionals.js:10,12]",
|
||||
"WARN: Condition always true [test/compress/conditionals.js:10,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/conditionals.js:12,15]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/conditionals.js:13,12]",
|
||||
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:3,12]",
|
||||
"WARN: Dropping side-effect-free statement [test/compress/conditionals.js:10,12]",
|
||||
]
|
||||
|
||||
@@ -1084,3 +1084,144 @@ issue_4231: {
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_4245: {
|
||||
options = {
|
||||
booleans: true,
|
||||
}
|
||||
input: {
|
||||
const a = f();
|
||||
function f() {
|
||||
typeof a;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
const a = f();
|
||||
function f() {
|
||||
a,
|
||||
1;
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_4248: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL";
|
||||
try {
|
||||
(function() {
|
||||
a = "PASS";
|
||||
b[a];
|
||||
const b = 0;
|
||||
})();
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL";
|
||||
try {
|
||||
(function() {
|
||||
a = "PASS";
|
||||
b[a];
|
||||
const b = 0;
|
||||
})();
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_4261: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_funcs: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
{
|
||||
const a = 42;
|
||||
(function() {
|
||||
function f() {
|
||||
console.log(a);
|
||||
}
|
||||
function g() {
|
||||
while (f());
|
||||
}
|
||||
(function() {
|
||||
while (g());
|
||||
})();
|
||||
})();
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
{
|
||||
const a = 42;
|
||||
(function() {
|
||||
function g() {
|
||||
while (void console.log(a));
|
||||
}
|
||||
(function() {
|
||||
while (g());
|
||||
})();
|
||||
})();
|
||||
}
|
||||
}
|
||||
expect_stdout: "42"
|
||||
}
|
||||
|
||||
issue_4274_1: {
|
||||
options = {
|
||||
loops: true,
|
||||
}
|
||||
input: {
|
||||
for (;;) {
|
||||
if (console.log("PASS")) {
|
||||
const a = 0;
|
||||
} else {
|
||||
break;
|
||||
var a;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
for (; console.log("PASS");) {
|
||||
{
|
||||
const a = 0;
|
||||
}
|
||||
var a;
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_4274_2: {
|
||||
options = {
|
||||
loops: true,
|
||||
}
|
||||
input: {
|
||||
for (;;) {
|
||||
if (!console.log("PASS")) {
|
||||
break;
|
||||
var a;
|
||||
} else {
|
||||
const a = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
for (; console.log("PASS");) {
|
||||
{
|
||||
const a = 0;
|
||||
}
|
||||
var a;
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
@@ -61,8 +61,6 @@ dead_code_2_should_warn: {
|
||||
expect_stdout: true
|
||||
expect_warnings: [
|
||||
"WARN: Dropping unreachable code [test/compress/dead-code.js:8,12]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:10,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/dead-code.js:10,16]",
|
||||
]
|
||||
node_version: "<=4"
|
||||
}
|
||||
@@ -103,11 +101,9 @@ dead_code_constant_boolean_should_warn_more: {
|
||||
"WARN: + in boolean context always true [test/compress/dead-code.js:1,33]",
|
||||
"WARN: Boolean || always true [test/compress/dead-code.js:1,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/dead-code.js:1,45]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:3,12]",
|
||||
"WARN: Boolean expression always true [test/compress/dead-code.js:6,47]",
|
||||
"WARN: Boolean && always false [test/compress/dead-code.js:6,28]",
|
||||
"WARN: Dropping unreachable code [test/compress/dead-code.js:6,63]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/dead-code.js:9,12]",
|
||||
"WARN: Dropping side-effect-free statement [test/compress/dead-code.js:1,15]",
|
||||
"WARN: Dropping side-effect-free statement [test/compress/dead-code.js:6,28]",
|
||||
]
|
||||
|
||||
@@ -3047,3 +3047,30 @@ issue_4214: {
|
||||
}
|
||||
expect_stdout: "NaN"
|
||||
}
|
||||
|
||||
issue_4271: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
unsafe: true,
|
||||
}
|
||||
input: {
|
||||
({
|
||||
p: null,
|
||||
q: (console.log("foo"), 42),
|
||||
p: function() {}
|
||||
})[console.log("bar"), "p"] && console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
({
|
||||
p: null,
|
||||
q: (console.log("foo"), 42),
|
||||
p: function() {}
|
||||
})[console.log("bar"), "p"],
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"bar",
|
||||
"PASS",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2081,7 +2081,7 @@ issue_3016_1: {
|
||||
var b = 1;
|
||||
do {
|
||||
3[b];
|
||||
} while(0);
|
||||
} while (0);
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "1"
|
||||
@@ -2112,7 +2112,7 @@ issue_3016_2: {
|
||||
do {
|
||||
a = 3,
|
||||
a[b];
|
||||
} while(0);
|
||||
} while (0);
|
||||
var a;
|
||||
console.log(b);
|
||||
}
|
||||
@@ -2145,7 +2145,7 @@ issue_3016_2_ie8: {
|
||||
do {
|
||||
a = 3,
|
||||
a[b];
|
||||
} while(0);
|
||||
} while (0);
|
||||
var a;
|
||||
console.log(b);
|
||||
}
|
||||
@@ -2175,7 +2175,7 @@ issue_3016_3: {
|
||||
var b = 1;
|
||||
do {
|
||||
console.log((a = void 0, a ? "FAIL" : a = "PASS"));
|
||||
} while(b--);
|
||||
} while (b--);
|
||||
var a;
|
||||
}
|
||||
expect_stdout: [
|
||||
@@ -2208,7 +2208,7 @@ issue_3016_3_ie8: {
|
||||
var b = 1;
|
||||
do {
|
||||
console.log((a = void 0, a ? "FAIL" : a = "PASS"));
|
||||
} while(b--);
|
||||
} while (b--);
|
||||
var a;
|
||||
}
|
||||
expect_stdout: [
|
||||
@@ -5115,3 +5115,102 @@ issue_4233: {
|
||||
}
|
||||
expect_stdout: "number"
|
||||
}
|
||||
|
||||
issue_4259: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
functions: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
var a = function b() {
|
||||
var c = b;
|
||||
for (b in c);
|
||||
};
|
||||
a();
|
||||
console.log(typeof a);
|
||||
}
|
||||
expect: {
|
||||
function a() {
|
||||
for (a in a);
|
||||
}
|
||||
a();
|
||||
console.log(typeof a);
|
||||
}
|
||||
expect_stdout: "function"
|
||||
}
|
||||
|
||||
issue_4261: {
|
||||
options = {
|
||||
inline: true,
|
||||
reduce_funcs: true,
|
||||
reduce_vars: true,
|
||||
toplevel: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
try {
|
||||
throw 42;
|
||||
} catch (e) {
|
||||
(function() {
|
||||
function f() {
|
||||
e.p;
|
||||
}
|
||||
function g() {
|
||||
while (f());
|
||||
}
|
||||
(function() {
|
||||
while (console.log(g()));
|
||||
})();
|
||||
})();
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
try {
|
||||
throw 42;
|
||||
} catch (e) {
|
||||
(function() {
|
||||
function g() {
|
||||
while (void e.p);
|
||||
}
|
||||
(function() {
|
||||
while (console.log(g()));
|
||||
})();
|
||||
})();
|
||||
}
|
||||
}
|
||||
expect_stdout: true
|
||||
}
|
||||
|
||||
issue_4265: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
dead_code: true,
|
||||
inline: true,
|
||||
sequences: true,
|
||||
}
|
||||
input: {
|
||||
function f() {
|
||||
console;
|
||||
if ([ function() {
|
||||
return this + console.log(a);
|
||||
a;
|
||||
var a;
|
||||
}() ]);
|
||||
return 0;
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect: {
|
||||
function f() {
|
||||
return console, function() {
|
||||
return console.log(a);
|
||||
var a;
|
||||
}(), 0;
|
||||
}
|
||||
f();
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
@@ -2877,3 +2877,30 @@ issue_4235: {
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
issue_4250: {
|
||||
options = {
|
||||
ie8: true,
|
||||
loops: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function f() {
|
||||
(function() {
|
||||
for (f in "f");
|
||||
})();
|
||||
return f;
|
||||
var f;
|
||||
}());
|
||||
}
|
||||
expect: {
|
||||
console.log(function f() {
|
||||
(function() {
|
||||
for (f in "f");
|
||||
})();
|
||||
return f;
|
||||
var f;
|
||||
}());
|
||||
}
|
||||
expect_stdout: "0"
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ non_hoisted_function_after_return: {
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:4,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]",
|
||||
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:11,21]"
|
||||
"WARN: Dropping unused function UnusedFunction [test/compress/issue-1034.js:11,21]",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -84,15 +84,12 @@ non_hoisted_function_after_return_2a: {
|
||||
}
|
||||
}
|
||||
expect_warnings: [
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:4,16]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:4,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:4,16]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:7,16]",
|
||||
"WARN: Dropping unused variable a [test/compress/issue-1034.js:4,20]",
|
||||
"WARN: Dropping unused function nope [test/compress/issue-1034.js:11,21]",
|
||||
"INFO: pass 0: last_count: Infinity, count: 35",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
|
||||
"INFO: Dropping unused variable b [test/compress/issue-1034.js:7,20]",
|
||||
"INFO: Dropping unused variable c [test/compress/issue-1034.js:9,16]",
|
||||
@@ -138,10 +135,7 @@ non_hoisted_function_after_return_2b: {
|
||||
}
|
||||
}
|
||||
expect_warnings: [
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:6,16]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:6,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:8,12]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,12]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:8,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:12,12]",
|
||||
]
|
||||
}
|
||||
@@ -242,15 +236,12 @@ non_hoisted_function_after_return_2a_strict: {
|
||||
}
|
||||
expect_stdout: "5 6"
|
||||
expect_warnings: [
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:5,16]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:5,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:8,16]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:8,16]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:5,16]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:8,16]",
|
||||
"WARN: Dropping unused variable a [test/compress/issue-1034.js:5,20]",
|
||||
"WARN: Dropping unused function nope [test/compress/issue-1034.js:12,21]",
|
||||
"INFO: pass 0: last_count: Infinity, count: 46",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:10,12]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:10,12]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:10,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
|
||||
"INFO: Dropping unused variable b [test/compress/issue-1034.js:8,20]",
|
||||
"INFO: Dropping unused variable c [test/compress/issue-1034.js:10,16]",
|
||||
@@ -301,10 +292,7 @@ non_hoisted_function_after_return_2b_strict: {
|
||||
}
|
||||
expect_stdout: "5 6"
|
||||
expect_warnings: [
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:7,16]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:7,16]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Declarations in unreachable code! [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Dropping initialization in unreachable code [test/compress/issue-1034.js:9,12]",
|
||||
"WARN: Dropping unreachable code [test/compress/issue-1034.js:13,12]",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -893,3 +893,116 @@ issue_4231: {
|
||||
expect_stdout: "function"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4245: {
|
||||
options = {
|
||||
booleans: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
let a = f();
|
||||
function f() {
|
||||
typeof a;
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
let a = f();
|
||||
function f() {
|
||||
a,
|
||||
1;
|
||||
}
|
||||
}
|
||||
expect_stdout: ReferenceError("a is not defined")
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4248: {
|
||||
options = {
|
||||
collapse_vars: true,
|
||||
}
|
||||
input: {
|
||||
var a = "FAIL";
|
||||
try {
|
||||
(function() {
|
||||
"use strict";
|
||||
a = "PASS";
|
||||
b[a];
|
||||
let b;
|
||||
})();
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var a = "FAIL";
|
||||
try {
|
||||
(function() {
|
||||
"use strict";
|
||||
a = "PASS";
|
||||
b[a];
|
||||
let b;
|
||||
})();
|
||||
} catch (e) {
|
||||
console.log(a);
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4274_1: {
|
||||
options = {
|
||||
loops: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
for (;;) {
|
||||
if (console.log("PASS")) {
|
||||
let a;
|
||||
} else {
|
||||
break;
|
||||
var a;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
for (; console.log("PASS");) {
|
||||
{
|
||||
let a;
|
||||
}
|
||||
var a;
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4274_2: {
|
||||
options = {
|
||||
loops: true,
|
||||
}
|
||||
input: {
|
||||
"use strict";
|
||||
for (;;) {
|
||||
if (!console.log("PASS")) {
|
||||
break;
|
||||
var a;
|
||||
} else {
|
||||
let a;
|
||||
}
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
"use strict";
|
||||
for (; console.log("PASS");) {
|
||||
{
|
||||
let a;
|
||||
}
|
||||
var a;
|
||||
}
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
@@ -3092,3 +3092,94 @@ issue_4237_2: {
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
}
|
||||
|
||||
issue_4253: {
|
||||
options = {
|
||||
merge_vars: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
switch (0) {
|
||||
default:
|
||||
var a = "FAIL";
|
||||
a = a && a;
|
||||
try {
|
||||
break;
|
||||
} catch (e) {}
|
||||
var b = 42;
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect: {
|
||||
switch (0) {
|
||||
default:
|
||||
var a = "FAIL";
|
||||
a = a && a;
|
||||
try {
|
||||
break;
|
||||
} catch (e) {}
|
||||
var b = 42;
|
||||
}
|
||||
console.log(b);
|
||||
}
|
||||
expect_stdout: "undefined"
|
||||
}
|
||||
|
||||
issue_4255: {
|
||||
options = {
|
||||
dead_code: true,
|
||||
loops: true,
|
||||
merge_vars: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
L: for (var a = 2; --a;)
|
||||
for (var b = 0; console.log(b); --b)
|
||||
break L;
|
||||
}
|
||||
expect: {
|
||||
L: for (var a = 2; --a;) {
|
||||
var b = 0;
|
||||
if (console.log(b))
|
||||
break L;
|
||||
}
|
||||
}
|
||||
expect_stdout: "0"
|
||||
}
|
||||
|
||||
issue_4257: {
|
||||
options = {
|
||||
merge_vars: true,
|
||||
toplevel: true,
|
||||
}
|
||||
input: {
|
||||
var a = 0;
|
||||
for (var i = 0; i < 2; i++)
|
||||
switch (--a) {
|
||||
case 0:
|
||||
var b = 0;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
var c = 1 + (0 | (b && A));
|
||||
console.log(c);
|
||||
}
|
||||
}
|
||||
expect: {
|
||||
var a = 0;
|
||||
for (var i = 0; i < 2; i++)
|
||||
switch (--a) {
|
||||
case 0:
|
||||
var b = 0;
|
||||
break;
|
||||
case 0:
|
||||
default:
|
||||
var c = 1 + (0 | (b && A));
|
||||
console.log(c);
|
||||
}
|
||||
}
|
||||
expect_stdout: [
|
||||
"1",
|
||||
"1",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -221,3 +221,142 @@ numeric_literal: {
|
||||
"8 7 8",
|
||||
]
|
||||
}
|
||||
|
||||
evaluate_computed_key: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
objects: true,
|
||||
}
|
||||
input: {
|
||||
console.log({
|
||||
["foo" + "bar"]: "PASS",
|
||||
}.foobar);
|
||||
}
|
||||
expect: {
|
||||
console.log({
|
||||
foobar: "PASS",
|
||||
}.foobar);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
keep_computed_key: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
({
|
||||
[console.log("PASS")]: 42,
|
||||
});
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4269_1: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
objects: true,
|
||||
}
|
||||
input: {
|
||||
console.log({
|
||||
get 0() {
|
||||
return "FAIL";
|
||||
},
|
||||
[0]: "PASS",
|
||||
}[0]);
|
||||
}
|
||||
expect: {
|
||||
console.log({
|
||||
get 0() {
|
||||
return "FAIL";
|
||||
},
|
||||
[0]: "PASS",
|
||||
}[0]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4269_2: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
objects: true,
|
||||
}
|
||||
input: {
|
||||
console.log({
|
||||
get [0]() {
|
||||
return "FAIL";
|
||||
},
|
||||
0: "PASS",
|
||||
}[0]);
|
||||
}
|
||||
expect: {
|
||||
console.log({
|
||||
get [0]() {
|
||||
return "FAIL";
|
||||
},
|
||||
0: "PASS",
|
||||
}[0]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4269_3: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
objects: true,
|
||||
}
|
||||
input: {
|
||||
console.log({
|
||||
["foo"]: "bar",
|
||||
get 42() {
|
||||
return "FAIL";
|
||||
},
|
||||
42: "PASS",
|
||||
}[42]);
|
||||
}
|
||||
expect: {
|
||||
console.log({
|
||||
["foo"]: "bar",
|
||||
get 42() {
|
||||
return "FAIL";
|
||||
},
|
||||
42: "PASS",
|
||||
}[42]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
issue_4269_4: {
|
||||
options = {
|
||||
evaluate: true,
|
||||
objects: true,
|
||||
}
|
||||
input: {
|
||||
console.log({
|
||||
get 42() {
|
||||
return "FAIL";
|
||||
},
|
||||
["foo"]: "bar",
|
||||
42: "PASS",
|
||||
}[42]);
|
||||
}
|
||||
expect: {
|
||||
console.log({
|
||||
get 42() {
|
||||
return "FAIL";
|
||||
},
|
||||
["foo"]: "bar",
|
||||
42: "PASS",
|
||||
}[42]);
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
@@ -1999,7 +1999,7 @@ issue_1606: {
|
||||
var a, b;
|
||||
function g(){};
|
||||
b = 2;
|
||||
x(b);
|
||||
x(2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ describe("tokens", function() {
|
||||
it("Should give correct positions for accessors", function() {
|
||||
// location 0 1 2 3 4
|
||||
// 01234567890123456789012345678901234567890123456789
|
||||
var ast = UglifyJS.parse("var obj = { get latest() { return undefined; } }");
|
||||
var ast = UglifyJS.parse("var obj = { get [prop]() { return undefined; } }");
|
||||
// test all AST_ObjectProperty tokens are set as expected
|
||||
var found = false;
|
||||
ast.walk(new UglifyJS.TreeWalker(function(node) {
|
||||
@@ -13,9 +13,9 @@ describe("tokens", function() {
|
||||
found = true;
|
||||
assert.equal(node.start.pos, 12);
|
||||
assert.equal(node.end.endpos, 46);
|
||||
assert(node.key instanceof UglifyJS.AST_SymbolAccessor);
|
||||
assert.equal(node.key.start.pos, 16);
|
||||
assert.equal(node.key.end.endpos, 22);
|
||||
assert(node.key instanceof UglifyJS.AST_SymbolRef);
|
||||
assert.equal(node.key.start.pos, 17);
|
||||
assert.equal(node.key.end.endpos, 21);
|
||||
assert(node.value instanceof UglifyJS.AST_Accessor);
|
||||
assert.equal(node.value.start.pos, 22);
|
||||
assert.equal(node.value.end.endpos, 46);
|
||||
|
||||
@@ -593,7 +593,7 @@ function is_error(result) {
|
||||
}
|
||||
|
||||
function is_timed_out(result) {
|
||||
return is_error(result) && /timed out/.test(result);
|
||||
return is_error(result) && /timed out/.test(result.message);
|
||||
}
|
||||
|
||||
function is_statement(node) {
|
||||
|
||||
@@ -276,6 +276,7 @@ var NO_DEFUN = false;
|
||||
var DEFUN_OK = true;
|
||||
var DONT_STORE = true;
|
||||
var NO_CONST = true;
|
||||
var NO_DUPLICATE = true;
|
||||
|
||||
var VAR_NAMES = [
|
||||
"a",
|
||||
@@ -356,11 +357,15 @@ function createFunctions(n, recurmax, allowDefun, canThrow, stmtDepth) {
|
||||
return s;
|
||||
}
|
||||
|
||||
function createParams() {
|
||||
function createParams(noDuplicate) {
|
||||
var len = unique_vars.length;
|
||||
var params = [];
|
||||
for (var n = rng(4); --n >= 0;) {
|
||||
params.push(createVarName(MANDATORY));
|
||||
var name = createVarName(MANDATORY);
|
||||
if (noDuplicate) unique_vars.push(name);
|
||||
params.push(name);
|
||||
}
|
||||
unique_vars.length = len;
|
||||
return params.join(", ");
|
||||
}
|
||||
|
||||
@@ -908,21 +913,27 @@ function getDotKey(assign) {
|
||||
return key;
|
||||
}
|
||||
|
||||
function createAccessor(recurmax, stmtDepth, canThrow) {
|
||||
function createObjectKey(recurmax, stmtDepth, canThrow) {
|
||||
return rng(10) ? KEYS[rng(KEYS.length)] : "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]";
|
||||
}
|
||||
|
||||
function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
var namesLenBefore = VAR_NAMES.length;
|
||||
var s;
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
var prop1 = getDotKey();
|
||||
if (rng(2) == 0) {
|
||||
switch (rng(3)) {
|
||||
case 0:
|
||||
s = [
|
||||
"get " + prop1 + "(){",
|
||||
"get " + createObjectKey(recurmax, stmtDepth, canThrow) + "(){",
|
||||
strictMode(),
|
||||
defns(),
|
||||
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||
createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
|
||||
"},"
|
||||
"},",
|
||||
];
|
||||
} else {
|
||||
break;
|
||||
case 1:
|
||||
var prop1 = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||
var prop2;
|
||||
do {
|
||||
prop2 = getDotKey();
|
||||
@@ -933,8 +944,18 @@ function createAccessor(recurmax, stmtDepth, canThrow) {
|
||||
defns(),
|
||||
_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||
"this." + prop2 + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
|
||||
"},"
|
||||
"},",
|
||||
];
|
||||
break;
|
||||
default:
|
||||
s = [
|
||||
createObjectKey(recurmax, stmtDepth, canThrow) + "(" + createParams(NO_DUPLICATE) + "){",
|
||||
strictMode(),
|
||||
defns(),
|
||||
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||
"},",
|
||||
]
|
||||
break;
|
||||
}
|
||||
});
|
||||
VAR_NAMES.length = namesLenBefore;
|
||||
@@ -944,13 +965,16 @@ function createAccessor(recurmax, stmtDepth, canThrow) {
|
||||
function createObjectLiteral(recurmax, stmtDepth, canThrow) {
|
||||
recurmax--;
|
||||
var obj = ["({"];
|
||||
for (var i = rng(6); --i >= 0;) {
|
||||
if (rng(20) == 0) {
|
||||
obj.push(createAccessor(recurmax, stmtDepth, canThrow));
|
||||
} else {
|
||||
var key = KEYS[rng(KEYS.length)];
|
||||
obj.push(key + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "),");
|
||||
}
|
||||
for (var i = rng(6); --i >= 0;) switch (rng(30)) {
|
||||
case 0:
|
||||
obj.push(createObjectFunction(recurmax, stmtDepth, canThrow));
|
||||
break;
|
||||
case 1:
|
||||
obj.push(getVarName() + ",");
|
||||
break;
|
||||
default:
|
||||
obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ":(" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "),");
|
||||
break;
|
||||
}
|
||||
obj.push("})");
|
||||
return obj.join("\n");
|
||||
@@ -1369,7 +1393,12 @@ for (var round = 1; round <= num_iterations; round++) {
|
||||
}
|
||||
}
|
||||
// ignore difference in error message caused by Temporal Dead Zone
|
||||
if (!ok && errored) ok = uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError";
|
||||
if (!ok && errored && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true;
|
||||
// ignore spurious time-outs
|
||||
if (!ok && errored && /timed out/.test(original_result.message) && !/timed out/.test(uglify_result.message)) {
|
||||
if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = sandbox.run_code(original_code, toplevel, 10000);
|
||||
ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result);
|
||||
}
|
||||
// ignore difference in error message caused by `in`
|
||||
// ignore difference in depth of termination caused by infinite recursion
|
||||
if (!ok) {
|
||||
|
||||
Reference in New Issue
Block a user