support arrow function (#4385)
This commit is contained in:
@@ -1209,3 +1209,11 @@ To allow for better optimizations, the compiler makes various assumptions:
|
||||
`function({}, arguments) {}` will result in `SyntaxError` in earlier versions
|
||||
of Chrome and Node.js - UglifyJS may modify the input which in turn may
|
||||
suppress those errors.
|
||||
- Later versions of JavaScript will throw `SyntaxError` with the following:
|
||||
```js
|
||||
a => {
|
||||
let a;
|
||||
};
|
||||
// SyntaxError: Identifier 'a' has already been declared
|
||||
```
|
||||
UglifyJS may modify the input which in turn may suppress those errors.
|
||||
|
||||
55
lib/ast.js
55
lib/ast.js
@@ -502,10 +502,9 @@ var AST_Toplevel = DEFNODE("Toplevel", "globals", {
|
||||
}
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_Lambda = DEFNODE("Lambda", "name argnames length_read uses_arguments", {
|
||||
var AST_Lambda = DEFNODE("Lambda", "argnames length_read uses_arguments", {
|
||||
$documentation: "Base class for functions",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolDeclaration?] the name of this function",
|
||||
argnames: "[(AST_Destructured|AST_SymbolFunarg)*] array of function arguments and/or destructured literals",
|
||||
uses_arguments: "[boolean/S] tells whether this function accesses the arguments array",
|
||||
},
|
||||
@@ -541,18 +540,49 @@ var AST_Lambda = DEFNODE("Lambda", "name argnames length_read uses_arguments", {
|
||||
}, AST_Scope);
|
||||
|
||||
var AST_Accessor = DEFNODE("Accessor", null, {
|
||||
$documentation: "A setter/getter function. The `name` property is always null.",
|
||||
$documentation: "A getter/setter function",
|
||||
_validate: function() {
|
||||
if (this.name != null) throw new Error("name must be null");
|
||||
},
|
||||
}, AST_Lambda);
|
||||
|
||||
function is_function(node) {
|
||||
return node instanceof AST_AsyncFunction || node instanceof AST_Function;
|
||||
return node instanceof AST_Arrow || node instanceof AST_AsyncFunction || node instanceof AST_Function;
|
||||
}
|
||||
|
||||
var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined", {
|
||||
var AST_Arrow = DEFNODE("Arrow", "inlined value", {
|
||||
$documentation: "An arrow function expression",
|
||||
$propdoc: {
|
||||
value: "[AST_Node?] simple return expression, or null if using function body.",
|
||||
},
|
||||
walk: function(visitor) {
|
||||
var node = this;
|
||||
visitor.visit(node, function() {
|
||||
node.argnames.forEach(function(argname) {
|
||||
argname.walk(visitor);
|
||||
});
|
||||
if (node.value) {
|
||||
node.value.walk(visitor);
|
||||
} else {
|
||||
walk_body(node, visitor);
|
||||
}
|
||||
});
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) throw new Error("name must be null");
|
||||
if (this.uses_arguments) throw new Error("uses_arguments must be false");
|
||||
if (this.value != null) {
|
||||
must_be_expression(this, "value");
|
||||
if (this.body.length) throw new Error("body must be empty if value exists");
|
||||
}
|
||||
},
|
||||
}, AST_Lambda);
|
||||
|
||||
var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined name", {
|
||||
$documentation: "An asynchronous function expression",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolLambda?] the name of this function",
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) {
|
||||
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
|
||||
@@ -560,8 +590,11 @@ var AST_AsyncFunction = DEFNODE("AsyncFunction", "inlined", {
|
||||
},
|
||||
}, AST_Lambda);
|
||||
|
||||
var AST_Function = DEFNODE("Function", "inlined", {
|
||||
var AST_Function = DEFNODE("Function", "inlined name", {
|
||||
$documentation: "A function expression",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolLambda?] the name of this function",
|
||||
},
|
||||
_validate: function() {
|
||||
if (this.name != null) {
|
||||
if (!(this.name instanceof AST_SymbolLambda)) throw new Error("name must be AST_SymbolLambda");
|
||||
@@ -573,15 +606,21 @@ function is_defun(node) {
|
||||
return node instanceof AST_AsyncDefun || node instanceof AST_Defun;
|
||||
}
|
||||
|
||||
var AST_AsyncDefun = DEFNODE("AsyncDefun", "inlined", {
|
||||
var AST_AsyncDefun = DEFNODE("AsyncDefun", "inlined name", {
|
||||
$documentation: "An asynchronous function definition",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolDefun] the name of this function",
|
||||
},
|
||||
_validate: function() {
|
||||
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
|
||||
},
|
||||
}, AST_Lambda);
|
||||
|
||||
var AST_Defun = DEFNODE("Defun", "inlined", {
|
||||
var AST_Defun = DEFNODE("Defun", "inlined name", {
|
||||
$documentation: "A function definition",
|
||||
$propdoc: {
|
||||
name: "[AST_SymbolDefun] the name of this function",
|
||||
},
|
||||
_validate: function() {
|
||||
if (!(this.name instanceof AST_SymbolDefun)) throw new Error("name must be AST_SymbolDefun");
|
||||
},
|
||||
|
||||
@@ -49,6 +49,7 @@ function Compressor(options, false_by_default) {
|
||||
TreeTransformer.call(this, this.before, this.after);
|
||||
this.options = defaults(options, {
|
||||
arguments : !false_by_default,
|
||||
arrows : !false_by_default,
|
||||
assignments : !false_by_default,
|
||||
booleans : !false_by_default,
|
||||
collapse_vars : !false_by_default,
|
||||
@@ -1372,7 +1373,8 @@ merge(Compressor.prototype, {
|
||||
|
||||
function is_iife_call(node) {
|
||||
if (node.TYPE != "Call") return false;
|
||||
return is_function(node.expression) || is_iife_call(node.expression);
|
||||
var exp = node.expression;
|
||||
return exp instanceof AST_AsyncFunction || exp instanceof AST_Function || is_iife_call(exp);
|
||||
}
|
||||
|
||||
function is_undeclared_ref(node) {
|
||||
@@ -3573,12 +3575,20 @@ merge(Compressor.prototype, {
|
||||
return map;
|
||||
}
|
||||
|
||||
AST_Lambda.DEFMETHOD("first_statement", function() {
|
||||
var body = this.body;
|
||||
function skip_directives(body) {
|
||||
for (var i = 0; i < body.length; i++) {
|
||||
var stat = body[i];
|
||||
if (!(stat instanceof AST_Directive)) return stat;
|
||||
}
|
||||
}
|
||||
AST_Arrow.DEFMETHOD("first_statement", function() {
|
||||
if (this.value) return make_node(AST_Return, this.value, {
|
||||
value: this.value
|
||||
});
|
||||
return skip_directives(this.body);
|
||||
});
|
||||
AST_Lambda.DEFMETHOD("first_statement", function() {
|
||||
return skip_directives(this.body);
|
||||
});
|
||||
|
||||
function try_evaluate(compressor, node) {
|
||||
@@ -4170,6 +4180,9 @@ merge(Compressor.prototype, {
|
||||
def(AST_Statement, function() {
|
||||
throw new Error("Cannot negate a statement");
|
||||
});
|
||||
def(AST_Arrow, function() {
|
||||
return basic_negation(this);
|
||||
});
|
||||
def(AST_AsyncFunction, function() {
|
||||
return basic_negation(this);
|
||||
});
|
||||
@@ -4568,6 +4581,10 @@ merge(Compressor.prototype, {
|
||||
result = false;
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_This) {
|
||||
if (scopes.length == 0 && self instanceof AST_Arrow) result = false;
|
||||
return true;
|
||||
}
|
||||
}));
|
||||
return result;
|
||||
});
|
||||
@@ -4651,6 +4668,28 @@ merge(Compressor.prototype, {
|
||||
return trim_block(self);
|
||||
});
|
||||
|
||||
OPT(AST_Arrow, function(self, compressor) {
|
||||
if (!compressor.option("arrows")) return self;
|
||||
if (self.value) {
|
||||
var value = self.value;
|
||||
if (is_undefined(value, compressor)) {
|
||||
self.value = null;
|
||||
} else if (value instanceof AST_UnaryPrefix && value.operator == "void") {
|
||||
self.body.push(make_node(AST_SimpleStatement, value, {
|
||||
body: value.expression
|
||||
}));
|
||||
self.value = null;
|
||||
}
|
||||
} else if (self.body.length == 1) {
|
||||
var stat = self.body[0];
|
||||
if (stat instanceof AST_Return && stat.value) {
|
||||
self.body.pop();
|
||||
self.value = stat.value;
|
||||
}
|
||||
}
|
||||
return self;
|
||||
});
|
||||
|
||||
OPT(AST_Function, function(self, compressor) {
|
||||
self.body = tighten_body(self.body, compressor);
|
||||
if (compressor.option("inline")) for (var i = 0; i < self.body.length; i++) {
|
||||
@@ -6316,6 +6355,7 @@ merge(Compressor.prototype, {
|
||||
})) return this;
|
||||
return make_sequence(this, values.map(convert_spread));
|
||||
});
|
||||
def(AST_Arrow, return_null);
|
||||
def(AST_Assign, function(compressor) {
|
||||
var left = this.left;
|
||||
if (left instanceof AST_PropAccess) {
|
||||
@@ -7702,7 +7742,7 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
}
|
||||
var fn = exp instanceof AST_SymbolRef ? exp.fixed_value() : exp;
|
||||
var is_func = fn instanceof AST_Defun || fn instanceof AST_Function;
|
||||
var is_func = fn instanceof AST_Arrow || fn instanceof AST_Defun || fn instanceof AST_Function;
|
||||
var stat = is_func && fn.first_statement();
|
||||
var can_inline = is_func
|
||||
&& compressor.option("inline")
|
||||
@@ -7772,10 +7812,11 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
if (compressor.option("side_effects")
|
||||
&& all(fn.body, is_empty)
|
||||
&& (fn !== exp || fn_name_unused(fn, compressor))
|
||||
&& !(fn instanceof AST_Arrow && fn.value)
|
||||
&& all(fn.argnames, function(argname) {
|
||||
return !(argname instanceof AST_Destructured);
|
||||
})
|
||||
&& (fn !== exp || fn_name_unused(fn, compressor))) {
|
||||
})) {
|
||||
var args = self.args.map(function(arg) {
|
||||
return arg instanceof AST_Spread ? make_node(AST_Array, arg, {
|
||||
elements: [ arg ],
|
||||
@@ -7814,9 +7855,11 @@ merge(Compressor.prototype, {
|
||||
|
||||
function can_flatten_body(stat) {
|
||||
var len = fn.body.length;
|
||||
if (compressor.option("inline") < 3) {
|
||||
return len == 1 && return_value(stat);
|
||||
if (len < 2) {
|
||||
stat = return_value(stat);
|
||||
if (stat) return stat;
|
||||
}
|
||||
if (compressor.option("inline") < 3) return false;
|
||||
stat = null;
|
||||
for (var i = 0; i < len; i++) {
|
||||
var line = fn.body[i];
|
||||
@@ -9833,7 +9876,7 @@ merge(Compressor.prototype, {
|
||||
&& expr instanceof AST_SymbolRef
|
||||
&& is_arguments(def = expr.definition())
|
||||
&& prop instanceof AST_Number
|
||||
&& (fn = expr.scope.resolve()) === find_lambda()
|
||||
&& (fn = def.scope) === find_lambda()
|
||||
&& !(assigned && fn.uses_arguments === "d")) {
|
||||
var index = prop.value;
|
||||
if (parent instanceof AST_UnaryPrefix && parent.operator == "delete") {
|
||||
@@ -9936,6 +9979,7 @@ merge(Compressor.prototype, {
|
||||
while (p = compressor.parent(i++)) {
|
||||
if (p instanceof AST_Lambda) {
|
||||
if (p instanceof AST_Accessor) return;
|
||||
if (p instanceof AST_Arrow) continue;
|
||||
fn_parent = compressor.parent(i);
|
||||
return p;
|
||||
}
|
||||
@@ -9943,13 +9987,14 @@ merge(Compressor.prototype, {
|
||||
}
|
||||
});
|
||||
|
||||
AST_Arrow.DEFMETHOD("contains_this", return_false);
|
||||
AST_Scope.DEFMETHOD("contains_this", function() {
|
||||
var result;
|
||||
var self = this;
|
||||
self.walk(new TreeWalker(function(node) {
|
||||
if (result) return true;
|
||||
if (node instanceof AST_This) return result = true;
|
||||
if (node !== self && node instanceof AST_Scope) return true;
|
||||
if (node !== self && node instanceof AST_Scope && !(node instanceof AST_Arrow)) return true;
|
||||
}));
|
||||
return result;
|
||||
});
|
||||
|
||||
@@ -678,7 +678,7 @@ function OutputStream(options) {
|
||||
// same goes for an object literal, because otherwise it would be
|
||||
// interpreted as a block of code.
|
||||
function needs_parens_obj(output) {
|
||||
return !output.has_parens() && first_in_statement(output);
|
||||
return !output.has_parens() && first_in_statement(output, true);
|
||||
}
|
||||
PARENS(AST_Object, needs_parens_obj);
|
||||
|
||||
@@ -691,6 +691,8 @@ function OutputStream(options) {
|
||||
var p = output.parent();
|
||||
// [ 1, (2, 3), 4 ] ==> [ 1, 3, 4 ]
|
||||
return p instanceof AST_Array
|
||||
// () => (foo, bar)
|
||||
|| p instanceof AST_Arrow && p.value === this
|
||||
// await (foo, bar)
|
||||
|| p instanceof AST_Await
|
||||
// 1 + (2, 3) + 4 ==> 8
|
||||
@@ -798,6 +800,9 @@ function OutputStream(options) {
|
||||
// !(a = false) → true
|
||||
if (p instanceof AST_Unary) return true;
|
||||
}
|
||||
PARENS(AST_Arrow, function(output) {
|
||||
return needs_parens_assign_cond(this, output);
|
||||
});
|
||||
PARENS(AST_Assign, function(output) {
|
||||
if (needs_parens_assign_cond(this, output)) return true;
|
||||
// v8 parser bug => workaround
|
||||
@@ -985,6 +990,25 @@ function OutputStream(options) {
|
||||
});
|
||||
|
||||
/* -----[ functions ]----- */
|
||||
DEFPRINT(AST_Arrow, function(output) {
|
||||
var self = this;
|
||||
if (self.argnames.length == 1 && self.argnames[0] instanceof AST_SymbolFunarg) {
|
||||
self.argnames[0].print(output);
|
||||
} else output.with_parens(function() {
|
||||
self.argnames.forEach(function(arg, i) {
|
||||
if (i) output.comma();
|
||||
arg.print(output);
|
||||
});
|
||||
});
|
||||
output.space();
|
||||
output.print("=>");
|
||||
output.space();
|
||||
if (self.value) {
|
||||
self.value.print(output);
|
||||
} else {
|
||||
print_braced(self, output, true);
|
||||
}
|
||||
});
|
||||
function print_lambda(self, output) {
|
||||
if (self.name) {
|
||||
output.space();
|
||||
|
||||
156
lib/parse.js
156
lib/parse.js
@@ -569,6 +569,7 @@ function tokenizer($TEXT, filename, html5_comments, shebang) {
|
||||
}
|
||||
if (is_digit(code)) return read_num();
|
||||
if (PUNC_CHARS[ch]) return token("punc", next());
|
||||
if (looking_at("=>")) return token("punc", next() + next());
|
||||
if (OPERATOR_CHARS[ch]) return read_operator();
|
||||
if (code == 92 || !NON_IDENTIFIER_CHARS[ch]) return read_word();
|
||||
break;
|
||||
@@ -634,7 +635,7 @@ var PRECEDENCE = function(a, ret) {
|
||||
["*", "/", "%"]
|
||||
], {});
|
||||
|
||||
var ATOMIC_START_TOKEN = makePredicate("atom num string regexp name");
|
||||
var ATOMIC_START_TOKEN = makePredicate("atom num regexp string");
|
||||
|
||||
/* -----[ Parser ]----- */
|
||||
|
||||
@@ -741,7 +742,7 @@ function parse($TEXT, options) {
|
||||
|
||||
function parenthesised() {
|
||||
expect("(");
|
||||
var exp = expression(true);
|
||||
var exp = expression();
|
||||
expect(")");
|
||||
return exp;
|
||||
}
|
||||
@@ -769,7 +770,7 @@ function parse($TEXT, options) {
|
||||
switch (S.token.type) {
|
||||
case "string":
|
||||
var dir = S.in_directives;
|
||||
var body = expression(true);
|
||||
var body = expression();
|
||||
if (dir) {
|
||||
if (body instanceof AST_String) {
|
||||
var value = body.start.raw.slice(1, -1);
|
||||
@@ -887,7 +888,7 @@ function parse($TEXT, options) {
|
||||
if (is("punc", ";")) {
|
||||
next();
|
||||
} else if (!can_insert_semicolon()) {
|
||||
value = expression(true);
|
||||
value = expression();
|
||||
semicolon();
|
||||
}
|
||||
return new AST_Return({
|
||||
@@ -905,7 +906,7 @@ function parse($TEXT, options) {
|
||||
next();
|
||||
if (has_newline_before(S.token))
|
||||
croak("Illegal newline after 'throw'");
|
||||
var value = expression(true);
|
||||
var value = expression();
|
||||
semicolon();
|
||||
return new AST_Throw({
|
||||
value: value
|
||||
@@ -956,9 +957,7 @@ function parse($TEXT, options) {
|
||||
// https://github.com/mishoo/UglifyJS/issues/287
|
||||
label.references.forEach(function(ref) {
|
||||
if (ref instanceof AST_Continue) {
|
||||
ref = ref.label.start;
|
||||
croak("Continue label `" + label.name + "` refers to non-IterationStatement.",
|
||||
ref.line, ref.col, ref.pos);
|
||||
token_error(ref.label.start, "Continue label `" + label.name + "` must refer to IterationStatement");
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -966,7 +965,7 @@ function parse($TEXT, options) {
|
||||
}
|
||||
|
||||
function simple_statement() {
|
||||
var body = expression(true);
|
||||
var body = expression();
|
||||
semicolon();
|
||||
return new AST_SimpleStatement({ body: body });
|
||||
}
|
||||
@@ -980,7 +979,7 @@ function parse($TEXT, options) {
|
||||
ldef = find_if(function(l) {
|
||||
return l.name == label.name;
|
||||
}, S.labels);
|
||||
if (!ldef) croak("Undefined label " + label.name);
|
||||
if (!ldef) token_error(label.start, "Undefined label " + label.name);
|
||||
label.thedef = ldef;
|
||||
} else if (S.in_loop == 0) croak(type.TYPE + " not inside a loop or switch");
|
||||
semicolon();
|
||||
@@ -999,13 +998,14 @@ function parse($TEXT, options) {
|
||||
? (next(), let_(true))
|
||||
: is("keyword", "var")
|
||||
? (next(), var_(true))
|
||||
: expression(true, true);
|
||||
: expression(true);
|
||||
if (is("operator", "in")) {
|
||||
if (init instanceof AST_Definitions) {
|
||||
if (init.definitions.length > 1)
|
||||
croak("Only one variable declaration allowed in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||
if (init.definitions.length > 1) {
|
||||
token_error(init.start, "Only one variable declaration allowed in for..in loop");
|
||||
}
|
||||
} else if (!(is_assignable(init) || (init = to_destructured(init)) instanceof AST_Destructured)) {
|
||||
croak("Invalid left-hand side in for..in loop", init.start.line, init.start.col, init.start.pos);
|
||||
token_error(init.start, "Invalid left-hand side in for..in loop");
|
||||
}
|
||||
next();
|
||||
return for_in(init);
|
||||
@@ -1016,9 +1016,9 @@ function parse($TEXT, options) {
|
||||
|
||||
function regular_for(init) {
|
||||
expect(";");
|
||||
var test = is("punc", ";") ? null : expression(true);
|
||||
var test = is("punc", ";") ? null : expression();
|
||||
expect(";");
|
||||
var step = is("punc", ")") ? null : expression(true);
|
||||
var step = is("punc", ")") ? null : expression();
|
||||
expect(")");
|
||||
return new AST_For({
|
||||
init : init,
|
||||
@@ -1029,7 +1029,7 @@ function parse($TEXT, options) {
|
||||
}
|
||||
|
||||
function for_in(init) {
|
||||
var obj = expression(true);
|
||||
var obj = expression();
|
||||
expect(")");
|
||||
return new AST_ForIn({
|
||||
init : init,
|
||||
@@ -1038,6 +1038,71 @@ function parse($TEXT, options) {
|
||||
});
|
||||
}
|
||||
|
||||
function to_funarg(node) {
|
||||
if (node instanceof AST_Array) return new AST_DestructuredArray({
|
||||
start: node.start,
|
||||
elements: node.elements.map(function(node) {
|
||||
return node instanceof AST_Hole ? node : to_funarg(node);
|
||||
}),
|
||||
end: node.end,
|
||||
});
|
||||
if (node instanceof AST_Object) return new AST_DestructuredObject({
|
||||
start: node.start,
|
||||
properties: node.properties.map(function(prop) {
|
||||
if (!(prop instanceof AST_ObjectKeyVal)) token_error(prop.start, "Invalid destructuring assignment");
|
||||
return new AST_DestructuredKeyVal({
|
||||
start: prop.start,
|
||||
key: prop.key,
|
||||
value: to_funarg(prop.value),
|
||||
end: prop.end,
|
||||
});
|
||||
}),
|
||||
end: node.end,
|
||||
});
|
||||
if (node instanceof AST_SymbolRef) return new AST_SymbolFunarg(node);
|
||||
token_error(node.start, "Invalid arrow parameter");
|
||||
}
|
||||
|
||||
function arrow(exprs, start) {
|
||||
var was_async = S.in_async;
|
||||
S.in_async = false;
|
||||
var was_funarg = S.in_funarg;
|
||||
S.in_funarg = S.in_function;
|
||||
var argnames = exprs.map(to_funarg);
|
||||
S.in_funarg = was_funarg;
|
||||
expect("=>");
|
||||
var body, value;
|
||||
var loop = S.in_loop;
|
||||
var labels = S.labels;
|
||||
++S.in_function;
|
||||
S.in_directives = true;
|
||||
S.input.push_directives_stack();
|
||||
S.in_loop = 0;
|
||||
S.labels = [];
|
||||
if (is("punc", "{")) {
|
||||
body = block_();
|
||||
value = null;
|
||||
if (S.input.has_directive("use strict")) {
|
||||
argnames.forEach(strict_verify_symbol);
|
||||
}
|
||||
} else {
|
||||
body = [];
|
||||
value = maybe_assign();
|
||||
}
|
||||
S.input.pop_directives_stack();
|
||||
--S.in_function;
|
||||
S.in_loop = loop;
|
||||
S.labels = labels;
|
||||
S.in_async = was_async;
|
||||
return new AST_Arrow({
|
||||
start: start,
|
||||
argnames: argnames,
|
||||
body: body,
|
||||
value: value,
|
||||
end: prev(),
|
||||
});
|
||||
}
|
||||
|
||||
var function_ = function(ctor) {
|
||||
var was_async = S.in_async;
|
||||
var name;
|
||||
@@ -1118,7 +1183,7 @@ function parse($TEXT, options) {
|
||||
cur = [];
|
||||
branch = new AST_Case({
|
||||
start : (tmp = S.token, next(), tmp),
|
||||
expression : expression(true),
|
||||
expression : expression(),
|
||||
body : cur
|
||||
});
|
||||
a.push(branch);
|
||||
@@ -1187,7 +1252,7 @@ function parse($TEXT, options) {
|
||||
var value = null;
|
||||
if (is("operator", "=")) {
|
||||
next();
|
||||
value = expression(false, no_in);
|
||||
value = maybe_assign(no_in);
|
||||
} else if (!no_in && (type === AST_SymbolConst || name instanceof AST_Destructured)) {
|
||||
croak("Missing initializer in declaration");
|
||||
}
|
||||
@@ -1251,9 +1316,6 @@ function parse($TEXT, options) {
|
||||
function as_atom_node() {
|
||||
var tok = S.token, ret;
|
||||
switch (tok.type) {
|
||||
case "name":
|
||||
ret = _make_symbol(AST_SymbolRef, tok);
|
||||
break;
|
||||
case "num":
|
||||
ret = new AST_Number({ start: tok, end: tok, value: tok.value });
|
||||
break;
|
||||
@@ -1295,7 +1357,11 @@ function parse($TEXT, options) {
|
||||
switch (start.value) {
|
||||
case "(":
|
||||
next();
|
||||
var ex = expression(true);
|
||||
if (is("punc", ")")) {
|
||||
next();
|
||||
return arrow([], start);
|
||||
}
|
||||
var ex = expression(false, true);
|
||||
var len = start.comments_before.length;
|
||||
[].unshift.apply(ex.start.comments_before, start.comments_before);
|
||||
start.comments_before.length = 0;
|
||||
@@ -1318,6 +1384,7 @@ function parse($TEXT, options) {
|
||||
end.comments_after = ex.end.comments_after;
|
||||
ex.end = end;
|
||||
if (ex instanceof AST_Call) mark_pure(ex);
|
||||
if (is("punc", "=>")) return arrow(ex instanceof AST_Sequence ? ex.expressions : [ ex ], start);
|
||||
return subscripts(ex, allow_calls);
|
||||
case "[":
|
||||
return subscripts(array_(), allow_calls);
|
||||
@@ -1340,6 +1407,11 @@ function parse($TEXT, options) {
|
||||
func.end = prev();
|
||||
return subscripts(func, allow_calls);
|
||||
}
|
||||
if (is("name")) {
|
||||
var sym = _make_symbol(AST_SymbolRef, start);
|
||||
next();
|
||||
return is("punc", "=>") ? arrow([ sym ], start) : subscripts(sym, allow_calls);
|
||||
}
|
||||
if (ATOMIC_START_TOKEN[S.token.type]) {
|
||||
return subscripts(as_atom_node(), allow_calls);
|
||||
}
|
||||
@@ -1347,14 +1419,14 @@ function parse($TEXT, options) {
|
||||
};
|
||||
|
||||
function expr_list(closing, allow_trailing_comma, allow_empty, parser) {
|
||||
if (!parser) parser = expression;
|
||||
if (!parser) parser = maybe_assign;
|
||||
var first = true, a = [];
|
||||
while (!is("punc", closing)) {
|
||||
if (first) first = false; else expect(",");
|
||||
if (allow_trailing_comma && is("punc", closing)) break;
|
||||
if (allow_empty && is("punc", ",")) {
|
||||
a.push(new AST_Hole({ start: S.token, end: S.token }));
|
||||
} else if (parser === expression && is("operator", "...")) {
|
||||
} else if (parser === maybe_assign && is("operator", "...")) {
|
||||
a.push(new AST_Spread({
|
||||
start: S.token,
|
||||
expression: (next(), parser()),
|
||||
@@ -1391,7 +1463,7 @@ function parse($TEXT, options) {
|
||||
next();
|
||||
a.push(new AST_Spread({
|
||||
start: start,
|
||||
expression: expression(false),
|
||||
expression: maybe_assign(),
|
||||
end: prev(),
|
||||
}));
|
||||
continue;
|
||||
@@ -1440,7 +1512,7 @@ function parse($TEXT, options) {
|
||||
a.push(new AST_ObjectKeyVal({
|
||||
start: start,
|
||||
key: key,
|
||||
value: expression(false),
|
||||
value: maybe_assign(),
|
||||
end: prev(),
|
||||
}));
|
||||
}
|
||||
@@ -1463,7 +1535,7 @@ function parse($TEXT, options) {
|
||||
case "punc":
|
||||
if (tmp.value != "[") unexpected();
|
||||
next();
|
||||
var key = expression(false);
|
||||
var key = maybe_assign();
|
||||
expect("]");
|
||||
return key;
|
||||
default:
|
||||
@@ -1490,7 +1562,7 @@ function parse($TEXT, options) {
|
||||
|
||||
function strict_verify_symbol(sym) {
|
||||
if (sym.name == "arguments" || sym.name == "eval")
|
||||
croak("Unexpected " + sym.name + " in strict mode", sym.start.line, sym.start.col, sym.start.pos);
|
||||
token_error(sym.start, "Unexpected " + sym.name + " in strict mode");
|
||||
}
|
||||
|
||||
function as_symbol(type, noerror) {
|
||||
@@ -1580,7 +1652,7 @@ function parse($TEXT, options) {
|
||||
}
|
||||
if (is("punc", "[")) {
|
||||
next();
|
||||
var prop = expression(true);
|
||||
var prop = expression();
|
||||
expect("]");
|
||||
return subscripts(new AST_Sub({
|
||||
start : start,
|
||||
@@ -1629,11 +1701,11 @@ function parse($TEXT, options) {
|
||||
case "++":
|
||||
case "--":
|
||||
if (!is_assignable(expr))
|
||||
croak("Invalid use of " + op + " operator", token.line, token.col, token.pos);
|
||||
token_error(token, "Invalid use of " + op + " operator");
|
||||
break;
|
||||
case "delete":
|
||||
if (expr instanceof AST_SymbolRef && S.input.has_directive("use strict"))
|
||||
croak("Calling delete on expression not allowed in strict mode", expr.start.line, expr.start.col, expr.start.pos);
|
||||
token_error(expr.start, "Calling delete on expression not allowed in strict mode");
|
||||
break;
|
||||
}
|
||||
return new ctor({ operator: op, expression: expr });
|
||||
@@ -1679,13 +1751,13 @@ function parse($TEXT, options) {
|
||||
var expr = expr_ops(no_in);
|
||||
if (is("operator", "?")) {
|
||||
next();
|
||||
var yes = expression(false);
|
||||
var yes = maybe_assign();
|
||||
expect(":");
|
||||
return new AST_Conditional({
|
||||
start : start,
|
||||
condition : expr,
|
||||
consequent : yes,
|
||||
alternative : expression(false, no_in),
|
||||
alternative : maybe_assign(no_in),
|
||||
end : prev()
|
||||
});
|
||||
}
|
||||
@@ -1728,7 +1800,7 @@ function parse($TEXT, options) {
|
||||
});
|
||||
}
|
||||
|
||||
var maybe_assign = function(no_in) {
|
||||
function maybe_assign(no_in) {
|
||||
var start = S.token;
|
||||
var left = maybe_conditional(no_in), val = S.token.value;
|
||||
if (is("operator") && ASSIGNMENT[val]) {
|
||||
@@ -1745,23 +1817,23 @@ function parse($TEXT, options) {
|
||||
croak("Invalid assignment");
|
||||
}
|
||||
return left;
|
||||
};
|
||||
}
|
||||
|
||||
var expression = function(commas, no_in) {
|
||||
function expression(no_in, maybe_arrow) {
|
||||
var start = S.token;
|
||||
var exprs = [];
|
||||
while (true) {
|
||||
exprs.push(maybe_assign(no_in));
|
||||
if (!commas || !is("punc", ",")) break;
|
||||
if (!is("punc", ",")) break;
|
||||
next();
|
||||
commas = true;
|
||||
if (maybe_arrow && is("punc", ")") && is_token(peek(), "punc", "=>")) break;
|
||||
}
|
||||
return exprs.length == 1 ? exprs[0] : new AST_Sequence({
|
||||
start : start,
|
||||
expressions : exprs,
|
||||
end : peek()
|
||||
end : prev()
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function in_loop(cont) {
|
||||
++S.in_loop;
|
||||
@@ -1772,7 +1844,7 @@ function parse($TEXT, options) {
|
||||
|
||||
if (options.expression) {
|
||||
handle_regexp();
|
||||
return expression(true);
|
||||
return expression();
|
||||
}
|
||||
|
||||
return function() {
|
||||
|
||||
13
lib/scope.js
13
lib/scope.js
@@ -212,7 +212,11 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
argname.walk(tw);
|
||||
});
|
||||
in_arg.pop();
|
||||
walk_body(node, tw);
|
||||
if (node instanceof AST_Arrow && node.value) {
|
||||
node.value.walk(tw);
|
||||
} else {
|
||||
walk_body(node, tw);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (node instanceof AST_LoopControl) {
|
||||
@@ -249,7 +253,9 @@ AST_Toplevel.DEFMETHOD("figure_out_scope", function(options) {
|
||||
}
|
||||
if (!sym) {
|
||||
sym = self.def_global(node);
|
||||
} else if (name == "arguments" && sym.scope instanceof AST_Lambda) {
|
||||
} else if (name == "arguments"
|
||||
&& sym.orig[0] instanceof AST_SymbolFunarg
|
||||
&& !(sym.scope instanceof AST_Arrow)) {
|
||||
if (!(tw.parent() instanceof AST_PropAccess)) {
|
||||
sym.scope.uses_arguments = "d";
|
||||
} else if (!sym.scope.uses_arguments) {
|
||||
@@ -360,6 +366,9 @@ AST_BlockScope.DEFMETHOD("init_vars", function(parent_scope) {
|
||||
AST_Scope.DEFMETHOD("init_vars", function(parent_scope) {
|
||||
init_scope_vars(this, parent_scope);
|
||||
});
|
||||
AST_Arrow.DEFMETHOD("init_vars", function(parent_scope) {
|
||||
init_scope_vars(this, parent_scope);
|
||||
});
|
||||
AST_Lambda.DEFMETHOD("init_vars", function(parent_scope) {
|
||||
init_scope_vars(this, parent_scope);
|
||||
this.uses_arguments = false;
|
||||
|
||||
@@ -131,6 +131,14 @@ TreeTransformer.prototype = new TreeWalker;
|
||||
self.argnames = do_list(self.argnames, tw);
|
||||
self.body = do_list(self.body, tw);
|
||||
});
|
||||
DEF(AST_Arrow, function(self, tw) {
|
||||
self.argnames = do_list(self.argnames, tw);
|
||||
if (self.value) {
|
||||
self.value = self.value.transform(tw);
|
||||
} else {
|
||||
self.body = do_list(self.body, tw);
|
||||
}
|
||||
});
|
||||
DEF(AST_Call, function(self, tw) {
|
||||
self.expression = self.expression.transform(tw);
|
||||
self.args = do_list(self.args, tw);
|
||||
|
||||
@@ -238,13 +238,15 @@ function HOP(obj, prop) {
|
||||
// return true if the node at the top of the stack (that means the
|
||||
// innermost node in the current output) is lexically the first in
|
||||
// a statement.
|
||||
function first_in_statement(stack) {
|
||||
function first_in_statement(stack, arrow) {
|
||||
var node = stack.parent(-1);
|
||||
for (var i = 0, p; p = stack.parent(i++); node = p) {
|
||||
if (p.TYPE == "Call") {
|
||||
if (p.expression === node) continue;
|
||||
if (p instanceof AST_Arrow) {
|
||||
return arrow && p.value === node;
|
||||
} else if (p instanceof AST_Binary) {
|
||||
if (p.left === node) continue;
|
||||
} else if (p.TYPE == "Call") {
|
||||
if (p.expression === node) continue;
|
||||
} else if (p instanceof AST_Conditional) {
|
||||
if (p.condition === node) continue;
|
||||
} else if (p instanceof AST_PropAccess) {
|
||||
|
||||
338
test/compress/arrows.js
Normal file
338
test/compress/arrows.js
Normal file
@@ -0,0 +1,338 @@
|
||||
no_funarg: {
|
||||
input: {
|
||||
(() => console.log(42))();
|
||||
}
|
||||
expect_exact: "(()=>console.log(42))();"
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
single_funarg: {
|
||||
input: {
|
||||
(a => console.log(a))(42);
|
||||
}
|
||||
expect_exact: "(a=>console.log(a))(42);"
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
multiple_funargs: {
|
||||
input: {
|
||||
((a, b) => console.log(a, b))("foo", "bar");
|
||||
}
|
||||
expect_exact: '((a,b)=>console.log(a,b))("foo","bar");'
|
||||
expect_stdout: "foo bar"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
destructured_funarg: {
|
||||
input: {
|
||||
(([ a, b, c ]) => console.log(a, b, c))("foo");
|
||||
}
|
||||
expect_exact: '(([a,b,c])=>console.log(a,b,c))("foo");'
|
||||
expect_stdout: "f o o"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
await_parenthesis: {
|
||||
input: {
|
||||
async function f() {
|
||||
await (a => a);
|
||||
}
|
||||
}
|
||||
expect_exact: "async function f(){await(a=>a)}"
|
||||
}
|
||||
|
||||
body_call: {
|
||||
input: {
|
||||
(() => {
|
||||
console.log("foo");
|
||||
console.log("bar");
|
||||
})();
|
||||
}
|
||||
expect_exact: '(()=>{console.log("foo");console.log("bar")})();'
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"bar",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
body_conditional: {
|
||||
input: {
|
||||
console.log((a => {}) ? "PASS" : "FAIL");
|
||||
}
|
||||
expect_exact: 'console.log((a=>{})?"PASS":"FAIL");'
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
destructured_object_value: {
|
||||
input: {
|
||||
console.log((a => ({} = a))(42));
|
||||
}
|
||||
expect_exact: "console.log((a=>({}=a))(42));"
|
||||
expect_stdout: "42"
|
||||
node_version: ">=6"
|
||||
}
|
||||
|
||||
function_value: {
|
||||
input: {
|
||||
console.log((a => function() {
|
||||
return a;
|
||||
})(42)());
|
||||
}
|
||||
expect_exact: "console.log((a=>function(){return a})(42)());"
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
in_value: {
|
||||
input: {
|
||||
console.log((a => a in {
|
||||
foo: 42,
|
||||
})("foo"));
|
||||
}
|
||||
expect_exact: 'console.log((a=>a in{foo:42})("foo"));'
|
||||
expect_stdout: "true"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
object_value: {
|
||||
input: {
|
||||
console.log((() => ({
|
||||
4: 2,
|
||||
}))()[4]);
|
||||
}
|
||||
expect_exact: "console.log((()=>({4:2}))()[4]);"
|
||||
expect_stdout: "2"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
object_first_in_value: {
|
||||
input: {
|
||||
console.log((a => ({
|
||||
p: a,
|
||||
}.p ? "FAIL" : "PASS"))());
|
||||
}
|
||||
expect_exact: 'console.log((a=>({p:a}).p?"FAIL":"PASS")());'
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
sequence_value: {
|
||||
input: {
|
||||
console.log((a => (console.log("foo"), a))("bar"));
|
||||
}
|
||||
expect_exact: 'console.log((a=>(console.log("foo"),a))("bar"));'
|
||||
expect_stdout: [
|
||||
"foo",
|
||||
"bar",
|
||||
]
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
side_effects_value: {
|
||||
options = {
|
||||
side_effects: true,
|
||||
}
|
||||
input: {
|
||||
console.log((a => function() {
|
||||
return a;
|
||||
})(42)());
|
||||
}
|
||||
expect: {
|
||||
console.log((a => function() {
|
||||
return a;
|
||||
})(42)());
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
arrow_property: {
|
||||
input: {
|
||||
console.log((a => 42).prototype);
|
||||
}
|
||||
expect_exact: "console.log((a=>42).prototype);"
|
||||
expect_stdout: "undefined"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
assign_arrow: {
|
||||
input: {
|
||||
var f = a => a;
|
||||
console.log(f(42));
|
||||
}
|
||||
expect_exact: "var f=a=>a;console.log(f(42));"
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
binary_arrow: {
|
||||
input: {
|
||||
console.log(4 || (() => 2));
|
||||
}
|
||||
expect_exact: "console.log(4||(()=>2));"
|
||||
expect_stdout: "4"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
unary_arrow: {
|
||||
input: {
|
||||
console.log(+(() => 42));
|
||||
}
|
||||
expect_exact: "console.log(+(()=>42));"
|
||||
expect_stdout: "NaN"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
trailing_comma: {
|
||||
input: {
|
||||
((a,) => console.log(a))(42);
|
||||
}
|
||||
expect_exact: "(a=>console.log(a))(42);"
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
drop_arguments: {
|
||||
options = {
|
||||
arguments: true,
|
||||
keep_fargs: false,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
return () => arguments[0];
|
||||
}("PASS")("FAIL"));
|
||||
}
|
||||
expect: {
|
||||
console.log(function(argument_0) {
|
||||
return () => argument_0;
|
||||
}("PASS")("FAIL"));
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
funarg_arguments: {
|
||||
options = {
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
console.log((arguments => arguments)(42));
|
||||
}
|
||||
expect: {
|
||||
console.log(42);
|
||||
}
|
||||
expect_stdout: "42"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
inline_arguments: {
|
||||
options = {
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
return () => arguments[0];
|
||||
}("PASS")("FAIL"));
|
||||
}
|
||||
expect: {
|
||||
console.log(function() {
|
||||
return () => arguments[0];
|
||||
}("PASS")("FAIL"));
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
var_arguments: {
|
||||
options = {
|
||||
inline: true,
|
||||
properties: true,
|
||||
reduce_vars: true,
|
||||
side_effects: true,
|
||||
unused: true,
|
||||
}
|
||||
input: {
|
||||
console.log(function() {
|
||||
return () => {
|
||||
var arguments = [ "PASS" ];
|
||||
return arguments;
|
||||
};
|
||||
}("FAIL 1")("FAIL 2")[0]);
|
||||
}
|
||||
expect: {
|
||||
console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
negate: {
|
||||
options = {
|
||||
conditionals: true,
|
||||
}
|
||||
input: {
|
||||
if (!console ? 0 : () => 1)
|
||||
console.log("PASS");
|
||||
}
|
||||
expect: {
|
||||
(console ? () => 1 : 0) && console.log("PASS");
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
inline_this: {
|
||||
options = {
|
||||
inline: true,
|
||||
}
|
||||
input: {
|
||||
var o = {
|
||||
p: function() {
|
||||
return function() {
|
||||
return () => this.q;
|
||||
}();
|
||||
},
|
||||
q: "FAIL",
|
||||
};
|
||||
q = "PASS";
|
||||
console.log(o.p()());
|
||||
}
|
||||
expect: {
|
||||
var o = {
|
||||
p: function() {
|
||||
return function() {
|
||||
return () => this.q;
|
||||
}();
|
||||
},
|
||||
q: "FAIL",
|
||||
};
|
||||
q = "PASS";
|
||||
console.log(o.p()());
|
||||
}
|
||||
expect_stdout: "PASS"
|
||||
node_version: ">=4"
|
||||
}
|
||||
|
||||
trim_body: {
|
||||
options = {
|
||||
arrows: true,
|
||||
}
|
||||
input: {
|
||||
var f = a => {
|
||||
return a;
|
||||
};
|
||||
var g = b => void b;
|
||||
console.log(f("PASS"), g("FAIL"));
|
||||
}
|
||||
expect: {
|
||||
var f = a => a;
|
||||
var g = b => {};
|
||||
console.log(f("PASS"), g("FAIL"));
|
||||
}
|
||||
expect_stdout: "PASS undefined"
|
||||
node_version: ">=4"
|
||||
}
|
||||
@@ -629,7 +629,8 @@ function is_timed_out(result) {
|
||||
}
|
||||
|
||||
function is_statement(node) {
|
||||
return node instanceof U.AST_Statement && !(node instanceof U.AST_AsyncFunction || node instanceof U.AST_Function);
|
||||
return node instanceof U.AST_Statement
|
||||
&& !(node instanceof U.AST_Arrow || node instanceof U.AST_AsyncFunction || node instanceof U.AST_Function);
|
||||
}
|
||||
|
||||
function merge_sequence(array, node) {
|
||||
|
||||
@@ -132,9 +132,11 @@ var SUPPORT = function(matrix) {
|
||||
}
|
||||
return matrix;
|
||||
}({
|
||||
arrow: "a => 0;",
|
||||
async: "async function f(){}",
|
||||
catch_omit_var: "try {} catch {}",
|
||||
computed_key: "({[0]: 0});",
|
||||
const_block: "var a; { const a = 0; }",
|
||||
destructuring: "[] = [];",
|
||||
let: "let a;",
|
||||
spread: "[...[]];",
|
||||
@@ -361,7 +363,7 @@ function createTopLevelCode() {
|
||||
return [
|
||||
strictMode(),
|
||||
"var _calls_ = 10, a = 100, b = 10, c = 0;",
|
||||
rng(2) == 0
|
||||
rng(2)
|
||||
? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0)
|
||||
: createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0),
|
||||
// preceding `null` makes for a cleaner output (empty string still shows up etc)
|
||||
@@ -382,7 +384,9 @@ function addTrailingComma(list) {
|
||||
return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list;
|
||||
}
|
||||
|
||||
function createParams(noDuplicate) {
|
||||
function createParams(was_async, noDuplicate) {
|
||||
var save_async = async;
|
||||
if (was_async) async = true;
|
||||
var len = unique_vars.length;
|
||||
var params = [];
|
||||
for (var n = rng(4); --n >= 0;) {
|
||||
@@ -391,6 +395,7 @@ function createParams(noDuplicate) {
|
||||
params.push(name);
|
||||
}
|
||||
unique_vars.length = len;
|
||||
async = save_async;
|
||||
return addTrailingComma(params.join(", "));
|
||||
}
|
||||
|
||||
@@ -411,13 +416,13 @@ function createArgs(recurmax, stmtDepth, canThrow) {
|
||||
args.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow));
|
||||
break;
|
||||
default:
|
||||
args.push(rng(2) ? createValue() : createExpression(recurmax, COMMA_OK, stmtDepth, canThrow));
|
||||
args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
|
||||
break;
|
||||
}
|
||||
return addTrailingComma(args.join(", "));
|
||||
}
|
||||
|
||||
function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames, was_async) {
|
||||
function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async) {
|
||||
var avoid = [];
|
||||
var len = unique_vars.length;
|
||||
var pairs = createPairs(recurmax);
|
||||
@@ -425,42 +430,46 @@ function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames,
|
||||
return pairs;
|
||||
|
||||
function createAssignmentValue(recurmax) {
|
||||
var current = VAR_NAMES;
|
||||
VAR_NAMES = (varNames || VAR_NAMES).slice();
|
||||
var save_async = async;
|
||||
if (was_async != null) async = was_async;
|
||||
var value = varNames && rng(2) ? createValue() : createExpression(recurmax, noComma, stmtDepth, canThrow);
|
||||
var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore);
|
||||
var value = nameLenBefore && rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
|
||||
if (save_vars) [].push.apply(VAR_NAMES, save_vars);
|
||||
async = save_async;
|
||||
VAR_NAMES = current;
|
||||
return value;
|
||||
}
|
||||
|
||||
function createKey(recurmax, keys) {
|
||||
addAvoidVars(avoid);
|
||||
var key;
|
||||
do {
|
||||
key = createObjectKey(recurmax, stmtDepth, canThrow);
|
||||
} while (keys.indexOf(key) >= 0);
|
||||
removeAvoidVars(avoid);
|
||||
return key;
|
||||
}
|
||||
|
||||
function createName() {
|
||||
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||
var save_async = async;
|
||||
if (was_async) async = true;
|
||||
var name = createVarName(MANDATORY);
|
||||
async = save_async;
|
||||
unique_vars.length -= 6;
|
||||
avoid.push(name);
|
||||
unique_vars.push(name);
|
||||
return name;
|
||||
}
|
||||
|
||||
function createPairs(recurmax) {
|
||||
var names = [], values = [];
|
||||
var m = rng(4), n = rng(4);
|
||||
if (!varNames) m = Math.max(m, n, 1);
|
||||
if (!nameLenBefore) m = Math.max(m, n, 1);
|
||||
for (var i = Math.max(m, n); --i >= 0;) {
|
||||
if (i < m && i < n) {
|
||||
createDestructured(recurmax, names, values);
|
||||
continue;
|
||||
}
|
||||
if (i < m) {
|
||||
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||
var name = createVarName(MANDATORY);
|
||||
unique_vars.length -= 6;
|
||||
avoid.push(name);
|
||||
unique_vars.push(name);
|
||||
names.unshift(name);
|
||||
names.unshift(createName());
|
||||
}
|
||||
if (i < n) {
|
||||
values.unshift(createAssignmentValue(recurmax));
|
||||
@@ -512,26 +521,28 @@ function createAssignmentPairs(recurmax, noComma, stmtDepth, canThrow, varNames,
|
||||
keys[index] = key;
|
||||
}
|
||||
});
|
||||
if (was_async) avoid.push("await");
|
||||
addAvoidVars(avoid);
|
||||
var save_async = async;
|
||||
async = false;
|
||||
names.unshift("{ " + addTrailingComma(pairs.names.map(function(name, index) {
|
||||
var key = index in keys ? keys[index] : rng(10) && createKey(recurmax, keys);
|
||||
return key ? key + ": " + name : name;
|
||||
}).join(", ")) + " }");
|
||||
var save_async = async;
|
||||
if (was_async) removeAvoidVars([ avoid.pop() ]);
|
||||
if (was_async != null) async = was_async;
|
||||
var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore);
|
||||
values.unshift("{ " + addTrailingComma(pairs.values.map(function(value, index) {
|
||||
var key = index in keys ? keys[index] : createKey(recurmax, keys);
|
||||
return key + ": " + value;
|
||||
}).join(", ")) + " }");
|
||||
if (save_vars) [].push.apply(VAR_NAMES, save_vars);
|
||||
async = save_async;
|
||||
removeAvoidVars(avoid);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||
var name = createVarName(MANDATORY);
|
||||
unique_vars.length -= 6;
|
||||
avoid.push(name);
|
||||
unique_vars.push(name);
|
||||
names.unshift(name);
|
||||
names.unshift(createName());
|
||||
values.unshift(createAssignmentValue(recurmax));
|
||||
break;
|
||||
}
|
||||
@@ -545,12 +556,12 @@ function filterDirective(s) {
|
||||
|
||||
function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
||||
var block_len = block_vars.length;
|
||||
var var_len = VAR_NAMES.length;
|
||||
var nameLenBefore = VAR_NAMES.length;
|
||||
var consts = [];
|
||||
var lets = [];
|
||||
unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
|
||||
while (!rng(block_vars.length > block_len ? 10 : 100)) {
|
||||
var name = createVarName(MANDATORY, DONT_STORE);
|
||||
var name = createVarName(MANDATORY);
|
||||
if (SUPPORT.let && rng(2)) {
|
||||
lets.push(name);
|
||||
} else {
|
||||
@@ -568,8 +579,8 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
||||
return createDefinitions("let", lets) + "\n" + createDefinitions("const", consts) + "\n";
|
||||
}
|
||||
});
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
block_vars.length = block_len;
|
||||
if (consts.length || lets.length) VAR_NAMES.splice(var_len, consts.length + lets.length);
|
||||
|
||||
function createDefinitions(type, names) {
|
||||
if (!names.length) return "";
|
||||
@@ -602,6 +613,7 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
||||
removeAvoidVars([ name ]);
|
||||
return name + " = " + value;
|
||||
}).join(", ") + ";";
|
||||
names.length = 0;
|
||||
break;
|
||||
}
|
||||
removeAvoidVars(names);
|
||||
@@ -609,6 +621,16 @@ function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
||||
}
|
||||
}
|
||||
|
||||
function mayCreateBlockVariables(recurmax, stmtDepth, canThrow, fn) {
|
||||
if (SUPPORT.const_block) {
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, fn);
|
||||
} else {
|
||||
fn(function() {
|
||||
return "";
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function makeFunction(name) {
|
||||
return (async ? "async function " : "function ") + name;
|
||||
}
|
||||
@@ -618,7 +640,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
||||
if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
|
||||
var s = [];
|
||||
var name, args;
|
||||
var varNames = VAR_NAMES.slice();
|
||||
var nameLenBefore = VAR_NAMES.length;
|
||||
var save_async = async;
|
||||
async = SUPPORT.async && rng(50) == 0;
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
@@ -632,11 +654,11 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
||||
var params;
|
||||
if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
|
||||
called[name] = false;
|
||||
var pairs = createAssignmentPairs(recurmax, COMMA_OK, stmtDepth, canThrow, varNames, save_async);
|
||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||
params = addTrailingComma(pairs.names.join(", "));
|
||||
args = addTrailingComma(pairs.values.join(", "));
|
||||
} else {
|
||||
params = createParams();
|
||||
params = createParams(save_async);
|
||||
}
|
||||
s.push(makeFunction(name) + "(" + params + "){", strictMode());
|
||||
s.push(defns());
|
||||
@@ -651,7 +673,7 @@ function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
|
||||
s = filterDirective(s).join("\n");
|
||||
});
|
||||
async = save_async;
|
||||
VAR_NAMES = varNames;
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
|
||||
if (!allowDefun) {
|
||||
// avoid "function statements" (decl inside statements)
|
||||
@@ -676,7 +698,7 @@ function _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotR
|
||||
|
||||
function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
|
||||
var s = "";
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
s += defns() + "\n";
|
||||
s += _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
|
||||
});
|
||||
@@ -736,7 +758,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
||||
var label = createLabel(canBreak);
|
||||
return label.target + "{" + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + "}";
|
||||
case STMT_IF_ELSE:
|
||||
return "if (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) === 1 ? " else " + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : "");
|
||||
return "if (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + (rng(2) ? " else " + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) : "");
|
||||
case STMT_DO_WHILE:
|
||||
var label = createLabel(canBreak, canContinue);
|
||||
canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
|
||||
@@ -777,7 +799,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
||||
return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
|
||||
case STMT_VAR:
|
||||
if (SUPPORT.destructuring && rng(20) == 0) {
|
||||
var pairs = createAssignmentPairs(recurmax, NO_COMMA, stmtDepth, canThrow);
|
||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow);
|
||||
return "var " + pairs.names.map(function(name, index) {
|
||||
return index in pairs.values ? name + " = " + pairs.values[index] : name;
|
||||
}).join(", ") + ";";
|
||||
@@ -837,7 +859,7 @@ function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn
|
||||
// the catch var should only be accessible in the catch clause...
|
||||
// we have to do go through some trouble here to prevent leaking it
|
||||
var nameLenBefore = VAR_NAMES.length;
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
if (SUPPORT.catch_omit_var && rng(20) == 0) {
|
||||
s += " catch { ";
|
||||
} else {
|
||||
@@ -911,10 +933,10 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
switch (rng(_createExpression.N)) {
|
||||
case p++:
|
||||
case p++:
|
||||
return createUnaryPrefix() + (rng(2) === 1 ? "a" : "b");
|
||||
return createUnaryPrefix() + (rng(2) ? "a" : "b");
|
||||
case p++:
|
||||
case p++:
|
||||
return (rng(2) === 1 ? "a" : "b") + createUnaryPostfix();
|
||||
return (rng(2) ? "a" : "b") + createUnaryPostfix();
|
||||
case p++:
|
||||
case p++:
|
||||
// parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
|
||||
@@ -966,12 +988,56 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
var s = [];
|
||||
switch (rng(5)) {
|
||||
case 0:
|
||||
s.push(
|
||||
"(" + makeFunction(name) + "(){",
|
||||
strictMode(),
|
||||
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||
rng(2) == 0 ? "})" : "})()"
|
||||
);
|
||||
if (SUPPORT.arrow && !async && !name && rng(2)) {
|
||||
var args, suffix;
|
||||
(rng(2) ? createBlockVariables : function() {
|
||||
arguments[3]();
|
||||
})(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
var params;
|
||||
if (SUPPORT.destructuring && rng(2)) {
|
||||
var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async);
|
||||
params = addTrailingComma(pairs.names.join(", "));
|
||||
args = addTrailingComma(pairs.values.join(", "));
|
||||
} else {
|
||||
params = createParams(save_async, NO_DUPLICATE);
|
||||
}
|
||||
if (defns) {
|
||||
s.push(
|
||||
"((" + params + ") => {",
|
||||
strictMode(),
|
||||
defns(),
|
||||
_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)
|
||||
);
|
||||
suffix = "})";
|
||||
} else {
|
||||
s.push("((" + params + ") => ");
|
||||
switch (rng(4)) {
|
||||
case 0:
|
||||
s.push('(typeof arguments != "undefined" && arguments && arguments[' + rng(3) + "])");
|
||||
break;
|
||||
case 1:
|
||||
s.push("(this && this." + getDotKey() + ")");
|
||||
break;
|
||||
default:
|
||||
s.push(createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
|
||||
break;
|
||||
}
|
||||
suffix = ")";
|
||||
}
|
||||
});
|
||||
async = save_async;
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
|
||||
if (args) suffix += "(" + args + ")";
|
||||
s.push(suffix);
|
||||
} else {
|
||||
s.push(
|
||||
"(" + makeFunction(name) + "(){",
|
||||
strictMode(),
|
||||
createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||
rng(2) ? "})" : "})()"
|
||||
);
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
s.push(
|
||||
@@ -1002,7 +1068,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
var instantiate = rng(4) ? "new " : "";
|
||||
s.push(
|
||||
instantiate + "function " + name + "(" + createParams() + "){",
|
||||
instantiate + "function " + name + "(" + createParams(save_async) + "){",
|
||||
strictMode(),
|
||||
defns()
|
||||
);
|
||||
@@ -1014,7 +1080,7 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
|
||||
});
|
||||
async = save_async;
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
s.push(rng(2) == 0 ? "}" : "}(" + createArgs(recurmax, stmtDepth, canThrow) + ")");
|
||||
s.push(rng(2) ? "}" : "}(" + createArgs(recurmax, stmtDepth, canThrow) + ")");
|
||||
break;
|
||||
}
|
||||
async = save_async;
|
||||
@@ -1175,7 +1241,9 @@ function createObjectKey(recurmax, stmtDepth, canThrow) {
|
||||
}
|
||||
|
||||
function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
var namesLenBefore = VAR_NAMES.length;
|
||||
var nameLenBefore = VAR_NAMES.length;
|
||||
var save_async = async;
|
||||
async = SUPPORT.async && rng(50) == 0;
|
||||
var s;
|
||||
createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
|
||||
switch (rng(SUPPORT.computed_key ? 3 : 2)) {
|
||||
@@ -1206,7 +1274,7 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
break;
|
||||
default:
|
||||
s = [
|
||||
createObjectKey(recurmax, stmtDepth, canThrow) + "(" + createParams(NO_DUPLICATE) + "){",
|
||||
createObjectKey(recurmax, stmtDepth, canThrow) + "(" + createParams(save_async, NO_DUPLICATE) + "){",
|
||||
strictMode(),
|
||||
defns(),
|
||||
_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
|
||||
@@ -1215,7 +1283,8 @@ function createObjectFunction(recurmax, stmtDepth, canThrow) {
|
||||
break;
|
||||
}
|
||||
});
|
||||
VAR_NAMES.length = namesLenBefore;
|
||||
async = save_async;
|
||||
VAR_NAMES.length = nameLenBefore;
|
||||
return filterDirective(s).join("\n");
|
||||
}
|
||||
|
||||
@@ -1406,7 +1475,7 @@ function getVarName(noConst) {
|
||||
do {
|
||||
if (--tries < 0) return "a";
|
||||
name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
|
||||
} while (!name || avoid_vars.indexOf(name) >= 0 || noConst && block_vars.indexOf(name) >= 0);
|
||||
} while (!name || avoid_vars.indexOf(name) >= 0 || noConst && block_vars.indexOf(name) >= 0 || async && name == "await");
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -1418,7 +1487,7 @@ function createVarName(maybe, dontStore) {
|
||||
name = VAR_NAMES[rng(VAR_NAMES.length)];
|
||||
if (suffix) name += "_" + suffix;
|
||||
} while (unique_vars.indexOf(name) >= 0 || block_vars.indexOf(name) >= 0 || async && name == "await");
|
||||
if (suffix && !dontStore) VAR_NAMES.push(name);
|
||||
if (!dontStore) VAR_NAMES.push(name);
|
||||
return name;
|
||||
}
|
||||
return "";
|
||||
|
||||
Reference in New Issue
Block a user