Compare commits

...

13 Commits

Author SHA1 Message Date
Alex Lam S.L
e2ec270b04 v3.3.4 2017-12-31 00:01:14 +08:00
Alex Lam S.L
ed7a0a454e fix dead_code on escaped return assignment (#2693)
fixes #2692
2017-12-30 15:20:25 +08:00
Alex Lam S.L
d819559a01 minor clean-ups (#2686) 2017-12-29 14:04:52 +08:00
Alex Lam S.L
8ca49155a8 v3.3.3 2017-12-29 03:07:39 +08:00
Alex Lam S.L
b95e3338d9 fix pure_getters on AST_Binary (#2681)
fixes #2678
2017-12-28 17:01:01 +08:00
Alex Lam S.L
e40a0ee9c6 improve assignment variations (#2671) 2017-12-28 15:36:55 +08:00
Alex Lam S.L
cb62bd98d3 fix function inlining within loops (#2675)
fixes #2663
2017-12-28 02:53:14 +08:00
Alex Lam S.L
f30790b11b fix dead_code on return assignments (#2668)
fixes #2666
2017-12-27 07:40:34 +08:00
Alex Lam S.L
5205dbcbf4 retain recursive function names (#2667)
fixes #2665
2017-12-27 07:00:12 +08:00
Alex Lam S.L
3ff625de7e fix bugs on substituted AST_Defun (#2661)
fixes #2660
2017-12-27 05:31:37 +08:00
Alex Lam S.L
4832bc5d88 replace single-use recursive functions (#2659)
fixes #2628
2017-12-26 21:25:35 +08:00
Alex Lam S.L
7f342cb3e3 suppress inline within substituted AST_Scope (#2658)
fixes #2657
2017-12-26 18:56:59 +08:00
Alex Lam S.L
05e7d34ed4 improve unused over duplicate variable names (#2656) 2017-12-26 18:29:28 +08:00
11 changed files with 510 additions and 72 deletions

View File

@@ -319,6 +319,7 @@ merge(Compressor.prototype, {
} else { } else {
def.fixed = false; def.fixed = false;
} }
def.recursive_refs = 0;
def.references = []; def.references = [];
def.should_replace = undefined; def.should_replace = undefined;
def.single_use = undefined; def.single_use = undefined;
@@ -369,7 +370,7 @@ merge(Compressor.prototype, {
return compressor.option("unused") return compressor.option("unused")
&& !def.scope.uses_eval && !def.scope.uses_eval
&& !def.scope.uses_with && !def.scope.uses_with
&& def.references.length == 1 && def.references.length - def.recursive_refs == 1
&& tw.loop_ids[def.id] === tw.in_loop; && tw.loop_ids[def.id] === tw.in_loop;
} }
@@ -621,7 +622,9 @@ merge(Compressor.prototype, {
d.fixed = false; d.fixed = false;
} else if (d.fixed) { } else if (d.fixed) {
value = this.fixed_value(); value = this.fixed_value();
if (value && ref_once(tw, compressor, d)) { if (value instanceof AST_Lambda && recursive_ref(tw, d)) {
d.recursive_refs++;
} else if (value && ref_once(tw, compressor, d)) {
d.single_use = value instanceof AST_Lambda d.single_use = value instanceof AST_Lambda
|| d.scope === this.scope && value.is_constant_expression(); || d.scope === this.scope && value.is_constant_expression();
} else { } else {
@@ -1690,15 +1693,8 @@ merge(Compressor.prototype, {
return this.operator == "void"; return this.operator == "void";
}); });
def(AST_Binary, function(compressor) { def(AST_Binary, function(compressor) {
switch (this.operator) { return (this.operator == "&&" || this.operator == "||")
case "&&": && (this.left._dot_throw(compressor) || this.right._dot_throw(compressor));
return this.left._dot_throw(compressor);
case "||":
return this.left._dot_throw(compressor)
&& this.right._dot_throw(compressor);
default:
return false;
}
}) })
def(AST_Assign, function(compressor) { def(AST_Assign, function(compressor) {
return this.operator == "=" return this.operator == "="
@@ -2645,14 +2641,14 @@ merge(Compressor.prototype, {
var tw = new TreeWalker(function(node, descend){ var tw = new TreeWalker(function(node, descend){
if (node === self) return; if (node === self) return;
if (node instanceof AST_Defun) { if (node instanceof AST_Defun) {
var node_def = node.name.definition();
if (!drop_funcs && scope === self) { if (!drop_funcs && scope === self) {
var node_def = node.name.definition();
if (!(node_def.id in in_use_ids)) { if (!(node_def.id in in_use_ids)) {
in_use_ids[node_def.id] = true; in_use_ids[node_def.id] = true;
in_use.push(node_def); in_use.push(node_def);
} }
} }
initializations.add(node.name.name, node); initializations.add(node_def.id, node);
return true; // don't go in nested scopes return true; // don't go in nested scopes
} }
if (node instanceof AST_SymbolFunarg && scope === self) { if (node instanceof AST_SymbolFunarg && scope === self) {
@@ -2671,7 +2667,7 @@ merge(Compressor.prototype, {
} }
} }
if (def.value) { if (def.value) {
initializations.add(def.name.name, def.value); initializations.add(node_def.id, def.value);
if (def.value.has_side_effects(compressor)) { if (def.value.has_side_effects(compressor)) {
def.value.walk(tw); def.value.walk(tw);
} }
@@ -2686,13 +2682,10 @@ merge(Compressor.prototype, {
// initialization code to figure out if it uses other // initialization code to figure out if it uses other
// symbols (that may not be in_use). // symbols (that may not be in_use).
tw = new TreeWalker(scan_ref_scoped); tw = new TreeWalker(scan_ref_scoped);
for (var i = 0; i < in_use.length; ++i) { for (var i = 0; i < in_use.length; i++) {
in_use[i].orig.forEach(function(decl){ var init = initializations.get(in_use[i].id);
// undeclared globals will be instanceof AST_SymbolRef if (init) init.forEach(function(init) {
var init = initializations.get(decl.name); init.walk(tw);
if (init) init.forEach(function(init){
init.walk(tw);
});
}); });
} }
// pass 3: we should drop declarations not in_use // pass 3: we should drop declarations not in_use
@@ -3685,8 +3678,9 @@ merge(Compressor.prototype, {
if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) { if (compressor.option("reduce_vars") && fn instanceof AST_SymbolRef) {
fn = fn.fixed_value(); fn = fn.fixed_value();
} }
var is_func = fn instanceof AST_Lambda;
if (compressor.option("unused") if (compressor.option("unused")
&& fn instanceof AST_Function && is_func
&& !fn.uses_arguments && !fn.uses_arguments
&& !fn.uses_eval) { && !fn.uses_eval) {
var pos = 0, last = 0; var pos = 0, last = 0;
@@ -3855,7 +3849,7 @@ merge(Compressor.prototype, {
if (func instanceof AST_SymbolRef) { if (func instanceof AST_SymbolRef) {
func = func.fixed_value(); func = func.fixed_value();
} }
if (func instanceof AST_Function && !func.contains_this()) { if (func instanceof AST_Lambda && !func.contains_this()) {
return make_sequence(this, [ return make_sequence(this, [
self.args[0], self.args[0],
make_node(AST_Call, self, { make_node(AST_Call, self, {
@@ -3925,7 +3919,7 @@ merge(Compressor.prototype, {
} }
} }
} }
var stat = fn instanceof AST_Function && fn.body[0]; var stat = is_func && fn.body[0];
if (compressor.option("inline") && stat instanceof AST_Return) { if (compressor.option("inline") && stat instanceof AST_Return) {
var value = stat.value; var value = stat.value;
if (!value || value.is_constant_expression()) { if (!value || value.is_constant_expression()) {
@@ -3933,7 +3927,7 @@ merge(Compressor.prototype, {
return make_sequence(self, args).optimize(compressor); return make_sequence(self, args).optimize(compressor);
} }
} }
if (fn instanceof AST_Function) { if (is_func) {
var def, value, scope, level = -1; var def, value, scope, level = -1;
if (compressor.option("inline") if (compressor.option("inline")
&& !fn.uses_arguments && !fn.uses_arguments
@@ -3981,23 +3975,30 @@ merge(Compressor.prototype, {
return self; return self;
function can_flatten_args(fn) { function can_flatten_args(fn) {
var catches = Object.create(null); var catches = Object.create(null), defs;
do { do {
scope = compressor.parent(++level); scope = compressor.parent(++level);
if (scope instanceof AST_SymbolRef) { if (scope instanceof AST_Catch) {
scope = scope.fixed_value();
} else if (scope instanceof AST_Catch) {
catches[scope.argname.name] = true; catches[scope.argname.name] = true;
} else if (scope instanceof AST_IterationStatement) {
defs = [];
} else if (scope instanceof AST_SymbolRef) {
if (scope.fixed_value() instanceof AST_Scope) return false;
} }
} while (!(scope instanceof AST_Scope)); } while (!(scope instanceof AST_Scope));
var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel); var safe_to_inject = compressor.toplevel.vars || !(scope instanceof AST_Toplevel);
return all(fn.argnames, function(arg) { for (var i = 0, len = fn.argnames.length; i < len; i++) {
return arg.__unused var arg = fn.argnames[i];
|| safe_to_inject if (arg.__unused) continue;
&& !catches[arg.name] if (!safe_to_inject
&& !identifier_atom(arg.name) || catches[arg.name]
&& !scope.var_names()[arg.name]; || identifier_atom(arg.name)
}); || scope.var_names()[arg.name]) {
return false;
}
if (defs) defs.push(arg.definition());
}
return !defs || defs.length == 0 || !is_reachable(stat, defs);
} }
function flatten_args(fn) { function flatten_args(fn) {
@@ -4670,20 +4671,18 @@ merge(Compressor.prototype, {
&& is_lhs(self, compressor.parent()) !== self) { && is_lhs(self, compressor.parent()) !== self) {
var d = self.definition(); var d = self.definition();
var fixed = self.fixed_value(); var fixed = self.fixed_value();
if (fixed instanceof AST_Defun) { var single_use = d.single_use;
d.fixed = fixed = make_node(AST_Function, fixed, fixed); if (single_use && fixed instanceof AST_Lambda) {
}
if (d.single_use && fixed instanceof AST_Function) {
if (d.scope !== self.scope if (d.scope !== self.scope
&& (!compressor.option("reduce_funcs") && (!compressor.option("reduce_funcs")
|| d.escaped == 1 || d.escaped == 1
|| fixed.inlined)) { || fixed.inlined)) {
d.single_use = false; single_use = false;
} else if (recursive_ref(compressor, d)) { } else if (recursive_ref(compressor, d)) {
d.single_use = false; single_use = false;
} else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) { } else if (d.scope !== self.scope || d.orig[0] instanceof AST_SymbolFunarg) {
d.single_use = fixed.is_constant_expression(self.scope); single_use = fixed.is_constant_expression(self.scope);
if (d.single_use == "f") { if (single_use == "f") {
var scope = self.scope; var scope = self.scope;
do { do {
if (scope instanceof AST_Defun || scope instanceof AST_Function) { if (scope instanceof AST_Defun || scope instanceof AST_Function) {
@@ -4693,9 +4692,33 @@ merge(Compressor.prototype, {
} }
} }
} }
if (d.single_use && fixed) { if (single_use && fixed) {
var value = fixed.optimize(compressor); if (fixed instanceof AST_Defun) {
return value === fixed ? fixed.clone(true) : value; fixed = make_node(AST_Function, fixed, fixed);
}
var value;
if (d.recursive_refs > 0 && fixed.name instanceof AST_SymbolDefun) {
value = fixed.clone(true);
var defun_def = value.name.definition();
var lambda_def = value.variables.get(value.name.name);
var name = lambda_def && lambda_def.orig[0];
if (!(name instanceof AST_SymbolLambda)) {
name = make_node(AST_SymbolLambda, value.name, value.name);
name.scope = value;
value.name = name;
lambda_def = value.def_function(name);
}
value.walk(new TreeWalker(function(node) {
if (node instanceof AST_SymbolRef && node.definition() === defun_def) {
node.thedef = lambda_def;
lambda_def.references.push(node);
}
}));
} else {
value = fixed.optimize(compressor);
if (value === fixed) value = fixed.clone(true);
}
return value;
} }
if (fixed && d.should_replace === undefined) { if (fixed && d.should_replace === undefined) {
var init; var init;
@@ -4815,18 +4838,42 @@ merge(Compressor.prototype, {
return self; return self;
}); });
function is_reachable(self, defs) {
var reachable = false;
var find_ref = new TreeWalker(function(node) {
if (reachable) return true;
if (node instanceof AST_SymbolRef && member(node.definition(), defs)) {
return reachable = true;
}
});
var scan_scope = new TreeWalker(function(node) {
if (reachable) return true;
if (node instanceof AST_Scope && node !== self) {
var parent = scan_scope.parent();
if (!(parent instanceof AST_Call && parent.expression === node)) {
node.walk(find_ref);
}
return true;
}
});
self.walk(scan_scope);
return reachable;
}
var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ]; var ASSIGN_OPS = [ '+', '-', '/', '*', '%', '>>', '<<', '>>>', '|', '^', '&' ];
var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ]; var ASSIGN_OPS_COMMUTATIVE = [ '*', '|', '^', '&' ];
OPT(AST_Assign, function(self, compressor){ OPT(AST_Assign, function(self, compressor){
var def;
if (compressor.option("dead_code") if (compressor.option("dead_code")
&& self.left instanceof AST_SymbolRef && self.left instanceof AST_SymbolRef
&& self.left.definition().scope === compressor.find_parent(AST_Lambda)) { && (def = self.left.definition()).scope === compressor.find_parent(AST_Lambda)) {
var level = 0, node, parent = self; var level = 0, node, parent = self;
do { do {
node = parent; node = parent;
parent = compressor.parent(level++); parent = compressor.parent(level++);
if (parent instanceof AST_Exit) { if (parent instanceof AST_Exit) {
if (in_try(level, parent instanceof AST_Throw)) break; if (in_try(level, parent instanceof AST_Throw)) break;
if (is_reachable(def.scope, [ def ])) break;
if (self.operator == "=") return self.right; if (self.operator == "=") return self.right;
return make_node(AST_Binary, self, { return make_node(AST_Binary, self, {
operator: self.operator.slice(0, -1), operator: self.operator.slice(0, -1),

View File

@@ -671,9 +671,7 @@ function OutputStream(options) {
} }
}; };
PARENS(AST_Node, function(){ PARENS(AST_Node, return_false);
return false;
});
// a function expression needs parens around it when it's provably // a function expression needs parens around it when it's provably
// the first token to appear in a statement. // the first token to appear in a statement.
@@ -699,9 +697,7 @@ function OutputStream(options) {
// same goes for an object literal, because otherwise it would be // same goes for an object literal, because otherwise it would be
// interpreted as a block of code. // interpreted as a block of code.
PARENS(AST_Object, function(output){ PARENS(AST_Object, first_in_statement);
return first_in_statement(output);
});
PARENS(AST_Unary, function(output){ PARENS(AST_Unary, function(output){
var p = output.parent(); var p = output.parent();

View File

@@ -307,7 +307,9 @@ AST_Scope.DEFMETHOD("find_variable", function(name){
}); });
AST_Scope.DEFMETHOD("def_function", function(symbol){ AST_Scope.DEFMETHOD("def_function", function(symbol){
this.functions.set(symbol.name, this.def_variable(symbol)); var def = this.def_variable(symbol);
this.functions.set(symbol.name, def);
return def;
}); });
AST_Scope.DEFMETHOD("def_variable", function(symbol){ AST_Scope.DEFMETHOD("def_variable", function(symbol){

View File

@@ -4,7 +4,7 @@
"homepage": "http://lisperator.net/uglifyjs", "homepage": "http://lisperator.net/uglifyjs",
"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.3.2", "version": "3.3.4",
"engines": { "engines": {
"node": ">=0.8.0" "node": ">=0.8.0"
}, },

View File

@@ -3878,10 +3878,9 @@ recursive_function_replacement: {
console.log(f(c)); console.log(f(c));
} }
expect: { expect: {
function f(n) { console.log(function n(o) {
return x(y(f(n))); return x(y(n(o)));
} }(c));
console.log(f(c));
} }
} }

View File

@@ -828,3 +828,56 @@ issue_2597: {
} }
expect_stdout: "PASS" expect_stdout: "PASS"
} }
issue_2666: {
options = {
dead_code: true,
}
input: {
function f(a) {
return a = {
p: function() {
return a;
}
};
}
console.log(typeof f().p());
}
expect: {
function f(a) {
return a = {
p: function() {
return a;
}
};
}
console.log(typeof f().p());
}
expect_stdout: "object"
}
issue_2692: {
options = {
dead_code: true,
reduce_vars: false,
}
input: {
function f(a) {
return a = g;
function g() {
return a;
}
}
console.log(typeof f()());
}
expect: {
function f(a) {
return a = g;
function g() {
return a;
}
}
console.log(typeof f()());
}
expect_stdout: "function"
}

View File

@@ -1413,3 +1413,112 @@ issue_2516_2: {
Baz(2); Baz(2);
} }
} }
defun_lambda_same_name: {
options = {
toplevel: true,
unused: true,
}
input: {
function f(n) {
return n ? n * f(n - 1) : 1;
}
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect: {
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect_stdout: "120"
}
issue_2660_1: {
options = {
reduce_vars: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 2;
function f(b) {
return b && f() || a--;
}
f(1);
console.log(a);
}
expect: {
var a = 2;
(function f(b) {
return b && f() || a--;
})(1);
console.log(a);
}
expect_stdout: "1"
}
issue_2660_2: {
options = {
collapse_vars: true,
reduce_vars: true,
sequences: true,
side_effects: true,
toplevel: true,
unused: true,
}
input: {
var a = 1;
function f(b) {
b && f();
--a, a.toString();
}
f();
console.log(a);
}
expect: {
var a = 1;
(function f(b) {
b && f(),
(--a).toString();
})(),
console.log(a);
}
expect_stdout: "0"
}
issue_2665: {
options = {
evaluate: true,
inline: true,
keep_fargs: false,
passes: 2,
reduce_funcs: true,
reduce_vars: true,
side_effects: true,
toplevel: true,
typeofs: true,
unused: true,
}
input: {
var a = 1;
function g() {
a-- && g();
}
typeof h == "function" && h();
function h() {
typeof g == "function" && g();
}
console.log(a);
}
expect: {
var a = 1;
!function g() {
a-- && g();
}();
console.log(a);
}
expect_stdout: "-1"
}

View File

@@ -1345,11 +1345,10 @@ issue_2630_3: {
expect: { expect: {
var x = 2, a = 1; var x = 2, a = 1;
(function() { (function() {
function f1(a) { (function f1(a) {
f2(); f2();
--x >= 0 && f1({}); --x >= 0 && f1({});
} })(a++);
f1(a++);
function f2() { function f2() {
a++; a++;
} }
@@ -1424,7 +1423,7 @@ issue_2630_5: {
expect_stdout: "155" expect_stdout: "155"
} }
recursive_inline: { recursive_inline_1: {
options = { options = {
inline: true, inline: true,
reduce_funcs: true, reduce_funcs: true,
@@ -1447,3 +1446,203 @@ recursive_inline: {
} }
expect: {} expect: {}
} }
recursive_inline_2: {
options = {
reduce_vars: true,
toplevel: true,
unused: true,
}
input: {
function f(n) {
return n ? n * f(n - 1) : 1;
}
console.log(f(5));
}
expect: {
console.log(function f(n) {
return n ? n * f(n - 1) : 1;
}(5));
}
expect_stdout: "120"
}
issue_2657: {
options = {
inline: true,
reduce_vars: true,
sequences: true,
unused: true,
}
input: {
"use strict";
console.log(function f() {
return h;
function g(b) {
return b || b();
}
function h(a) {
g(a);
return a;
}
}()(42));
}
expect: {
"use strict";
console.log(function(a) {
return b = a, b || b(), a;
var b;
}(42));
}
expect_stdout: "42"
}
issue_2663_1: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function() {
var i, o = {};
function createFn(j) {
return function() {
console.log(j);
};
}
for (i in { a: 1, b: 2, c: 3 })
o[i] = createFn(i);
for (i in o)
o[i]();
})();
}
expect: {
(function() {
var i, o = {};
function createFn(j) {
return function() {
console.log(j);
};
}
for (i in { a: 1, b: 2, c: 3 })
o[i] = createFn(i);
for (i in o)
o[i]();
})();
}
expect_stdout: [
"a",
"b",
"c",
]
}
issue_2663_2: {
options = {
inline: true,
reduce_vars: true,
side_effects: true,
unused: true,
}
input: {
(function() {
var i;
function fn(j) {
return function() {
console.log(j);
}();
}
for (i in { a: 1, b: 2, c: 3 })
fn(i);
})();
}
expect: {
(function() {
var i;
for (i in { a: 1, b: 2, c: 3 })
j = i, console.log(j);
var j;
})();
}
expect_stdout: [
"a",
"b",
"c",
]
}
issue_2663_3: {
options = {
inline: true,
reduce_vars: true,
unused: true,
}
input: {
(function () {
var outputs = [
{ type: 0, target: null, eventName: "ngSubmit", propName: null },
{ type: 0, target: null, eventName: "submit", propName: null },
{ type: 0, target: null, eventName: "reset", propName: null },
];
function listenToElementOutputs(outputs) {
var handlers = [];
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var handleEventClosure = renderEventHandlerClosure(output.eventName);
handlers.push(handleEventClosure)
}
var target, name;
return handlers;
}
function renderEventHandlerClosure(eventName) {
return function () {
return console.log(eventName);
};
}
listenToElementOutputs(outputs).forEach(function (handler) {
return handler()
});
})();
}
expect: {
(function() {
function renderEventHandlerClosure(eventName) {
return function() {
return console.log(eventName);
};
}
(function(outputs) {
var handlers = [];
for (var i = 0; i < outputs.length; i++) {
var output = outputs[i];
var handleEventClosure = renderEventHandlerClosure(output.eventName);
handlers.push(handleEventClosure);
}
return handlers;
})([ {
type: 0,
target: null,
eventName: "ngSubmit",
propName: null
}, {
type: 0,
target: null,
eventName: "submit",
propName: null
}, {
type: 0,
target: null,
eventName: "reset",
propName: null
} ]).forEach(function(handler) {
return handler();
});
})();
}
expect_stdout: [
"ngSubmit",
"submit",
"reset",
]
}

View File

@@ -611,3 +611,35 @@ issue_2313_6: {
x(); x();
} }
} }
issue_2678: {
options = {
pure_getters: "strict",
side_effects: true,
}
input: {
var a = 1, c = "FAIL";
(function f() {
(a-- && f()).p;
return {
get p() {
c = "PASS";
}
};
})();
console.log(c);
}
expect: {
var a = 1, c = "FAIL";
(function f() {
(a-- && f()).p;
return {
get p() {
c = "PASS";
}
};
})();
console.log(c);
}
expect_stdout: "PASS"
}

View File

@@ -1355,10 +1355,9 @@ defun_inline_1: {
function f() { function f() {
return function(b) { return function(b) {
return b; return b;
}(2) + h(); }(2) + function h() {
function h() {
return h(); return h();
} }();
} }
} }
} }
@@ -1382,12 +1381,11 @@ defun_inline_2: {
} }
expect: { expect: {
function f() { function f() {
function h() {
return h();
}
return function(b) { return function(b) {
return b; return b;
}(2) + h(); }(2) + function h() {
return h();
}();
} }
} }
} }

View File

@@ -329,7 +329,8 @@ function createTopLevelCode() {
rng(2) == 0 rng(2) == 0
? createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0) ? 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), : createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0),
'console.log(null, a, b, c);' // preceding `null` makes for a cleaner output (empty string still shows up etc) // preceding `null` makes for a cleaner output (empty string still shows up etc)
'console.log(null, a, b, c, Infinity, NaN, undefined);'
].join('\n'); ].join('\n');
} }
@@ -635,6 +636,8 @@ function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
case p++: case p++:
case p++: case p++:
return getVarName(); return getVarName();
case p++:
return getVarName() + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++:
return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow); return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
case p++: case p++: