Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5c84dfa151 | ||
|
|
5359900b78 | ||
|
|
739fa266f8 | ||
|
|
da24dfb59e | ||
|
|
a2f27c7640 | ||
|
|
3c556b8689 | ||
|
|
7110c6923b | ||
|
|
b27b6807cb | ||
|
|
ba6e29d6fd | ||
|
|
d4685640a0 |
10
README.md
10
README.md
@@ -769,6 +769,9 @@ to be `false` and all symbol names will be omitted.
|
|||||||
|
|
||||||
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
- `switches` (default: `true`) -- de-duplicate and remove unreachable `switch` branches
|
||||||
|
|
||||||
|
- `templates` (default: `true`) -- compact template literals by embedding expressions
|
||||||
|
and/or converting to string literals, e.g. `` `foo ${42}` → "foo 42"``
|
||||||
|
|
||||||
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
|
- `top_retain` (default: `null`) -- prevent specific toplevel functions and
|
||||||
variables from `unused` removal (can be array, comma-separated, RegExp or
|
variables from `unused` removal (can be array, comma-separated, RegExp or
|
||||||
function. Implies `toplevel`)
|
function. Implies `toplevel`)
|
||||||
@@ -1260,3 +1263,10 @@ To allow for better optimizations, the compiler makes various assumptions:
|
|||||||
// TypeError: can't convert BigInt to number
|
// TypeError: can't convert BigInt to number
|
||||||
```
|
```
|
||||||
UglifyJS may modify the input which in turn may suppress those errors.
|
UglifyJS may modify the input which in turn may suppress those errors.
|
||||||
|
- Some versions of JavaScript will throw `SyntaxError` with the
|
||||||
|
following:
|
||||||
|
```javascript
|
||||||
|
console.log(String.raw`\uFo`);
|
||||||
|
// SyntaxError: Invalid Unicode escape sequence
|
||||||
|
```
|
||||||
|
UglifyJS may modify the input which in turn may suppress those errors.
|
||||||
|
|||||||
28
lib/ast.js
28
lib/ast.js
@@ -1418,6 +1418,34 @@ var AST_This = DEFNODE("This", null, {
|
|||||||
},
|
},
|
||||||
}, AST_Symbol);
|
}, AST_Symbol);
|
||||||
|
|
||||||
|
var AST_Template = DEFNODE("Template", "expressions strings tag", {
|
||||||
|
$documentation: "A template literal, i.e. tag`str1${expr1}...strN${exprN}strN+1`",
|
||||||
|
$propdoc: {
|
||||||
|
expressions: "[AST_Node*] the placeholder expressions",
|
||||||
|
strings: "[string*] the raw text segments",
|
||||||
|
tag: "[AST_Node] tag function, or null if absent",
|
||||||
|
},
|
||||||
|
walk: function(visitor) {
|
||||||
|
var node = this;
|
||||||
|
visitor.visit(node, function() {
|
||||||
|
if (node.tag) node.tag.walk(visitor);
|
||||||
|
node.expressions.forEach(function(expr) {
|
||||||
|
expr.walk(visitor);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
},
|
||||||
|
_validate: function() {
|
||||||
|
if (this.expressions.length + 1 != this.strings.length) {
|
||||||
|
throw new Error("malformed template with " + this.expressions.length + " placeholder(s) but " + this.strings.length + " text segment(s)");
|
||||||
|
}
|
||||||
|
must_be_expressions(this, "expressions");
|
||||||
|
this.strings.forEach(function(string) {
|
||||||
|
if (typeof string != "string") throw new Error("strings must contain string");
|
||||||
|
});
|
||||||
|
if (this.tag != null) must_be_expression(this, "tag");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
var AST_Constant = DEFNODE("Constant", null, {
|
var AST_Constant = DEFNODE("Constant", null, {
|
||||||
$documentation: "Base class for all constants",
|
$documentation: "Base class for all constants",
|
||||||
});
|
});
|
||||||
|
|||||||
140
lib/compress.js
140
lib/compress.js
@@ -91,6 +91,7 @@ function Compressor(options, false_by_default) {
|
|||||||
spreads : !false_by_default,
|
spreads : !false_by_default,
|
||||||
strings : !false_by_default,
|
strings : !false_by_default,
|
||||||
switches : !false_by_default,
|
switches : !false_by_default,
|
||||||
|
templates : !false_by_default,
|
||||||
top_retain : null,
|
top_retain : null,
|
||||||
toplevel : !!(options && options["top_retain"]),
|
toplevel : !!(options && options["top_retain"]),
|
||||||
typeofs : !false_by_default,
|
typeofs : !false_by_default,
|
||||||
@@ -1842,7 +1843,7 @@ merge(Compressor.prototype, {
|
|||||||
if (node instanceof AST_Call) {
|
if (node instanceof AST_Call) {
|
||||||
if (!(lhs instanceof AST_PropAccess)) return false;
|
if (!(lhs instanceof AST_PropAccess)) return false;
|
||||||
if (!lhs.equivalent_to(node.expression)) return false;
|
if (!lhs.equivalent_to(node.expression)) return false;
|
||||||
return !(rvalue instanceof AST_Function && !rvalue.contains_this());
|
return !(is_function(rvalue) && !rvalue.contains_this());
|
||||||
}
|
}
|
||||||
if (node instanceof AST_Debugger) return true;
|
if (node instanceof AST_Debugger) return true;
|
||||||
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
|
if (node instanceof AST_Defun) return funarg && lhs.name === node.name.name;
|
||||||
@@ -1941,6 +1942,7 @@ merge(Compressor.prototype, {
|
|||||||
var def = node.definition();
|
var def = node.definition();
|
||||||
return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
|
return (in_try || def.scope.resolve() !== scope) && !can_drop_symbol(node);
|
||||||
}
|
}
|
||||||
|
if (node instanceof AST_Template) return node.tag && !is_raw_tag(compressor, node.tag);
|
||||||
if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
|
if (node instanceof AST_This) return symbol_in_lvalues(node, parent);
|
||||||
if (node instanceof AST_VarDef) {
|
if (node instanceof AST_VarDef) {
|
||||||
if (check_destructured(node.name)) return true;
|
if (check_destructured(node.name)) return true;
|
||||||
@@ -2480,7 +2482,7 @@ merge(Compressor.prototype, {
|
|||||||
if (modify_toplevel) return;
|
if (modify_toplevel) return;
|
||||||
var exp = node.expression;
|
var exp = node.expression;
|
||||||
if (exp instanceof AST_PropAccess) return;
|
if (exp instanceof AST_PropAccess) return;
|
||||||
if (exp instanceof AST_Function && !exp.contains_this()) return;
|
if (is_function(exp) && !exp.contains_this()) return;
|
||||||
modify_toplevel = true;
|
modify_toplevel = true;
|
||||||
} else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
|
} else if (node instanceof AST_PropAccess && may_be_global(node.expression)) {
|
||||||
if (node === lhs && !(expr instanceof AST_Unary)) {
|
if (node === lhs && !(expr instanceof AST_Unary)) {
|
||||||
@@ -3439,7 +3441,7 @@ merge(Compressor.prototype, {
|
|||||||
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
|
if (is_undeclared_ref(this) && this.is_declared(compressor)) return false;
|
||||||
if (this.is_immutable()) return false;
|
if (this.is_immutable()) return false;
|
||||||
var def = this.definition();
|
var def = this.definition();
|
||||||
if (is_arguments(def) && all(def.scope.argnames, function(argname) {
|
if (is_arguments(def) && !def.scope.rest && all(def.scope.argnames, function(argname) {
|
||||||
return argname instanceof AST_SymbolFunarg;
|
return argname instanceof AST_SymbolFunarg;
|
||||||
})) return def.scope.uses_arguments > 2;
|
})) return def.scope.uses_arguments > 2;
|
||||||
var fixed = this.fixed_value();
|
var fixed = this.fixed_value();
|
||||||
@@ -3689,6 +3691,9 @@ merge(Compressor.prototype, {
|
|||||||
delete this.is_string;
|
delete this.is_string;
|
||||||
return result;
|
return result;
|
||||||
});
|
});
|
||||||
|
def(AST_Template, function(compressor) {
|
||||||
|
return !this.tag || is_raw_tag(compressor, this.tag);
|
||||||
|
});
|
||||||
def(AST_UnaryPrefix, function() {
|
def(AST_UnaryPrefix, function() {
|
||||||
return this.operator == "typeof";
|
return this.operator == "typeof";
|
||||||
});
|
});
|
||||||
@@ -3698,14 +3703,6 @@ merge(Compressor.prototype, {
|
|||||||
|
|
||||||
var lazy_op = makePredicate("&& ||");
|
var lazy_op = makePredicate("&& ||");
|
||||||
|
|
||||||
function is_lhs(node, parent) {
|
|
||||||
if (parent instanceof AST_Assign) return parent.left === node && node;
|
|
||||||
if (parent instanceof AST_DefaultValue) return parent.name === node && node;
|
|
||||||
if (parent instanceof AST_Destructured) return node;
|
|
||||||
if (parent instanceof AST_DestructuredKeyVal) return node;
|
|
||||||
if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression;
|
|
||||||
}
|
|
||||||
|
|
||||||
(function(def) {
|
(function(def) {
|
||||||
function to_node(value, orig) {
|
function to_node(value, orig) {
|
||||||
if (value instanceof AST_Node) return value.clone(true);
|
if (value instanceof AST_Node) return value.clone(true);
|
||||||
@@ -4063,7 +4060,7 @@ merge(Compressor.prototype, {
|
|||||||
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
key = key._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (key === prop.key) return this;
|
if (key === prop.key) return this;
|
||||||
}
|
}
|
||||||
if (prop.value instanceof AST_Function && typeof Object.prototype[key] == "function") return this;
|
if (key == "toString" || key == "valueOf") return this;
|
||||||
val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
|
val[key] = prop.value._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
if (val[key] === prop.value) return this;
|
if (val[key] === prop.value) return this;
|
||||||
}
|
}
|
||||||
@@ -4298,6 +4295,16 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
});
|
});
|
||||||
|
function eval_all(nodes, compressor, ignore_side_effects, cached, depth) {
|
||||||
|
var values = [];
|
||||||
|
for (var i = 0; i < nodes.length; i++) {
|
||||||
|
var node = nodes[i];
|
||||||
|
var value = node._eval(compressor, ignore_side_effects, cached, depth);
|
||||||
|
if (node === value) return;
|
||||||
|
values.push(value);
|
||||||
|
}
|
||||||
|
return values;
|
||||||
|
}
|
||||||
def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
|
def(AST_Call, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
||||||
@@ -4305,7 +4312,7 @@ merge(Compressor.prototype, {
|
|||||||
if (fn.evaluating) return this;
|
if (fn.evaluating) return this;
|
||||||
if (fn.name && fn.name.definition().recursive_refs > 0) return this;
|
if (fn.name && fn.name.definition().recursive_refs > 0) return this;
|
||||||
if (this.is_expr_pure(compressor)) return this;
|
if (this.is_expr_pure(compressor)) return this;
|
||||||
var args = eval_args(this.args);
|
var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
|
||||||
if (!all(fn.argnames, function(sym, index) {
|
if (!all(fn.argnames, function(sym, index) {
|
||||||
if (sym instanceof AST_DefaultValue) {
|
if (sym instanceof AST_DefaultValue) {
|
||||||
if (!args) return false;
|
if (!args) return false;
|
||||||
@@ -4323,6 +4330,7 @@ merge(Compressor.prototype, {
|
|||||||
if (ignore_side_effects) {
|
if (ignore_side_effects) {
|
||||||
fn.walk(scan_modified);
|
fn.walk(scan_modified);
|
||||||
var found = false;
|
var found = false;
|
||||||
|
fn.evaluating = true;
|
||||||
walk_body(fn, new TreeWalker(function(node) {
|
walk_body(fn, new TreeWalker(function(node) {
|
||||||
if (found) return true;
|
if (found) return true;
|
||||||
if (node instanceof AST_Return) {
|
if (node instanceof AST_Return) {
|
||||||
@@ -4333,6 +4341,7 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
if (node instanceof AST_Scope && node !== fn) return true;
|
if (node instanceof AST_Scope && node !== fn) return true;
|
||||||
}));
|
}));
|
||||||
|
delete fn.evaluating;
|
||||||
if (!found) return;
|
if (!found) return;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
@@ -4380,7 +4389,7 @@ merge(Compressor.prototype, {
|
|||||||
if (!native_fn || !native_fn[key]) return this;
|
if (!native_fn || !native_fn[key]) return this;
|
||||||
if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
|
if (val instanceof RegExp && val.global && !(e instanceof AST_RegExp)) return this;
|
||||||
}
|
}
|
||||||
var args = eval_args(this.args);
|
var args = eval_all(this.args, compressor, ignore_side_effects, cached, depth);
|
||||||
if (!args) return this;
|
if (!args) return this;
|
||||||
if (key == "replace" && typeof args[1] == "function") return this;
|
if (key == "replace" && typeof args[1] == "function") return this;
|
||||||
try {
|
try {
|
||||||
@@ -4397,19 +4406,35 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
||||||
function eval_args(args) {
|
|
||||||
var values = [];
|
|
||||||
for (var i = 0; i < args.length; i++) {
|
|
||||||
var arg = args[i];
|
|
||||||
var value = arg._eval(compressor, ignore_side_effects, cached, depth);
|
|
||||||
if (arg === value) return;
|
|
||||||
values.push(value);
|
|
||||||
}
|
|
||||||
return values;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
def(AST_New, return_this);
|
def(AST_New, return_this);
|
||||||
|
def(AST_Template, function(compressor, ignore_side_effects, cached, depth) {
|
||||||
|
if (!compressor.option("templates")) return this;
|
||||||
|
if (this.tag) {
|
||||||
|
if (!is_raw_tag(compressor, this.tag)) return this;
|
||||||
|
decode = function(str) {
|
||||||
|
return str;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var exprs = eval_all(this.expressions, compressor, ignore_side_effects, cached, depth);
|
||||||
|
if (!exprs) return this;
|
||||||
|
var malformed = false;
|
||||||
|
var ret = decode(this.strings[0]);
|
||||||
|
for (var i = 0; i < exprs.length; i++) {
|
||||||
|
ret += exprs[i] + decode(this.strings[i + 1]);
|
||||||
|
}
|
||||||
|
if (!malformed) return ret;
|
||||||
|
this._eval = return_this;
|
||||||
|
return this;
|
||||||
|
|
||||||
|
function decode(str) {
|
||||||
|
return str.replace(/\\(u\{[^}]*\}?|u[\s\S]{0,4}|x[\s\S]{0,2}|[0-9]+|[\s\S])/g, function(match, seq) {
|
||||||
|
var s = decode_escape_sequence(seq);
|
||||||
|
if (typeof s != "string") malformed = true;
|
||||||
|
return s;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
})(function(node, func) {
|
})(function(node, func) {
|
||||||
node.DEFMETHOD("_eval", func);
|
node.DEFMETHOD("_eval", func);
|
||||||
});
|
});
|
||||||
@@ -4663,6 +4688,9 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_SymbolRef, function(compressor) {
|
def(AST_SymbolRef, function(compressor) {
|
||||||
return !this.is_declared(compressor) || !can_drop_symbol(this);
|
return !this.is_declared(compressor) || !can_drop_symbol(this);
|
||||||
});
|
});
|
||||||
|
def(AST_Template, function(compressor) {
|
||||||
|
return this.tag && !is_raw_tag(compressor, this.tag) || any(this.expressions, compressor);
|
||||||
|
});
|
||||||
def(AST_This, return_false);
|
def(AST_This, return_false);
|
||||||
def(AST_Try, function(compressor) {
|
def(AST_Try, function(compressor) {
|
||||||
return any(this.body, compressor)
|
return any(this.body, compressor)
|
||||||
@@ -4673,7 +4701,7 @@ merge(Compressor.prototype, {
|
|||||||
return unary_side_effects[this.operator]
|
return unary_side_effects[this.operator]
|
||||||
|| this.expression.has_side_effects(compressor);
|
|| this.expression.has_side_effects(compressor);
|
||||||
});
|
});
|
||||||
def(AST_VarDef, function(compressor) {
|
def(AST_VarDef, function() {
|
||||||
return this.value;
|
return this.value;
|
||||||
});
|
});
|
||||||
})(function(node, func) {
|
})(function(node, func) {
|
||||||
@@ -4973,6 +5001,7 @@ merge(Compressor.prototype, {
|
|||||||
var stat = self.body[i];
|
var stat = self.body[i];
|
||||||
if (stat instanceof AST_Directive) continue;
|
if (stat instanceof AST_Directive) continue;
|
||||||
if (stat instanceof AST_Return) {
|
if (stat instanceof AST_Return) {
|
||||||
|
if (i != self.body.length - 1) break;
|
||||||
var call = stat.value;
|
var call = stat.value;
|
||||||
if (!call || call.TYPE != "Call") break;
|
if (!call || call.TYPE != "Call") break;
|
||||||
if (call.is_expr_pure(compressor)) break;
|
if (call.is_expr_pure(compressor)) break;
|
||||||
@@ -5821,10 +5850,10 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
} else if (compressor.option("functions")
|
} else if (compressor.option("functions")
|
||||||
&& !compressor.option("ie8")
|
&& !compressor.option("ie8")
|
||||||
&& !(node instanceof AST_Const || node instanceof AST_Let)
|
&& node instanceof AST_Var
|
||||||
&& var_defs[sym.id] == 1
|
&& var_defs[sym.id] == 1
|
||||||
&& sym.assignments == 0
|
&& sym.assignments == 0
|
||||||
&& value instanceof AST_Function
|
&& (value instanceof AST_AsyncFunction || value instanceof AST_Function)
|
||||||
&& (sym.references.length ? all(sym.references, function(ref) {
|
&& (sym.references.length ? all(sym.references, function(ref) {
|
||||||
return value === ref.fixed_value();
|
return value === ref.fixed_value();
|
||||||
}) : value === def.name.fixed_value())
|
}) : value === def.name.fixed_value())
|
||||||
@@ -5835,7 +5864,7 @@ merge(Compressor.prototype, {
|
|||||||
&& can_declare_defun()
|
&& can_declare_defun()
|
||||||
&& can_rename(value, def.name.name)) {
|
&& can_rename(value, def.name.name)) {
|
||||||
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
AST_Node.warn("Declaring {name} as function [{file}:{line},{col}]", template(def.name));
|
||||||
var defun = make_node(AST_Defun, def, value);
|
var defun = make_node(value instanceof AST_Function ? AST_Defun : AST_AsyncDefun, def, value);
|
||||||
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
|
defun.name = make_node(AST_SymbolDefun, def.name, def.name);
|
||||||
var name_def = def.name.scope.resolve().def_function(defun.name);
|
var name_def = def.name.scope.resolve().def_function(defun.name);
|
||||||
if (old_def) old_def.forEach(function(node) {
|
if (old_def) old_def.forEach(function(node) {
|
||||||
@@ -6873,7 +6902,7 @@ merge(Compressor.prototype, {
|
|||||||
return exprs && make_sequence(self, exprs.map(convert_spread));
|
return exprs && make_sequence(self, exprs.map(convert_spread));
|
||||||
}
|
}
|
||||||
var def;
|
var def;
|
||||||
if (exp instanceof AST_Function
|
if ((is_arrow(exp) && !exp.value || exp instanceof AST_AsyncFunction || exp instanceof AST_Function)
|
||||||
&& !(exp.name && (def = exp.name.definition()).references.length > def.replaced)) {
|
&& !(exp.name && (def = exp.name.definition()).references.length > def.replaced)) {
|
||||||
exp.process_expression(false, function(node) {
|
exp.process_expression(false, function(node) {
|
||||||
var value = node.value && node.value.drop_side_effect_free(compressor, true);
|
var value = node.value && node.value.drop_side_effect_free(compressor, true);
|
||||||
@@ -7015,6 +7044,12 @@ merge(Compressor.prototype, {
|
|||||||
def(AST_SymbolRef, function(compressor) {
|
def(AST_SymbolRef, function(compressor) {
|
||||||
return this.is_declared(compressor) && can_drop_symbol(this) ? null : this;
|
return this.is_declared(compressor) && can_drop_symbol(this) ? null : this;
|
||||||
});
|
});
|
||||||
|
def(AST_Template, function(compressor, first_in_statement) {
|
||||||
|
if (this.tag && !is_raw_tag(compressor, this.tag)) return this;
|
||||||
|
var expressions = this.expressions;
|
||||||
|
if (expressions.length == 0) return null;
|
||||||
|
return make_sequence(this, expressions).drop_side_effect_free(compressor, first_in_statement);
|
||||||
|
});
|
||||||
def(AST_This, return_null);
|
def(AST_This, return_null);
|
||||||
def(AST_Unary, function(compressor, first_in_statement) {
|
def(AST_Unary, function(compressor, first_in_statement) {
|
||||||
var exp = this.expression;
|
var exp = this.expression;
|
||||||
@@ -8291,9 +8326,12 @@ merge(Compressor.prototype, {
|
|||||||
return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
|
return parent ? maintain_this_binding(compressor, parent, node, arg) : arg;
|
||||||
}
|
}
|
||||||
})));
|
})));
|
||||||
|
var save_inlined = fn.inlined;
|
||||||
|
if (exp !== fn) fn.inlined = true;
|
||||||
var node = make_sequence(self, args.filter(function(arg) {
|
var node = make_sequence(self, args.filter(function(arg) {
|
||||||
return arg;
|
return arg;
|
||||||
})).optimize(compressor);
|
})).optimize(compressor);
|
||||||
|
fn.inlined = save_inlined;
|
||||||
node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
node = maintain_this_binding(compressor, compressor.parent(), compressor.self(), node);
|
||||||
if (replacing || best_of_expression(node, self) === node) {
|
if (replacing || best_of_expression(node, self) === node) {
|
||||||
refs.forEach(function(ref) {
|
refs.forEach(function(ref) {
|
||||||
@@ -9756,8 +9794,8 @@ merge(Compressor.prototype, {
|
|||||||
single_use = fixed.is_constant_expression(self.scope);
|
single_use = fixed.is_constant_expression(self.scope);
|
||||||
if (single_use == "f") {
|
if (single_use == "f") {
|
||||||
var scope = self.scope;
|
var scope = self.scope;
|
||||||
do if (is_defun(scope) || is_function(scope)) {
|
do {
|
||||||
scope.inlined = true;
|
if (is_defun(scope) || is_function(scope)) scope.inlined = true;
|
||||||
} while (scope = scope.parent_scope);
|
} while (scope = scope.parent_scope);
|
||||||
}
|
}
|
||||||
} else if (fixed.name && fixed.name.name == "await" && is_async(fixed)) {
|
} else if (fixed.name && fixed.name.name == "await" && is_async(fixed)) {
|
||||||
@@ -9899,6 +9937,44 @@ merge(Compressor.prototype, {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
function is_raw_tag(compressor, tag) {
|
||||||
|
return compressor.option("unsafe")
|
||||||
|
&& tag instanceof AST_Dot
|
||||||
|
&& tag.property == "raw"
|
||||||
|
&& is_undeclared_ref(tag.expression)
|
||||||
|
&& tag.expression.name == "String";
|
||||||
|
}
|
||||||
|
|
||||||
|
OPT(AST_Template, function(self, compressor) {
|
||||||
|
if (!compressor.option("templates")) return self;
|
||||||
|
if (!self.tag || is_raw_tag(compressor, self.tag)) {
|
||||||
|
var exprs = self.expressions;
|
||||||
|
var strs = self.strings;
|
||||||
|
for (var i = exprs.length; --i >= 0;) {
|
||||||
|
var node = exprs[i];
|
||||||
|
var ev = node.evaluate(compressor);
|
||||||
|
if (ev === node) continue;
|
||||||
|
ev = ("" + ev).replace(/\r|\\|`/g, function(s) {
|
||||||
|
return "\\" + (s == "\r" ? "r" : s);
|
||||||
|
});
|
||||||
|
if (ev.length > node.print_to_string().length + 3) continue;
|
||||||
|
var combined = strs[i] + ev + strs[i + 1];
|
||||||
|
if (typeof make_node(AST_Template, self, {
|
||||||
|
expressions: [],
|
||||||
|
strings: [ combined ],
|
||||||
|
tag: self.tag,
|
||||||
|
}).evaluate(compressor) != typeof make_node(AST_Template, self, {
|
||||||
|
expressions: [ node ],
|
||||||
|
strings: strs.slice(i, i + 2),
|
||||||
|
tag: self.tag,
|
||||||
|
}).evaluate(compressor)) continue;
|
||||||
|
exprs.splice(i, 1);
|
||||||
|
strs.splice(i, 2, combined);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return try_evaluate(compressor, self);
|
||||||
|
});
|
||||||
|
|
||||||
function is_atomic(lhs, self) {
|
function is_atomic(lhs, self) {
|
||||||
return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
|
return lhs instanceof AST_SymbolRef || lhs.TYPE === self.TYPE;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -780,7 +780,9 @@ function OutputStream(options) {
|
|||||||
// (new foo)(bar)
|
// (new foo)(bar)
|
||||||
if (p instanceof AST_Call) return p.expression === this;
|
if (p instanceof AST_Call) return p.expression === this;
|
||||||
// (new Date).getTime(), (new Date)["getTime"]()
|
// (new Date).getTime(), (new Date)["getTime"]()
|
||||||
return p instanceof AST_PropAccess;
|
if (p instanceof AST_PropAccess) return true;
|
||||||
|
// (new foo)`bar`
|
||||||
|
if (p instanceof AST_Template) return p.tag === this;
|
||||||
});
|
});
|
||||||
|
|
||||||
PARENS(AST_Number, function(output) {
|
PARENS(AST_Number, function(output) {
|
||||||
@@ -802,6 +804,8 @@ function OutputStream(options) {
|
|||||||
if (p instanceof AST_Conditional) return p.condition === self;
|
if (p instanceof AST_Conditional) return p.condition === self;
|
||||||
// (a = foo)["prop"] —or— (a = foo).prop
|
// (a = foo)["prop"] —or— (a = foo).prop
|
||||||
if (p instanceof AST_PropAccess) return p.expression === self;
|
if (p instanceof AST_PropAccess) return p.expression === self;
|
||||||
|
// (a = foo)`bar`
|
||||||
|
if (p instanceof AST_Template) return p.tag === self;
|
||||||
// !(a = false) → true
|
// !(a = false) → true
|
||||||
if (p instanceof AST_Unary) return true;
|
if (p instanceof AST_Unary) return true;
|
||||||
}
|
}
|
||||||
@@ -1486,6 +1490,19 @@ function OutputStream(options) {
|
|||||||
DEFPRINT(AST_This, function(output) {
|
DEFPRINT(AST_This, function(output) {
|
||||||
output.print("this");
|
output.print("this");
|
||||||
});
|
});
|
||||||
|
DEFPRINT(AST_Template, function(output) {
|
||||||
|
var self = this;
|
||||||
|
if (self.tag) self.tag.print(output);
|
||||||
|
output.print("`");
|
||||||
|
for (var i = 0; i < self.expressions.length; i++) {
|
||||||
|
output.print(self.strings[i]);
|
||||||
|
output.print("${");
|
||||||
|
self.expressions[i].print(output);
|
||||||
|
output.print("}");
|
||||||
|
}
|
||||||
|
output.print(self.strings[i]);
|
||||||
|
output.print("`");
|
||||||
|
});
|
||||||
DEFPRINT(AST_Constant, function(output) {
|
DEFPRINT(AST_Constant, function(output) {
|
||||||
output.print(this.value);
|
output.print(this.value);
|
||||||
});
|
});
|
||||||
|
|||||||
145
lib/parse.js
145
lib/parse.js
@@ -113,7 +113,7 @@ var OPERATORS = makePredicate([
|
|||||||
var NEWLINE_CHARS = "\n\r\u2028\u2029";
|
var NEWLINE_CHARS = "\n\r\u2028\u2029";
|
||||||
var OPERATOR_CHARS = "+-*&%=<>!?|~^";
|
var OPERATOR_CHARS = "+-*&%=<>!?|~^";
|
||||||
var PUNC_BEFORE_EXPRESSION = "[{(,;:";
|
var PUNC_BEFORE_EXPRESSION = "[{(,;:";
|
||||||
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + ")}]";
|
var PUNC_CHARS = PUNC_BEFORE_EXPRESSION + "`)}]";
|
||||||
var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF";
|
var WHITESPACE_CHARS = NEWLINE_CHARS + " \u00a0\t\f\u000b\u200b\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200a\u202f\u205f\u3000\uFEFF";
|
||||||
var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS));
|
var NON_IDENTIFIER_CHARS = makePredicate(characters("./'\"" + OPERATOR_CHARS + PUNC_CHARS + WHITESPACE_CHARS));
|
||||||
|
|
||||||
@@ -145,6 +145,43 @@ function is_identifier_string(str) {
|
|||||||
return /^[a-z_$][a-z0-9_$]*$/i.test(str);
|
return /^[a-z_$][a-z0-9_$]*$/i.test(str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function decode_escape_sequence(seq) {
|
||||||
|
switch (seq[0]) {
|
||||||
|
case "b": return "\b";
|
||||||
|
case "f": return "\f";
|
||||||
|
case "n": return "\n";
|
||||||
|
case "r": return "\r";
|
||||||
|
case "t": return "\t";
|
||||||
|
case "u":
|
||||||
|
var code;
|
||||||
|
if (seq.length == 5) {
|
||||||
|
code = seq.slice(1);
|
||||||
|
} else if (seq[1] == "{" && seq.slice(-1) == "}") {
|
||||||
|
code = seq.slice(2, -1);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var num = parseInt(code, 16);
|
||||||
|
if (num < 0 || isNaN(num)) return;
|
||||||
|
if (num < 0x10000) return String.fromCharCode(num);
|
||||||
|
if (num > 0x10ffff) return;
|
||||||
|
return String.fromCharCode((num >> 10) + 0xd7c0) + String.fromCharCode((num & 0x03ff) + 0xdc00);
|
||||||
|
case "v": return "\u000b";
|
||||||
|
case "x":
|
||||||
|
if (seq.length != 3) return;
|
||||||
|
var num = parseInt(seq.slice(1), 16);
|
||||||
|
if (num < 0 || isNaN(num)) return;
|
||||||
|
return String.fromCharCode(num);
|
||||||
|
case "\r":
|
||||||
|
case "\n":
|
||||||
|
return "";
|
||||||
|
default:
|
||||||
|
if (seq == "0") return "\0";
|
||||||
|
if (seq[0] >= "0" && seq[0] <= "9") return;
|
||||||
|
return seq;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function parse_js_number(num) {
|
function parse_js_number(num) {
|
||||||
var match;
|
var match;
|
||||||
if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
|
if (match = RE_BIN_NUMBER.exec(num)) return parseInt(match[1], 2);
|
||||||
@@ -191,7 +228,28 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
regex_allowed : false,
|
regex_allowed : false,
|
||||||
comments_before : [],
|
comments_before : [],
|
||||||
directives : {},
|
directives : {},
|
||||||
directive_stack : []
|
directive_stack : [],
|
||||||
|
read_template : with_eof_error("Unterminated template literal", function(strings) {
|
||||||
|
var s = "";
|
||||||
|
for (;;) {
|
||||||
|
var ch = next(true, true);
|
||||||
|
switch (ch) {
|
||||||
|
case "\\":
|
||||||
|
ch += next(true, true);
|
||||||
|
break;
|
||||||
|
case "`":
|
||||||
|
strings.push(s);
|
||||||
|
return;
|
||||||
|
case "$":
|
||||||
|
if (peek() == "{") {
|
||||||
|
next();
|
||||||
|
strings.push(s);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s += ch;
|
||||||
|
}
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
var prev_was_dot = false;
|
var prev_was_dot = false;
|
||||||
|
|
||||||
@@ -319,36 +377,23 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function read_escaped_char(in_string) {
|
function read_escaped_char(in_string) {
|
||||||
|
var seq = next(true, in_string);
|
||||||
|
if (seq >= "0" && seq <= "7") return read_octal_escape_sequence(seq);
|
||||||
|
if (seq == "u") {
|
||||||
var ch = next(true, in_string);
|
var ch = next(true, in_string);
|
||||||
switch (ch.charCodeAt(0)) {
|
seq += ch;
|
||||||
case 110: return "\n";
|
if (ch != "{") {
|
||||||
case 114: return "\r";
|
seq += next(true, in_string) + next(true, in_string) + next(true, in_string);
|
||||||
case 116: return "\t";
|
} else do {
|
||||||
case 98: return "\b";
|
ch = next(true, in_string);
|
||||||
case 118: return "\u000b"; // \v
|
seq += ch;
|
||||||
case 102: return "\f";
|
} while (ch != "}");
|
||||||
case 120: return String.fromCharCode(hex_bytes(2)); // \x
|
} else if (seq == "x") {
|
||||||
case 117: // \u
|
seq += next(true, in_string) + next(true, in_string);
|
||||||
if (peek() != "{") return String.fromCharCode(hex_bytes(4));
|
|
||||||
next();
|
|
||||||
var num = 0;
|
|
||||||
do {
|
|
||||||
var digit = parseInt(next(true), 16);
|
|
||||||
if (isNaN(digit)) parse_error("Invalid hex-character pattern in string");
|
|
||||||
num = num * 16 + digit;
|
|
||||||
} while (peek() != "}");
|
|
||||||
next();
|
|
||||||
if (num < 0x10000) return String.fromCharCode(num);
|
|
||||||
if (num > 0x10ffff) parse_error("Invalid character code: " + num);
|
|
||||||
return String.fromCharCode((num >> 10) + 0xd7c0) + String.fromCharCode((num & 0x03ff) + 0xdc00);
|
|
||||||
case 13: // \r
|
|
||||||
// DOS newline
|
|
||||||
if (peek() == "\n") next(true, in_string);
|
|
||||||
case 10: return ""; // \n
|
|
||||||
}
|
}
|
||||||
if (ch >= "0" && ch <= "7")
|
var str = decode_escape_sequence(seq);
|
||||||
return read_octal_escape_sequence(ch);
|
if (typeof str != "string") parse_error("Invalid escape sequence: \\" + seq);
|
||||||
return ch;
|
return str;
|
||||||
}
|
}
|
||||||
|
|
||||||
function read_octal_escape_sequence(ch) {
|
function read_octal_escape_sequence(ch) {
|
||||||
@@ -367,17 +412,6 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
|||||||
return String.fromCharCode(parseInt(ch, 8));
|
return String.fromCharCode(parseInt(ch, 8));
|
||||||
}
|
}
|
||||||
|
|
||||||
function hex_bytes(n) {
|
|
||||||
var num = 0;
|
|
||||||
for (; n > 0; --n) {
|
|
||||||
var digit = parseInt(next(true), 16);
|
|
||||||
if (isNaN(digit))
|
|
||||||
parse_error("Invalid hex-character pattern in string");
|
|
||||||
num = (num << 4) | digit;
|
|
||||||
}
|
|
||||||
return num;
|
|
||||||
}
|
|
||||||
|
|
||||||
var read_string = with_eof_error("Unterminated string constant", function(quote_char) {
|
var read_string = with_eof_error("Unterminated string constant", function(quote_char) {
|
||||||
var quote = next(), ret = "";
|
var quote = next(), ret = "";
|
||||||
for (;;) {
|
for (;;) {
|
||||||
@@ -816,6 +850,7 @@ function parse($TEXT, options) {
|
|||||||
});
|
});
|
||||||
case "[":
|
case "[":
|
||||||
case "(":
|
case "(":
|
||||||
|
case "`":
|
||||||
return simple_statement();
|
return simple_statement();
|
||||||
case ";":
|
case ";":
|
||||||
S.in_directives = false;
|
S.in_directives = false;
|
||||||
@@ -1401,6 +1436,11 @@ function parse($TEXT, options) {
|
|||||||
var start = S.token;
|
var start = S.token;
|
||||||
if (is("punc")) {
|
if (is("punc")) {
|
||||||
switch (start.value) {
|
switch (start.value) {
|
||||||
|
case "`":
|
||||||
|
var tmpl = template(null);
|
||||||
|
tmpl.start = start;
|
||||||
|
tmpl.end = prev();
|
||||||
|
return subscripts(tmpl, allow_calls);
|
||||||
case "(":
|
case "(":
|
||||||
next();
|
next();
|
||||||
if (is("punc", ")")) {
|
if (is("punc", ")")) {
|
||||||
@@ -1771,6 +1811,23 @@ function parse($TEXT, options) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function template(tag) {
|
||||||
|
var read = S.input.context().read_template;
|
||||||
|
var strings = [];
|
||||||
|
var expressions = [];
|
||||||
|
while (read(strings)) {
|
||||||
|
next();
|
||||||
|
expressions.push(expression());
|
||||||
|
if (!is("punc", "}")) unexpected();
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
return new AST_Template({
|
||||||
|
expressions: expressions,
|
||||||
|
strings: strings,
|
||||||
|
tag: tag,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var subscripts = function(expr, allow_calls) {
|
var subscripts = function(expr, allow_calls) {
|
||||||
var start = expr.start;
|
var start = expr.start;
|
||||||
if (is("punc", ".")) {
|
if (is("punc", ".")) {
|
||||||
@@ -1804,6 +1861,12 @@ function parse($TEXT, options) {
|
|||||||
mark_pure(call);
|
mark_pure(call);
|
||||||
return subscripts(call, true);
|
return subscripts(call, true);
|
||||||
}
|
}
|
||||||
|
if (is("punc", "`")) {
|
||||||
|
var tmpl = template(expr);
|
||||||
|
tmpl.start = expr.start;
|
||||||
|
tmpl.end = prev();
|
||||||
|
return subscripts(tmpl, allow_calls);
|
||||||
|
}
|
||||||
return expr;
|
return expr;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
11
lib/scope.js
11
lib/scope.js
@@ -101,6 +101,14 @@ SymbolDef.prototype = {
|
|||||||
|
|
||||||
var unary_side_effects = makePredicate("delete ++ --");
|
var unary_side_effects = makePredicate("delete ++ --");
|
||||||
|
|
||||||
|
function is_lhs(node, parent) {
|
||||||
|
if (parent instanceof AST_Assign) return parent.left === node && node;
|
||||||
|
if (parent instanceof AST_DefaultValue) return parent.name === node && node;
|
||||||
|
if (parent instanceof AST_Destructured) return node;
|
||||||
|
if (parent instanceof AST_DestructuredKeyVal) return node;
|
||||||
|
if (parent instanceof AST_Unary) return unary_side_effects[parent.operator] && parent.expression;
|
||||||
|
}
|
||||||
|
|
||||||
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||||
options = defaults(options, {
|
options = defaults(options, {
|
||||||
cache: null,
|
cache: null,
|
||||||
@@ -269,8 +277,7 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
|||||||
sym = self.def_global(node);
|
sym = self.def_global(node);
|
||||||
} else if (name == "arguments" && is_arguments(sym)) {
|
} else if (name == "arguments" && is_arguments(sym)) {
|
||||||
var parent = tw.parent();
|
var parent = tw.parent();
|
||||||
if (parent instanceof AST_Assign && parent.left === node
|
if (is_lhs(node, parent)) {
|
||||||
|| parent instanceof AST_Unary && unary_side_effects[parent.operator]) {
|
|
||||||
sym.scope.uses_arguments = 3;
|
sym.scope.uses_arguments = 3;
|
||||||
} else if (sym.scope.uses_arguments < 2
|
} else if (sym.scope.uses_arguments < 2
|
||||||
&& !(parent instanceof AST_PropAccess && parent.expression === node)) {
|
&& !(parent instanceof AST_PropAccess && parent.expression === node)) {
|
||||||
|
|||||||
@@ -201,6 +201,10 @@ TreeTransformer.prototype = new TreeWalker;
|
|||||||
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
if (self.key instanceof AST_Node) self.key = self.key.transform(tw);
|
||||||
self.value = self.value.transform(tw);
|
self.value = self.value.transform(tw);
|
||||||
});
|
});
|
||||||
|
DEF(AST_Template, function(self, tw) {
|
||||||
|
if (self.tag) self.tag = self.tag.transform(tw);
|
||||||
|
self.expressions = do_list(self.expressions, tw);
|
||||||
|
});
|
||||||
})(function(node, descend) {
|
})(function(node, descend) {
|
||||||
node.DEFMETHOD("transform", function(tw, in_list) {
|
node.DEFMETHOD("transform", function(tw, in_list) {
|
||||||
var x, y;
|
var x, y;
|
||||||
|
|||||||
@@ -255,6 +255,8 @@ function first_in_statement(stack, arrow) {
|
|||||||
if (p.expressions[0] === node) continue;
|
if (p.expressions[0] === node) continue;
|
||||||
} else if (p instanceof AST_Statement) {
|
} else if (p instanceof AST_Statement) {
|
||||||
return p.body === node;
|
return p.body === node;
|
||||||
|
} else if (p instanceof AST_Template) {
|
||||||
|
if (p.tag === node) continue;
|
||||||
} else if (p instanceof AST_UnaryPostfix) {
|
} else if (p instanceof AST_UnaryPostfix) {
|
||||||
if (p.expression === node) continue;
|
if (p.expression === node) continue;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
"description": "JavaScript parser, mangler/compressor and beautifier toolkit",
|
||||||
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
"author": "Mihai Bazon <mihai.bazon@gmail.com> (http://lisperator.net/)",
|
||||||
"license": "BSD-2-Clause",
|
"license": "BSD-2-Clause",
|
||||||
"version": "3.12.6",
|
"version": "3.12.7",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=0.8.0"
|
"node": ">=0.8.0"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -434,6 +434,46 @@ collapse_value: {
|
|||||||
node_version: ">=4"
|
node_version: ">=4"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collapse_property_lambda: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function f() {
|
||||||
|
f.g = () => 42;
|
||||||
|
return f.g();
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function f() {
|
||||||
|
return (f.g = () => 42)();
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_return: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(a => {
|
||||||
|
while (!console);
|
||||||
|
return console.log(a);
|
||||||
|
})(42);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(a => {
|
||||||
|
while (!console);
|
||||||
|
console.log(a);
|
||||||
|
})(42);
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
reduce_iife_1: {
|
reduce_iife_1: {
|
||||||
options = {
|
options = {
|
||||||
evaluate: true,
|
evaluate: true,
|
||||||
|
|||||||
@@ -519,6 +519,160 @@ collapse_vars_3: {
|
|||||||
node_version: ">=8"
|
node_version: ">=8"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
collapse_property_lambda: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(async function f() {
|
||||||
|
f.g = () => 42;
|
||||||
|
return f.g();
|
||||||
|
})().then(console.log);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(async function f() {
|
||||||
|
return (f.g = () => 42)();
|
||||||
|
})().then(console.log);
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
drop_return: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(async function(a) {
|
||||||
|
while (!console);
|
||||||
|
return console.log(a);
|
||||||
|
})(42);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(async function(a) {
|
||||||
|
while (!console);
|
||||||
|
console.log(a);
|
||||||
|
})(42);
|
||||||
|
}
|
||||||
|
expect_stdout: "42"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
functions: {
|
||||||
|
options = {
|
||||||
|
functions: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
!async function() {
|
||||||
|
var a = async function a() {
|
||||||
|
return a && "a";
|
||||||
|
};
|
||||||
|
var b = async function x() {
|
||||||
|
return !!x;
|
||||||
|
};
|
||||||
|
var c = async function(c) {
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
if (await c(await b(await a()))) {
|
||||||
|
var d = async function() {};
|
||||||
|
var e = async function y() {
|
||||||
|
return typeof y;
|
||||||
|
};
|
||||||
|
var f = async function(f) {
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
!async function() {
|
||||||
|
async function a() {
|
||||||
|
return a && "a";
|
||||||
|
}
|
||||||
|
async function b() {
|
||||||
|
return !!b;
|
||||||
|
}
|
||||||
|
var c = async function(c) {
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
if (await c(await b(await a()))) {
|
||||||
|
async function d() {}
|
||||||
|
async function e() {
|
||||||
|
return typeof e;
|
||||||
|
}
|
||||||
|
var f = async function(f) {
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "a true 42 function function function"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
functions_use_strict: {
|
||||||
|
options = {
|
||||||
|
functions: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
"use strict";
|
||||||
|
!async function() {
|
||||||
|
var a = async function a() {
|
||||||
|
return a && "a";
|
||||||
|
};
|
||||||
|
var b = async function x() {
|
||||||
|
return !!x;
|
||||||
|
};
|
||||||
|
var c = async function(c) {
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
if (await c(await b(await a()))) {
|
||||||
|
var d = async function() {};
|
||||||
|
var e = async function y() {
|
||||||
|
return typeof y;
|
||||||
|
};
|
||||||
|
var f = async function(f) {
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
"use strict";
|
||||||
|
!async function() {
|
||||||
|
async function a() {
|
||||||
|
return a && "a";
|
||||||
|
}
|
||||||
|
async function b() {
|
||||||
|
return !!b;
|
||||||
|
}
|
||||||
|
var c = async function(c) {
|
||||||
|
return c;
|
||||||
|
};
|
||||||
|
if (await c(await b(await a()))) {
|
||||||
|
var d = async function() {};
|
||||||
|
var e = async function y() {
|
||||||
|
return typeof y;
|
||||||
|
};
|
||||||
|
var f = async function(f) {
|
||||||
|
return f;
|
||||||
|
};
|
||||||
|
console.log(await a(await d()), await b(await e()), await c(await f(42)), typeof d, await e(), typeof f);
|
||||||
|
}
|
||||||
|
}();
|
||||||
|
}
|
||||||
|
expect_stdout: "a true 42 function function function"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
issue_4335_1: {
|
issue_4335_1: {
|
||||||
options = {
|
options = {
|
||||||
inline: true,
|
inline: true,
|
||||||
|
|||||||
@@ -2514,3 +2514,45 @@ issue_4584: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
node_version: ">=6"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4608_1: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
keep_fargs: false,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function() {
|
||||||
|
[ arguments ] = [ "foo" ];
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function() {
|
||||||
|
[ arguments ] = [ "foo" ];
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "f"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4608_2: {
|
||||||
|
options = {
|
||||||
|
arguments: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
(function(a) {
|
||||||
|
[ arguments ] = [ "foo" ];
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
(function(a) {
|
||||||
|
[ arguments ] = [ "foo" ];
|
||||||
|
console.log(arguments[0]);
|
||||||
|
})();
|
||||||
|
}
|
||||||
|
expect_stdout: "f"
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|||||||
@@ -703,6 +703,7 @@ prototype_function: {
|
|||||||
var g = 0();
|
var g = 0();
|
||||||
var h = 0();
|
var h = 0();
|
||||||
}
|
}
|
||||||
|
expect_stdout: true
|
||||||
}
|
}
|
||||||
|
|
||||||
call_args: {
|
call_args: {
|
||||||
@@ -2800,7 +2801,7 @@ operator_in: {
|
|||||||
console.log("PASS" in { });
|
console.log("PASS" in { });
|
||||||
console.log("FAIL" in { });
|
console.log("FAIL" in { });
|
||||||
console.log("toString" in { });
|
console.log("toString" in { });
|
||||||
console.log(true);
|
console.log("toString" in { toString: 3 });
|
||||||
}
|
}
|
||||||
expect_stdout: [
|
expect_stdout: [
|
||||||
"true",
|
"true",
|
||||||
|
|||||||
@@ -5283,3 +5283,127 @@ issue_4471: {
|
|||||||
"PASS",
|
"PASS",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4612_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
function f() {
|
||||||
|
return g();
|
||||||
|
}
|
||||||
|
function g(a) {
|
||||||
|
return a || f();
|
||||||
|
}
|
||||||
|
return g("PASS");
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4612_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
side_effects: true,
|
||||||
|
unused: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
function fn() {
|
||||||
|
return h();
|
||||||
|
}
|
||||||
|
function g() {
|
||||||
|
return fn();
|
||||||
|
}
|
||||||
|
function h(a) {
|
||||||
|
return a || fn();
|
||||||
|
}
|
||||||
|
return h("PASS");
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4612_3: {
|
||||||
|
options = {
|
||||||
|
inline: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
return g();
|
||||||
|
function f() {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
function g() {
|
||||||
|
{
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(typeof function() {
|
||||||
|
return g();
|
||||||
|
function f() {
|
||||||
|
return g;
|
||||||
|
}
|
||||||
|
function g() {
|
||||||
|
return f;
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "function"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4612_4: {
|
||||||
|
options = {
|
||||||
|
booleans: true,
|
||||||
|
evaluate: true,
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(function() {
|
||||||
|
function f() {
|
||||||
|
return h();
|
||||||
|
}
|
||||||
|
function g() {
|
||||||
|
{
|
||||||
|
return h();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function h() {
|
||||||
|
{
|
||||||
|
return g();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(function() {
|
||||||
|
function f() {
|
||||||
|
return h();
|
||||||
|
}
|
||||||
|
function g() {
|
||||||
|
return h();
|
||||||
|
}
|
||||||
|
function h() {
|
||||||
|
return g();
|
||||||
|
}
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined"
|
||||||
|
}
|
||||||
|
|||||||
@@ -248,6 +248,35 @@ issue_2110_2: {
|
|||||||
expect_stdout: "function"
|
expect_stdout: "function"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_2110_3: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
pure_getters: "strict",
|
||||||
|
reduce_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
function g() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
console.log(typeof function() {
|
||||||
|
function f() {}
|
||||||
|
f.g = g;
|
||||||
|
return f.g();
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
function g() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
console.log(typeof function() {
|
||||||
|
function f() {}
|
||||||
|
f.g = g;
|
||||||
|
return f.g();
|
||||||
|
}());
|
||||||
|
}
|
||||||
|
expect_stdout: "function"
|
||||||
|
}
|
||||||
|
|
||||||
set_immutable_1: {
|
set_immutable_1: {
|
||||||
options = {
|
options = {
|
||||||
collapse_vars: true,
|
collapse_vars: true,
|
||||||
|
|||||||
@@ -918,3 +918,32 @@ issue_4560_3: {
|
|||||||
expect_stdout: "PASS"
|
expect_stdout: "PASS"
|
||||||
node_version: ">=6"
|
node_version: ">=6"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
issue_4614: {
|
||||||
|
options = {
|
||||||
|
pure_getters: "strict",
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
try {
|
||||||
|
(function(...[]) {
|
||||||
|
var arguments;
|
||||||
|
arguments[0];
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
try {
|
||||||
|
(function(...[]) {
|
||||||
|
var arguments;
|
||||||
|
arguments[0];
|
||||||
|
})();
|
||||||
|
} catch (e) {
|
||||||
|
console.log("PASS");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
node_version: ">=6"
|
||||||
|
}
|
||||||
|
|||||||
285
test/compress/templates.js
Normal file
285
test/compress/templates.js
Normal file
@@ -0,0 +1,285 @@
|
|||||||
|
simple: {
|
||||||
|
input: {
|
||||||
|
console.log(`foo
|
||||||
|
bar\nbaz`);
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(`foo\n bar\\nbaz`);"
|
||||||
|
expect_stdout: [
|
||||||
|
"foo",
|
||||||
|
" bar",
|
||||||
|
"baz",
|
||||||
|
]
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
placeholder: {
|
||||||
|
input: {
|
||||||
|
console.log(`foo ${ function(a, b) {
|
||||||
|
return a * b;
|
||||||
|
}(6, 7) }`);
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(`foo ${function(a,b){return a*b}(6,7)}`);"
|
||||||
|
expect_stdout: "foo 42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
nested: {
|
||||||
|
input: {
|
||||||
|
console.log(`P${`A${"S"}`}S`);
|
||||||
|
}
|
||||||
|
expect_exact: 'console.log(`P${`A${"S"}`}S`);'
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
tagged: {
|
||||||
|
input: {
|
||||||
|
console.log(String.raw`foo\nbar`);
|
||||||
|
}
|
||||||
|
expect_exact: "console.log(String.raw`foo\\nbar`);"
|
||||||
|
expect_stdout: "foo\\nbar"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
tagged_chain: {
|
||||||
|
input: {
|
||||||
|
function f(strings) {
|
||||||
|
return strings.join("") || f;
|
||||||
|
}
|
||||||
|
console.log(f```${42}``pass`.toUpperCase());
|
||||||
|
}
|
||||||
|
expect_exact: 'function f(strings){return strings.join("")||f}console.log(f```${42}``pass`.toUpperCase());'
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_parenthesis_arrow: {
|
||||||
|
input: {
|
||||||
|
console.log((s => s.raw[0])`\tPASS`.slice(2));
|
||||||
|
}
|
||||||
|
expect_exact: "console.log((s=>s.raw[0])`\\tPASS`.slice(2));"
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
tag_parenthesis_new: {
|
||||||
|
input: {
|
||||||
|
(new function() {
|
||||||
|
return console.log;
|
||||||
|
})`foo`;
|
||||||
|
}
|
||||||
|
expect_exact: "(new function(){return console.log})`foo`;"
|
||||||
|
expect_stdout: true
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
malformed_escape: {
|
||||||
|
input: {
|
||||||
|
(function(s) {
|
||||||
|
s.forEach((c, i) => console.log(i, c, s.raw[i]));
|
||||||
|
return () => console.log(arguments);
|
||||||
|
})`\uFo${42}`();
|
||||||
|
}
|
||||||
|
expect_exact: "(function(s){s.forEach((c,i)=>console.log(i,c,s.raw[i]));return()=>console.log(arguments)})`\\uFo${42}`();"
|
||||||
|
expect_stdout: true
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: false,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(`foo ${ function(a, b) {
|
||||||
|
return a * b;
|
||||||
|
}(6, 7) }`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(`foo ${42}`);
|
||||||
|
}
|
||||||
|
expect_stdout: "foo 42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
evaluate_templates: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(`foo ${ function(a, b) {
|
||||||
|
return a * b;
|
||||||
|
}(6, 7) }`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("foo 42");
|
||||||
|
}
|
||||||
|
expect_stdout: "foo 42"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
partial_evaluate: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(`${6 * 7} foo ${console ? `PA` + "SS" : `FA` + `IL`}`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(`42 foo ${console ? "PASS" : "FAIL"}`);
|
||||||
|
}
|
||||||
|
expect_stdout: "42 foo PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
malformed_evaluate_1: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(`\67 ${6 * 7}`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(`\67 42`);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
malformed_evaluate_2: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(`\u0${0}b${5}`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(`\u0${0}b5`);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
malformed_evaluate_3: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(`\u${0}b${5}`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(`\u0b5`);
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
malformed_evaluate_4: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(String.raw`\u0${0}b${5}`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("\\u00b5");
|
||||||
|
}
|
||||||
|
expect_stdout: "\\u00b5"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe_evaluate: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(String.raw`\uFo`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("\\uFo");
|
||||||
|
}
|
||||||
|
expect_stdout: "\\uFo"
|
||||||
|
node_version: ">=8"
|
||||||
|
}
|
||||||
|
|
||||||
|
side_effects: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
`42`;
|
||||||
|
`${console.log("foo")}`;
|
||||||
|
console.log`\nbar`;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("foo");
|
||||||
|
console.log`\nbar`;
|
||||||
|
}
|
||||||
|
expect_stdout: true
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
unsafe_side_effects: {
|
||||||
|
options = {
|
||||||
|
side_effects: true,
|
||||||
|
unsafe: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
`42`;
|
||||||
|
`${console.log("foo")}`;
|
||||||
|
String.raw`\nbar`;
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log("foo");
|
||||||
|
}
|
||||||
|
expect_stdout: "foo"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4604: {
|
||||||
|
options = {
|
||||||
|
collapse_vars: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
var a = 0, log = console.log;
|
||||||
|
a = "FAIL";
|
||||||
|
(function() {
|
||||||
|
a = "PASS";
|
||||||
|
})``;
|
||||||
|
log(a);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
var a = 0, log = console.log;
|
||||||
|
a = "FAIL";
|
||||||
|
(function() {
|
||||||
|
a = "PASS";
|
||||||
|
})``;
|
||||||
|
log(a);
|
||||||
|
}
|
||||||
|
expect_stdout: "PASS"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
|
|
||||||
|
issue_4606: {
|
||||||
|
options = {
|
||||||
|
evaluate: true,
|
||||||
|
templates: true,
|
||||||
|
}
|
||||||
|
input: {
|
||||||
|
console.log(`${typeof A} ${"\r"} ${"\\"} ${"`"}`);
|
||||||
|
}
|
||||||
|
expect: {
|
||||||
|
console.log(`${typeof A} \r \\ \``);
|
||||||
|
}
|
||||||
|
expect_stdout: "undefined \r \\ `"
|
||||||
|
node_version: ">=4"
|
||||||
|
}
|
||||||
@@ -115,8 +115,8 @@ describe("String literals", function() {
|
|||||||
UglifyJS.parse(test);
|
UglifyJS.parse(test);
|
||||||
}, function(e) {
|
}, function(e) {
|
||||||
return e instanceof UglifyJS.JS_Parse_Error
|
return e instanceof UglifyJS.JS_Parse_Error
|
||||||
&& e.message === "Invalid hex-character pattern in string";
|
&& /^Invalid escape sequence: \\u/.test(e.message);
|
||||||
});
|
}, test);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
it("Should reject invalid code points in Unicode escape sequence", function() {
|
it("Should reject invalid code points in Unicode escape sequence", function() {
|
||||||
@@ -130,8 +130,8 @@ describe("String literals", function() {
|
|||||||
UglifyJS.parse(test);
|
UglifyJS.parse(test);
|
||||||
}, function(e) {
|
}, function(e) {
|
||||||
return e instanceof UglifyJS.JS_Parse_Error
|
return e instanceof UglifyJS.JS_Parse_Error
|
||||||
&& /^Invalid character code: /.test(e.message);
|
&& /^Invalid escape sequence: \\u{1/.test(e.message);
|
||||||
});
|
}, test);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
67
test/mocha/templates.js
Normal file
67
test/mocha/templates.js
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
var assert = require("assert");
|
||||||
|
var run_code = require("../sandbox").run_code;
|
||||||
|
var semver = require("semver");
|
||||||
|
var UglifyJS = require("../node");
|
||||||
|
|
||||||
|
describe("Template literals", function() {
|
||||||
|
it("Should reject invalid literal", function() {
|
||||||
|
[
|
||||||
|
"`foo\\`",
|
||||||
|
"`foo${bar`",
|
||||||
|
"`foo${bar}",
|
||||||
|
].forEach(function(input) {
|
||||||
|
assert.throws(function() {
|
||||||
|
UglifyJS.parse(input);
|
||||||
|
}, function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error
|
||||||
|
&& e.message === "Unterminated template literal";
|
||||||
|
}, input);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should reject invalid expression", function() {
|
||||||
|
[
|
||||||
|
"`foo${bar;}`",
|
||||||
|
"`foo${42bar}`",
|
||||||
|
].forEach(function(input) {
|
||||||
|
assert.throws(function() {
|
||||||
|
UglifyJS.parse(input);
|
||||||
|
}, function(e) {
|
||||||
|
return e instanceof UglifyJS.JS_Parse_Error;
|
||||||
|
}, input);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("Should process line-break characters correctly", function() {
|
||||||
|
[
|
||||||
|
// native line breaks
|
||||||
|
[ "`foo\nbar`", "`foo\nbar`" ],
|
||||||
|
[ "`foo\rbar`", "`foo\rbar`" ],
|
||||||
|
[ "`foo\r\nbar`", "`foo\nbar`" ],
|
||||||
|
[ "`foo\r\n\rbar`", "`foo\n\rbar`" ],
|
||||||
|
// escaped line breaks
|
||||||
|
[ "`foo\\nbar`", "`foo\\nbar`" ],
|
||||||
|
[ "`foo\\rbar`", "`foo\\rbar`" ],
|
||||||
|
[ "`foo\r\\nbar`", "`foo\r\\nbar`" ],
|
||||||
|
[ "`foo\\r\nbar`", "`foo\\r\nbar`" ],
|
||||||
|
[ "`foo\\r\\nbar`", "`foo\\r\\nbar`" ],
|
||||||
|
// continuation
|
||||||
|
[ "`foo\\\nbar`", "`foo\\\nbar`" ],
|
||||||
|
[ "`foo\\\rbar`", "`foo\\\rbar`" ],
|
||||||
|
[ "`foo\\\r\nbar`", "`foo\\\nbar`" ],
|
||||||
|
[ "`foo\\\r\n\rbar`", "`foo\\\n\rbar`" ],
|
||||||
|
[ "`foo\\\\nbar`", "`foo\\\\nbar`" ],
|
||||||
|
[ "`foo\\\\rbar`", "`foo\\\\rbar`" ],
|
||||||
|
[ "`foo\\\\r\nbar`", "`foo\\\\r\nbar`" ],
|
||||||
|
].forEach(function(test) {
|
||||||
|
var input = "console.log(" + test[0] + ");";
|
||||||
|
var result = UglifyJS.minify(input, {
|
||||||
|
compress: false,
|
||||||
|
mangle: false,
|
||||||
|
});
|
||||||
|
if (result.error) throw result.error;
|
||||||
|
var expected = "console.log(" + test[1] + ");";
|
||||||
|
assert.strictEqual(result.code, expected, test[0]);
|
||||||
|
if (semver.satisfies(process.version, "<4")) return;
|
||||||
|
assert.strictEqual(run_code(result.code), run_code(input), test[0]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -146,6 +146,7 @@ var SUPPORT = function(matrix) {
|
|||||||
rest_object: "var {...a} = {};",
|
rest_object: "var {...a} = {};",
|
||||||
spread: "[...[]];",
|
spread: "[...[]];",
|
||||||
spread_object: "({...0});",
|
spread_object: "({...0});",
|
||||||
|
template: "``",
|
||||||
trailing_comma: "function f(a,) {}",
|
trailing_comma: "function f(a,) {}",
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -409,8 +410,9 @@ function createParams(was_async, noDuplicate) {
|
|||||||
return addTrailingComma(params.join(", "));
|
return addTrailingComma(params.join(", "));
|
||||||
}
|
}
|
||||||
|
|
||||||
function createArgs(recurmax, stmtDepth, canThrow) {
|
function createArgs(recurmax, stmtDepth, canThrow, noTemplate) {
|
||||||
recurmax--;
|
recurmax--;
|
||||||
|
if (SUPPORT.template && !noTemplate && rng(20) == 0) return createTemplateLiteral(recurmax, stmtDepth, canThrow);
|
||||||
var args = [];
|
var args = [];
|
||||||
for (var n = rng(4); --n >= 0;) switch (SUPPORT.spread ? rng(50) : 3) {
|
for (var n = rng(4); --n >= 0;) switch (SUPPORT.spread ? rng(50) : 3) {
|
||||||
case 0:
|
case 0:
|
||||||
@@ -429,7 +431,7 @@ function createArgs(recurmax, stmtDepth, canThrow) {
|
|||||||
args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
|
args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return addTrailingComma(args.join(", "));
|
return "(" + addTrailingComma(args.join(", ")) + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async) {
|
function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async) {
|
||||||
@@ -730,7 +732,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||||
params = pairs.names.join(", ");
|
params = pairs.names.join(", ");
|
||||||
if (!pairs.has_rest) params = addTrailingComma(params);
|
if (!pairs.has_rest) params = addTrailingComma(params);
|
||||||
args = addTrailingComma(pairs.values.join(", "));
|
args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
|
||||||
} else {
|
} else {
|
||||||
params = createParams(save_async);
|
params = createParams(save_async);
|
||||||
}
|
}
|
||||||
@@ -752,10 +754,10 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
|||||||
if (!allowDefun) {
|
if (!allowDefun) {
|
||||||
// avoid "function statements" (decl inside statements)
|
// avoid "function statements" (decl inside statements)
|
||||||
s = "var " + createVarName(MANDATORY) + " = " + s;
|
s = "var " + createVarName(MANDATORY) + " = " + s;
|
||||||
s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
|
s += args || createArgs(recurmax, stmtDepth, canThrow);
|
||||||
} else if (!(name in called) || args || rng(3)) {
|
} else if (!(name in called) || args || rng(3)) {
|
||||||
s += "var " + createVarName(MANDATORY) + " = " + name;
|
s += "var " + createVarName(MANDATORY) + " = " + name;
|
||||||
s += "(" + (args || createArgs(recurmax, stmtDepth, canThrow)) + ")";
|
s += args || createArgs(recurmax, stmtDepth, canThrow);
|
||||||
}
|
}
|
||||||
|
|
||||||
return s + ";";
|
return s + ";";
|
||||||
@@ -1038,6 +1040,11 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
case p++:
|
case p++:
|
||||||
return rng(2) + " === 1 ? a : b";
|
return rng(2) + " === 1 ? a : b";
|
||||||
case p++:
|
case p++:
|
||||||
|
if (SUPPORT.template && rng(20) == 0) {
|
||||||
|
var tmpl = createTemplateLiteral(recurmax, stmtDepth, canThrow);
|
||||||
|
if (rng(10) == 0) tmpl = "String.raw" + tmpl;
|
||||||
|
return tmpl;
|
||||||
|
}
|
||||||
case p++:
|
case p++:
|
||||||
return createValue();
|
return createValue();
|
||||||
case p++:
|
case p++:
|
||||||
@@ -1091,7 +1098,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||||
params = pairs.names.join(", ");
|
params = pairs.names.join(", ");
|
||||||
if (!pairs.has_rest) params = addTrailingComma(params);
|
if (!pairs.has_rest) params = addTrailingComma(params);
|
||||||
args = addTrailingComma(pairs.values.join(", "));
|
args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
|
||||||
} else {
|
} else {
|
||||||
params = createParams(save_async, NO_DUPLICATE);
|
params = createParams(save_async, NO_DUPLICATE);
|
||||||
}
|
}
|
||||||
@@ -1123,7 +1130,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
async = save_async;
|
async = save_async;
|
||||||
VAR_NAMES.length = nameLenBefore;
|
VAR_NAMES.length = nameLenBefore;
|
||||||
if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
|
if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
|
||||||
if (args) suffix += "(" + args + ")";
|
if (args) suffix += args;
|
||||||
s.push(suffix);
|
s.push(suffix);
|
||||||
} else {
|
} else {
|
||||||
s.push(
|
s.push(
|
||||||
@@ -1160,8 +1167,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
async = false;
|
async = false;
|
||||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
|
||||||
var instantiate = rng(4) ? "new " : "";
|
var instantiate = rng(4) ? "new " : "";
|
||||||
|
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||||
s.push(
|
s.push(
|
||||||
instantiate + "function " + name + "(" + createParams(save_async) + "){",
|
instantiate + "function " + name + "(" + createParams(save_async) + "){",
|
||||||
strictMode(),
|
strictMode(),
|
||||||
@@ -1175,7 +1182,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
});
|
});
|
||||||
async = save_async;
|
async = save_async;
|
||||||
VAR_NAMES.length = nameLenBefore;
|
VAR_NAMES.length = nameLenBefore;
|
||||||
s.push(rng(2) ? "}" : "}(" + createArgs(recurmax, stmtDepth, canThrow) + ")");
|
s.push(rng(2) ? "}" : "}" + createArgs(recurmax, stmtDepth, canThrow, instantiate));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
async = save_async;
|
async = save_async;
|
||||||
@@ -1253,7 +1260,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
case p++:
|
case p++:
|
||||||
var name = getVarName();
|
var name = getVarName();
|
||||||
var s = name + "." + getDotKey();
|
var s = name + "." + getDotKey();
|
||||||
s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
s = "typeof " + s + ' == "function" && --_calls_ >= 0 && ' + s + createArgs(recurmax, stmtDepth, canThrow);
|
||||||
return canThrow && rng(8) == 0 ? s : name + " && " + s;
|
return canThrow && rng(8) == 0 ? s : name + " && " + s;
|
||||||
case p++:
|
case p++:
|
||||||
case p++:
|
case p++:
|
||||||
@@ -1264,7 +1271,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
|||||||
name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
|
name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
|
||||||
} while (name in called && !called[name]);
|
} while (name in called && !called[name]);
|
||||||
called[name] = true;
|
called[name] = true;
|
||||||
return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + "(" + createArgs(recurmax, stmtDepth, canThrow) + ")";
|
return "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + createArgs(recurmax, stmtDepth, canThrow);
|
||||||
}
|
}
|
||||||
_createExpression.N = p;
|
_createExpression.N = p;
|
||||||
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
return _createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||||
@@ -1298,6 +1305,28 @@ function createArrayLiteral(recurmax, stmtDepth, canThrow) {
|
|||||||
return "[" + arr.join(", ") + "]";
|
return "[" + arr.join(", ") + "]";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function createTemplateLiteral(recurmax, stmtDepth, canThrow) {
|
||||||
|
recurmax--;
|
||||||
|
var s = [];
|
||||||
|
addText();
|
||||||
|
for (var i = rng(6); --i >= 0;) {
|
||||||
|
s.push("${", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "}");
|
||||||
|
addText();
|
||||||
|
}
|
||||||
|
return "`" + s.join(rng(5) ? "" : "\n") + "`";
|
||||||
|
|
||||||
|
function addText() {
|
||||||
|
while (rng(5) == 0) s.push([
|
||||||
|
" ",
|
||||||
|
"$",
|
||||||
|
"}",
|
||||||
|
"\\`",
|
||||||
|
"\\\\",
|
||||||
|
"tmpl",
|
||||||
|
][rng(6)]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var SAFE_KEYS = [
|
var SAFE_KEYS = [
|
||||||
"length",
|
"length",
|
||||||
"foo",
|
"foo",
|
||||||
|
|||||||
Reference in New Issue
Block a user