f8812806b4e176f3fa8fb626d1083327c7fd54d0
[UglifyJS.git] / test / ufuzz / index.js
1 // derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
2 "use strict";
3
4 // check both CLI and file modes of nodejs (!). See #1695 for details. and the various settings of uglify.
5 // bin/uglifyjs s.js -c && bin/uglifyjs s.js -c passes=3 && bin/uglifyjs s.js -c passes=3 -m
6 // cat s.js | node && node s.js && bin/uglifyjs s.js -c | node && bin/uglifyjs s.js -c passes=3 | node && bin/uglifyjs s.js -c passes=3 -m | node
7
8 require("../../tools/tty");
9
10 var UglifyJS = require("../..");
11 var randomBytes = require("crypto").randomBytes;
12 var sandbox = require("../sandbox");
13 var reduce_test = require("../reduce");
14
15 var MAX_GENERATED_TOPLEVELS_PER_RUN = 1;
16 var MAX_GENERATION_RECURSION_DEPTH = 12;
17 var INTERVAL_COUNT = 100;
18
19 var STMT_ARG_TO_ID = Object.create(null);
20 var STMTS_TO_USE = [];
21 function STMT_(name) {
22     return STMT_ARG_TO_ID[name] = STMTS_TO_USE.push(STMTS_TO_USE.length) - 1;
23 }
24
25 var STMT_BLOCK = STMT_("block");
26 var STMT_IF_ELSE = STMT_("ifelse");
27 var STMT_DO_WHILE = STMT_("dowhile");
28 var STMT_WHILE = STMT_("while");
29 var STMT_FOR_LOOP = STMT_("forloop");
30 var STMT_FOR_ENUM = STMT_("forenum");
31 var STMT_SEMI = STMT_("semi");
32 var STMT_EXPR = STMT_("expr");
33 var STMT_SWITCH = STMT_("switch");
34 var STMT_VAR = STMT_("var");
35 var STMT_RETURN_ETC = STMT_("stop");
36 var STMT_FUNC_EXPR = STMT_("funcexpr");
37 var STMT_TRY = STMT_("try");
38 var STMT_C = STMT_("c");
39
40 var STMT_FIRST_LEVEL_OVERRIDE = -1;
41 var STMT_SECOND_LEVEL_OVERRIDE = -1;
42 var STMT_COUNT_FROM_GLOBAL = true; // count statement depth from nearest function scope or just global scope?
43
44 var num_iterations = +process.argv[2] || 1/0;
45 var verbose = false; // log every generated test
46 var verbose_interval = false; // log every 100 generated tests
47 var use_strict = false;
48 var catch_redef = require.main === module;
49 var generate_directive = require.main === module;
50 for (var i = 2; i < process.argv.length; ++i) {
51     switch (process.argv[i]) {
52       case "-v":
53         verbose = true;
54         break;
55       case "-V":
56         verbose_interval = true;
57         break;
58       case "-t":
59         MAX_GENERATED_TOPLEVELS_PER_RUN = +process.argv[++i];
60         if (!MAX_GENERATED_TOPLEVELS_PER_RUN) throw new Error("Must generate at least one toplevel per run");
61         break;
62       case "-r":
63         MAX_GENERATION_RECURSION_DEPTH = +process.argv[++i];
64         if (!MAX_GENERATION_RECURSION_DEPTH) throw new Error("Recursion depth must be at least 1");
65         break;
66       case "-s1":
67         var name = process.argv[++i];
68         STMT_FIRST_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
69         if (!(STMT_FIRST_LEVEL_OVERRIDE >= 0)) throw new Error("Unknown statement name; use -? to get a list");
70         break;
71       case "-s2":
72         var name = process.argv[++i];
73         STMT_SECOND_LEVEL_OVERRIDE = STMT_ARG_TO_ID[name];
74         if (!(STMT_SECOND_LEVEL_OVERRIDE >= 0)) throw new Error("Unknown statement name; use -? to get a list");
75         break;
76       case "--no-catch-redef":
77         catch_redef = false;
78         break;
79       case "--no-directive":
80         generate_directive = false;
81         break;
82       case "--use-strict":
83         use_strict = true;
84         break;
85       case "--stmt-depth-from-func":
86         STMT_COUNT_FROM_GLOBAL = false;
87         break;
88       case "--only-stmt":
89         STMTS_TO_USE = process.argv[++i].split(",").map(function(name) {
90           return STMT_ARG_TO_ID[name];
91         });
92         break;
93       case "--without-stmt":
94         // meh. it runs once it's fine.
95         process.argv[++i].split(",").forEach(function(name) {
96             var omit = STMT_ARG_TO_ID[name];
97             STMTS_TO_USE = STMTS_TO_USE.filter(function(id) {
98               return id !== omit;
99             });
100         });
101         break;
102       case "--help":
103       case "-h":
104       case "-?":
105         println("** UglifyJS fuzzer help **");
106         println("Valid options (optional):");
107         println("<number>: generate this many cases (if used must be first arg)");
108         println("-v: print every generated test case");
109         println("-V: print every 100th generated test case");
110         println("-t <int>: generate this many toplevels per run (more take longer)");
111         println("-r <int>: maximum recursion depth for generator (higher takes longer)");
112         println("-s1 <statement name>: force the first level statement to be this one (see list below)");
113         println("-s2 <statement name>: force the second level statement to be this one (see list below)");
114         println("--no-catch-redef: do not redefine catch variables");
115         println("--no-directive: do not generate directives");
116         println('--use-strict: generate "use strict"');
117         println("--stmt-depth-from-func: reset statement depth counter at each function, counts from global otherwise");
118         println("--only-stmt <statement names>: a comma delimited white list of statements that may be generated");
119         println("--without-stmt <statement names>: a comma delimited black list of statements never to generate");
120         println("List of accepted statement names: " + Object.keys(STMT_ARG_TO_ID));
121         println("** UglifyJS fuzzer exiting **");
122         return 0;
123       default:
124         // first arg may be a number.
125         if (i > 2 || !parseInt(process.argv[i], 10)) throw new Error("Unknown argument[" + process.argv[i] + "]; see -h for help");
126     }
127 }
128
129 var SUPPORT = function(matrix) {
130     for (var name in matrix) {
131         matrix[name] = typeof sandbox.run_code(matrix[name]) == "string";
132     }
133     return matrix;
134 }({
135     arrow: "a => 0;",
136     async: "async function f(){}",
137     async_generator: "async function* f(){}",
138     bigint: "42n",
139     catch_omit_var: "try {} catch {}",
140     class: "class C { f() {} }",
141     class_field: "class C { p = 0; }",
142     class_private: "class C { #f() {} }",
143     computed_key: "({[0]: 0});",
144     const_block: "var a; { const a = 0; }",
145     default_value: "[ a = 0 ] = [];",
146     destructuring: "[] = [];",
147     exponentiation: "0 ** 0",
148     for_await_of: "async function f(a) { for await (a of []); }",
149     for_of: "for (var a of []);",
150     generator: "function* f(){}",
151     let: "let a;",
152     logical_assignment: "[].p ??= 0;",
153     new_target: "function f() { new.target; }",
154     nullish: "0 ?? 0",
155     optional_chaining: "0?.p",
156     rest: "var [...a] = [];",
157     rest_object: "var {...a} = {};",
158     spread: "[...[]];",
159     spread_object: "({...0});",
160     template: "``",
161     trailing_comma: "function f(a,) {}",
162 });
163 if (SUPPORT.exponentiation && sandbox.run_code("console.log(10 ** 100 === Math.pow(10, 100));") !== "true\n") {
164     SUPPORT.exponentiation = false;
165 }
166
167 var VALUES = [
168     '"a"',
169     '"b"',
170     '"c"',
171     '""',
172     "true",
173     "false",
174     " /[a2][^e]+$/ ",
175     "(-1)",
176     "(-2)",
177     "(-3)",
178     "(-4)",
179     "(-5)",
180     "0",
181     "1",
182     "2",
183     "3",
184     "4",
185     "5",
186     "22",
187     "(-0)", // 0/-0 !== 0
188     "23..toString()",
189     "24 .toString()",
190     "25. ",
191     "0x26.toString()",
192     "NaN",
193     "null",
194     "Infinity",
195     "undefined",
196     "[]",
197     "[,0][1]", // an array with elisions... but this is always false
198     "([,0].length === 2)", // an array with elisions... this is always true
199     "({})", // wrapped the object causes too many syntax errors in statements
200     '"foo"',
201     '"bar"',
202     '"undefined"',
203     '"object"',
204     '"number"',
205     '"function"',
206     "this",
207 ];
208 VALUES = VALUES.concat(VALUES);
209 VALUES = VALUES.concat(VALUES);
210 VALUES = VALUES.concat(VALUES);
211 if (SUPPORT.bigint) VALUES = VALUES.concat([
212     "(!0o644n)",
213     "([3n][0] > 2)",
214     "(-42n).toString()",
215     "Number(0XDEADn << 16n | 0xbeefn)",
216 ]);
217 VALUES = VALUES.concat(VALUES);
218 VALUES = VALUES.concat(VALUES);
219 VALUES = VALUES.concat(VALUES);
220 VALUES = VALUES.concat(VALUES);
221 VALUES.push("import.meta");
222
223 var BINARY_OPS = [
224     " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
225     " - ",
226     "/",
227     "*",
228     "&",
229     "|",
230     "^",
231     "<",
232     "<=",
233     ">",
234     ">=",
235     "==",
236     "===",
237     "!=",
238     "!==",
239     "<<",
240     ">>",
241     ">>>",
242     "%",
243     "&&",
244     "||",
245     "^",
246     ",",
247 ];
248 BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
249 if (SUPPORT.nullish) BINARY_OPS.push("??");
250 BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
251 BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
252 if (SUPPORT.exponentiation) BINARY_OPS.push("**");
253 BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
254 BINARY_OPS = BINARY_OPS.concat(BINARY_OPS);
255 BINARY_OPS.push(" in ");
256
257 var ASSIGNMENTS = [ "=" ];
258 ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
259 ASSIGNMENTS.push("+=");
260 ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
261 ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
262 ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
263 ASSIGNMENTS = ASSIGNMENTS.concat([
264     "-=",
265     "*=",
266     "/=",
267     "%=",
268     "&=",
269     "|=",
270     "^=",
271     "<<=",
272     ">>=",
273     ">>>=",
274 ]);
275 ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
276 if (SUPPORT.exponentiation) ASSIGNMENTS.push("**=");
277 if (SUPPORT.logical_assignment) ASSIGNMENTS = ASSIGNMENTS.concat([
278     "&&=",
279     "||=",
280     "??=",
281 ]);
282
283 var UNARY_SAFE = [
284     "+",
285     "-",
286     "~",
287     "!",
288     "void ",
289     "delete ",
290 ];
291 var UNARY_POSTFIX = [
292     "++",
293     "--",
294 ];
295 var UNARY_PREFIX = UNARY_POSTFIX.concat(UNARY_SAFE);
296
297 var NO_COMMA = true;
298 var COMMA_OK = false;
299 var MAYBE = true;
300 var MANDATORY = false;
301 var CAN_THROW = true;
302 var CANNOT_THROW = false;
303 var CAN_BREAK = true;
304 var CANNOT_BREAK = false;
305 var CAN_CONTINUE = true;
306 var CANNOT_CONTINUE = false;
307 var CAN_RETURN = false;
308 var CANNOT_RETURN = true;
309 var NO_DEFUN = false;
310 var DEFUN_OK = true;
311 var DONT_STORE = true;
312 var NO_CONST = true;
313 var NO_DUPLICATE = true;
314 var NO_LAMBDA = true;
315 var NO_TEMPLATE = true;
316
317 var VAR_NAMES = [
318     "a",
319     "a",
320     "a",
321     "a",
322     "b",
323     "b",
324     "b",
325     "b",
326     "c", // prevent redeclaring this, avoid assigning to this
327     "foo",
328     "foo",
329     "bar",
330     "bar",
331     "undefined",
332     "NaN",
333     "Infinity",
334     "arguments",
335     "async",
336     "await",
337     "let",
338     "yield",
339 ];
340 var INITIAL_NAMES_LEN = VAR_NAMES.length;
341
342 var TYPEOF_OUTCOMES = [
343     "function",
344     "undefined",
345     "string",
346     "number",
347     "object",
348     "boolean",
349     "special",
350     "unknown",
351     "symbol",
352     "crap",
353 ];
354
355 var avoid_vars = [];
356 var block_vars = [ "let" ];
357 var lambda_vars = [];
358 var unique_vars = [];
359 var classes = [];
360 var allow_this = true;
361 var async = false;
362 var has_await = false;
363 var export_default = false;
364 var generator = false;
365 var loops = 0;
366 var funcs = 0;
367 var clazz = 0;
368 var imports = 0;
369 var in_class = 0;
370 var called = Object.create(null);
371 var labels = 10000;
372
373 function rng(max) {
374     var r = randomBytes(2).readUInt16LE(0) / 65536;
375     return Math.floor(max * r);
376 }
377
378 function strictMode() {
379     return use_strict && rng(4) == 0 ? '"use strict";' : "";
380 }
381
382 function appendExport(stmtDepth, allowDefault) {
383     if (SUPPORT.destructuring && stmtDepth == 1 && rng(20) == 0) {
384         if (allowDefault && !export_default && rng(5) == 0) {
385             export_default = true;
386             return "export default ";
387         }
388         return "export ";
389     }
390     return "";
391 }
392
393 function mayDefer(code) {
394     if (SUPPORT.arrow && rng(200) == 0) {
395         has_await = true;
396         return "void setImmediate(() => (" + code + "))";
397     }
398     return code;
399 }
400
401 function createTopLevelCode() {
402     VAR_NAMES.length = INITIAL_NAMES_LEN; // prune any previous names still in the list
403     block_vars.length = 1;
404     lambda_vars.length = 0;
405     unique_vars.length = 0;
406     classes.length = 0;
407     allow_this = true;
408     async = false;
409     has_await = false;
410     export_default = false;
411     generator = false;
412     loops = 0;
413     funcs = 0;
414     clazz = 0;
415     imports = 0;
416     in_class = 0;
417     called = Object.create(null);
418     var s = [
419         strictMode(),
420         appendExport(1) + "var _calls_ = 10, a = 100, b = 10, c = 0;",
421     ];
422     createBlockVariables(MAX_GENERATION_RECURSION_DEPTH, 0, CANNOT_THROW, function(defns) {
423         s.push(defns());
424         if (rng(2)) {
425             s.push(createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0));
426         } else {
427             s.push(createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0));
428         }
429     });
430     // preceding `null` makes for a cleaner output (empty string still shows up etc)
431     var log = "console.log(null, a, b, c, Infinity, NaN, undefined)";
432     if (SUPPORT.arrow && has_await && rng(20) == 0) log = "setImmediate(() => " + log + ")";
433     s.push(log + ";");
434     return s.join("\n");
435 }
436
437 function createFunctions(n, recurmax, allowDefun, canThrow, stmtDepth) {
438     if (--recurmax < 0) { return ";"; }
439     var s = "";
440     while (n-- > 0) {
441         s += createFunction(recurmax, allowDefun, canThrow, stmtDepth) + "\n";
442     }
443     return s;
444 }
445
446 function addTrailingComma(list) {
447     return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list;
448 }
449
450 function createParams(was_async, was_generator, noDuplicate) {
451     var save_async = async;
452     if (!async) async = was_async;
453     var save_generator = generator;
454     if (!generator) generator = was_generator;
455     var len = unique_vars.length;
456     var params = [];
457     for (var n = rng(4); --n >= 0;) {
458         var name = createVarName(MANDATORY);
459         if (noDuplicate || in_class) unique_vars.push(name);
460         params.push(name);
461     }
462     unique_vars.length = len;
463     generator = save_generator;
464     async = save_async;
465     return addTrailingComma(params.join(", "));
466 }
467
468 function createArgs(recurmax, stmtDepth, canThrow, noTemplate) {
469     recurmax--;
470     if (SUPPORT.template && !noTemplate && rng(20) == 0) return createTemplateLiteral(recurmax, stmtDepth, canThrow);
471     var args = [];
472     for (var n = rng(4); --n >= 0;) switch (SUPPORT.spread ? rng(50) : 3) {
473       case 0:
474       case 1:
475         var name = getVarName();
476         if (canThrow && rng(20) == 0) {
477             args.push("..." + name);
478         } else {
479             args.push('...("" + ' + name + ")");
480         }
481         break;
482       case 2:
483         args.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow));
484         break;
485       default:
486         args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
487         break;
488     }
489     return "(" + addTrailingComma(args.join(", ")) + ")";
490 }
491
492 function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async, was_generator) {
493     var avoid = [];
494     var len = unique_vars.length;
495     var pairs = createPairs(recurmax, !nameLenBefore);
496     pairs.has_rest = nameLenBefore && convertToRest(pairs.names);
497     unique_vars.length = len;
498     return pairs;
499
500     function mapShuffled(values, fn) {
501         var declare_only = [];
502         var side_effects = [];
503         values.forEach(function(value, index) {
504             value = fn(value, index);
505             if (/]:|=/.test(value) ? canThrow && rng(20) == 0 : rng(5)) {
506                 declare_only.splice(rng(declare_only.length + 1), 0, value);
507             } else if (canThrow && rng(20) == 0) {
508                 side_effects.splice(rng(side_effects.length + 1), 0, value);
509             } else {
510                 side_effects.push(value);
511             }
512         });
513         return declare_only.concat(side_effects);
514     }
515
516     function convertToRest(names) {
517         var last = names.length - 1;
518         if (last >= 0 && SUPPORT.rest && rng(20) == 0) {
519             var name = names[last];
520             if (name && name.indexOf("=") < 0) {
521                 if (/^[[{]/.test(name)) name = "[ " + name + " ]";
522                 names[last] = "..." + name;
523                 return true;
524             }
525         }
526     }
527
528     function fill(nameFn, valueFn) {
529         var save_async = async;
530         if (was_async != null) {
531             async = false;
532             if (save_async || was_async) addAvoidVar("await");
533         }
534         var save_generator = generator;
535         if (was_generator != null) {
536             generator = false;
537             if (save_generator || was_generator) addAvoidVar("yield");
538         }
539         avoid.forEach(addAvoidVar);
540         var save_vars = nameLenBefore && VAR_NAMES.splice(nameLenBefore);
541         if (nameFn) nameFn();
542         if (was_generator != null) {
543             generator = was_generator;
544             if (save_generator || was_generator) removeAvoidVar("yield");
545         }
546         if (was_async != null) {
547             async = was_async;
548             if (save_async || was_async) removeAvoidVar("await");
549         }
550         if (valueFn) valueFn();
551         if (save_vars) [].push.apply(VAR_NAMES, save_vars);
552         avoid.forEach(removeAvoidVar);
553         generator = save_generator;
554         async = save_async;
555     }
556
557     function createAssignmentValue(recurmax) {
558         return nameLenBefore && rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
559     }
560
561     function createDefaultValue(recurmax, noDefault) {
562         return !noDefault && SUPPORT.default_value && rng(20) == 0 ? " = " +  createAssignmentValue(recurmax) : "";
563     }
564
565     function createKey(recurmax, keys) {
566         var key;
567         do {
568             key = createObjectKey(recurmax, stmtDepth, canThrow);
569         } while (keys.indexOf(key) >= 0);
570         return key;
571     }
572
573     function createName() {
574         unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
575         var save_async = async;
576         if (!async) async = was_async;
577         var save_generator = generator;
578         if (!generator) generator = was_generator;
579         var name = createVarName(MANDATORY);
580         generator = save_generator;
581         async = save_async;
582         unique_vars.length -= 6;
583         avoid.push(name);
584         unique_vars.push(name);
585         return name;
586     }
587
588     function createPairs(recurmax, noDefault) {
589         var names = [], values = [];
590         var m = rng(4), n = rng(4);
591         if (!nameLenBefore) m = Math.max(m, n, 1);
592         for (var i = Math.max(m, n); --i >= 0;) {
593             if (i < m && i < n) {
594                 createDestructured(recurmax, noDefault, names, values);
595             } else if (i < m) {
596                 var name = createName();
597                 fill(function() {
598                     names.unshift(name + createDefaultValue(recurmax, noDefault));
599                 });
600             } else {
601                 fill(null, function() {
602                     values.unshift(createAssignmentValue(recurmax));
603                 });
604             }
605         }
606         return {
607             names: names,
608             values: values,
609         };
610     }
611
612     function createDestructured(recurmax, noDefault, names, values) {
613         switch (rng(20)) {
614           case 0:
615             if (--recurmax < 0) {
616                 names.unshift("[]");
617                 values.unshift('""');
618             } else {
619                 var pairs = createPairs(recurmax);
620                 var default_value;
621                 fill(function() {
622                     default_value = createDefaultValue(recurmax, noDefault);
623                 }, function() {
624                     while (!rng(10)) {
625                         var index = rng(pairs.names.length + 1);
626                         pairs.names.splice(index, 0, "");
627                         if (index < pairs.values.length) {
628                             pairs.values.splice(index, 0, rng(2) ? createAssignmentValue(recurmax) : "");
629                         } else switch (rng(5)) {
630                           case 0:
631                             pairs.values[index] = createAssignmentValue(recurmax);
632                           case 1:
633                             pairs.values.length = index + 1;
634                         }
635                     }
636                     convertToRest(pairs.names);
637                     names.unshift("[ " + pairs.names.join(", ") + " ]" + default_value);
638                     values.unshift("[ " + pairs.values.join(", ") + " ]");
639                 });
640             }
641             break;
642           case 1:
643             if (--recurmax < 0) {
644                 names.unshift("{}");
645                 values.unshift('""');
646             } else {
647                 var pairs = createPairs(recurmax);
648                 var keys = [];
649                 pairs.names.forEach(function(name, index) {
650                     if (/^[[{]/.test(name)) {
651                         var key;
652                         do {
653                             key = KEYS[rng(KEYS.length)];
654                         } while (keys.indexOf(key) >= 0);
655                         keys[index] = key;
656                     }
657                 });
658                 fill(function() {
659                     var last = pairs.names.length - 1, rest;
660                     if (last >= 0 && !(last in keys) && SUPPORT.rest_object && rng(20) == 0) {
661                         rest = pairs.names.pop();
662                         if (!/=/.test(rest)) rest = "..." + rest;
663                     }
664                     var s = mapShuffled(pairs.names, function(name, index) {
665                         if (index in keys) return keys[index] + ": " + name;
666                         return rng(10) == 0 ? name : createKey(recurmax, keys) + ": " + name;
667                     });
668                     if (rest) {
669                         s.push(rest);
670                         s = s.join(", ");
671                     } else {
672                         s = addTrailingComma(s.join(", "));
673                     }
674                     names.unshift("{ " + s + " }" + createDefaultValue(recurmax, noDefault));
675                 }, function() {
676                     values.unshift("{ " + addTrailingComma(mapShuffled(pairs.values, function(value, index) {
677                         var key = index in keys ? keys[index] : createKey(recurmax, keys);
678                         return key + ": " + value;
679                     }).join(", ")) + " }");
680                 });
681             }
682             break;
683           default:
684             var name = createName();
685             fill(function() {
686                 names.unshift(name + createDefaultValue(recurmax, noDefault));
687             }, function() {
688                 values.unshift(createAssignmentValue(recurmax));
689             });
690             break;
691         }
692     }
693 }
694
695 function filterDirective(s) {
696     if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2];
697     return s;
698 }
699
700 function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
701     ++stmtDepth;
702     var block_len = block_vars.length;
703     var class_len = classes.length;
704     var nameLenBefore = VAR_NAMES.length;
705     var consts = [];
706     var lets = [];
707     unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
708     while (!rng(block_vars.length > block_len ? 10 : 100)) {
709         var name = createVarName(MANDATORY);
710         if (SUPPORT.let && rng(2)) {
711             lets.push(name);
712         } else {
713             consts.push(name);
714         }
715         block_vars.push(name);
716     }
717     unique_vars.length -= 6;
718     fn(function() {
719         consts.forEach(addAvoidVar);
720         lets.forEach(addAvoidVar);
721         var s = [];
722         if (SUPPORT.class) while (rng(200) == 0) {
723             var name = "C" + clazz++;
724             classes.push(name);
725             s.push(appendExport(stmtDepth, true) + createClassLiteral(recurmax, stmtDepth, canThrow, name));
726         }
727         if (rng(2)) {
728             s.push(createDefinitions("const", consts), createDefinitions("let", lets));
729         } else {
730             s.push(createDefinitions("let", lets), createDefinitions("const", consts));
731         }
732         s.push("");
733         return s.join("\n");
734     });
735     VAR_NAMES.length = nameLenBefore;
736     classes.length = class_len;
737     block_vars.length = block_len;
738
739     function createDefinitions(type, names) {
740         if (!names.length) return "";
741         var s = appendExport(stmtDepth) + type + " ";
742         switch (SUPPORT.destructuring ? rng(10) : 2) {
743           case 0:
744             while (!rng(10)) names.splice(rng(names.length + 1), 0, "");
745             s += "[ " + names.join(", ") + " ] = [ " + names.map(function() {
746                 return rng(10) ? createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) : "";
747             }).join(", ") + " ];";
748             break;
749           case 1:
750             var keys = [];
751             s += "{ " + names.map(function(name, i) {
752                 var key = createObjectKey(recurmax, stmtDepth, canThrow);
753                 if (!/\[/.test(key)) keys[i] = key;
754                 return key + ": " + name;
755             }).join(", ") + "} = { " + names.map(function(name, i) {
756                 var key = i in keys ? keys[i] : createObjectKey(recurmax, stmtDepth, canThrow);
757                 return key + ": " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
758             }).join(", ") + "};";
759             break;
760           default:
761             s += names.map(function(name) {
762                 if (type == "let" && !rng(10)) {
763                     removeAvoidVar(name);
764                     return name;
765                 }
766                 var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
767                 removeAvoidVar(name);
768                 return name + " = " + value;
769             }).join(", ") + ";";
770             names.length = 0;
771             break;
772         }
773         names.forEach(removeAvoidVar);
774         return s;
775     }
776 }
777
778 function mayCreateBlockVariables(recurmax, stmtDepth, canThrow, fn) {
779     if (SUPPORT.const_block) {
780         createBlockVariables(recurmax, stmtDepth, canThrow, fn);
781     } else {
782         fn(function() {
783             return "";
784         });
785     }
786 }
787
788 function makeFunction(name) {
789     lambda_vars.push(name);
790     if (generator) {
791         name = "function* " + name;
792     } else {
793         name = "function " + name;
794     }
795     if (async) name = "async " + name;
796     return name;
797 }
798
799 function invokeGenerator(was_generator) {
800     if (generator && !was_generator) switch (rng(4)) {
801       case 0:
802         return ".next()";
803       case 1:
804         return ".next().done";
805       case 2:
806         return ".next().value";
807     }
808     return "";
809 }
810
811 function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
812     if (--recurmax < 0) { return ";"; }
813     if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
814     ++stmtDepth;
815     var s = [];
816     var name, args;
817     var nameLenBefore = VAR_NAMES.length;
818     var lambda_len = lambda_vars.length;
819     var save_async = async;
820     var save_generator = generator;
821     async = SUPPORT.async && rng(200) == 0;
822     generator = SUPPORT.generator && rng(50) == 0;
823     if (async && generator && !SUPPORT.async_generator) {
824         if (rng(2)) {
825             async = false;
826         } else {
827             generator = false;
828         }
829     }
830     createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
831         if (allowDefun || rng(5) > 0) {
832             name = "f" + funcs++;
833         } else {
834             unique_vars.push("a", "b", "c");
835             name = createVarName(MANDATORY, !allowDefun);
836             unique_vars.length -= 3;
837         }
838         var params;
839         if (SUPPORT.destructuring && (!allowDefun || !(name in called)) && rng(2)) {
840             called[name] = false;
841             var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator);
842             params = pairs.names.join(", ");
843             if (!pairs.has_rest) params = addTrailingComma(params);
844             args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
845         } else {
846             params = createParams(save_async, save_generator);
847         }
848         s.push(makeFunction(name) + "(" + params + "){", strictMode());
849         s.push(defns());
850         if (rng(5) === 0) {
851             // functions with functions. lower the recursion to prevent a mess.
852             s.push(createFunctions(rng(5) + 1, Math.ceil(recurmax * 0.7), DEFUN_OK, canThrow, stmtDepth));
853         } else {
854             // functions with statements
855             s.push(_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
856         }
857         s.push("}", "");
858         s = filterDirective(s).join("\n");
859     });
860     var call_next = invokeGenerator(save_generator);
861     generator = save_generator;
862     async = save_async;
863     lambda_vars.length = lambda_len;
864     VAR_NAMES.length = nameLenBefore;
865
866     if (allowDefun) s = appendExport(stmtDepth, true) + s;
867     if (!allowDefun) {
868         // avoid "function statements" (decl inside statements)
869         s = appendExport(stmtDepth) + "var " + createVarName(MANDATORY) + " = " + s;
870         s += args || createArgs(recurmax, stmtDepth, canThrow);
871         s += call_next;
872     } else if (!(name in called) || args || rng(3)) {
873         s += appendExport(stmtDepth) + "var " + createVarName(MANDATORY) + " = " + name;
874         s += args || createArgs(recurmax, stmtDepth, canThrow);
875         s += call_next;
876     }
877
878     return s + ";";
879 }
880
881 function _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
882     if (--recurmax < 0) { return ";"; }
883     var s = "";
884     while (--n > 0) {
885         s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "\n";
886     }
887     return s;
888 }
889
890 function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
891     var s = "";
892     mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
893         s += defns() + "\n";
894         s += _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
895     });
896     return s;
897 }
898
899 function enableLoopControl(flag, defaultValue) {
900     return Array.isArray(flag) && flag.indexOf("") < 0 ? flag.concat("") : flag || defaultValue;
901 }
902
903 function createLabel(canBreak, canContinue) {
904     var label;
905     if (rng(10) < 3) {
906         label = ++labels;
907         if (Array.isArray(canBreak)) {
908             canBreak = canBreak.slice();
909         } else {
910             canBreak = canBreak ? [ "" ] : [];
911         }
912         canBreak.push(label);
913         if (Array.isArray(canContinue)) {
914             canContinue = canContinue.slice();
915         } else {
916             canContinue = canContinue ? [ "" ] : [];
917         }
918         canContinue.push(label);
919     }
920     return {
921         break: canBreak,
922         continue: canContinue,
923         target: label ? "L" + label + ": " : ""
924     };
925 }
926
927 function getLabel(label) {
928     if (!Array.isArray(label)) return "";
929     label = label[rng(label.length)];
930     return label && " L" + label;
931 }
932
933 function declareVarName(name, no_var) {
934     if (!SUPPORT.let || !no_var && rng(10)) return "var ";
935     block_vars.push(name);
936     return rng(2) ? "let " : "const ";
937 }
938
939 function createImportAlias() {
940     if (rng(10)) return "alias" + imports++;
941     unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
942     var name = createVarName(MANDATORY);
943     block_vars.push(name);
944     unique_vars.length -= 6;
945     return name;
946 }
947
948 function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) {
949     ++stmtDepth;
950     var loop = ++loops;
951     if (--recurmax < 0) {
952         return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";";
953     }
954
955     // allow to forcefully generate certain structures at first or second recursion level
956     if (target === undefined) {
957         if (stmtDepth === 1 && STMT_FIRST_LEVEL_OVERRIDE >= 0) target = STMT_FIRST_LEVEL_OVERRIDE;
958         else if (stmtDepth === 2 && STMT_SECOND_LEVEL_OVERRIDE >= 0) target = STMT_SECOND_LEVEL_OVERRIDE;
959         else target = STMTS_TO_USE[rng(STMTS_TO_USE.length)];
960     }
961
962     switch (target) {
963       case STMT_BLOCK:
964         var label = createLabel(canBreak);
965         return label.target + "{" + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + "}";
966       case STMT_IF_ELSE:
967         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) : "");
968       case STMT_DO_WHILE:
969         var label = createLabel(canBreak, canContinue);
970         canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
971         canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
972         return "{var brake" + loop + " = 5; " + label.target + "do {" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "} while (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && --brake" + loop + " > 0);}";
973       case STMT_WHILE:
974         var label = createLabel(canBreak, canContinue);
975         canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
976         canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
977         return "{var brake" + loop + " = 5; " + label.target + "while (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && --brake" + loop + " > 0)" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
978       case STMT_FOR_LOOP:
979         var label = createLabel(canBreak, canContinue);
980         canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
981         canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
982         return label.target + "for (var brake" + loop + " = 5; " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " && brake" + loop + " > 0; --brake" + loop + ")" + createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
983       case STMT_FOR_ENUM:
984         var block_len = block_vars.length;
985         var nameLenBefore = VAR_NAMES.length;
986         var label = createLabel(canBreak, canContinue);
987         canBreak = label.break || enableLoopControl(canBreak, CAN_BREAK);
988         canContinue = label.continue || enableLoopControl(canContinue, CAN_CONTINUE);
989         var of = SUPPORT.for_of && rng(20) == 0;
990         var key;
991         if (rng(10)) {
992             key = "key" + loop;
993         } else if (bug_for_of_async && of) {
994             addAvoidVar("async");
995             key = getVarName(NO_CONST);
996             removeAvoidVar("async");
997         } else {
998             key = getVarName(NO_CONST);
999         }
1000         var init = "";
1001         if (!/^key/.test(key)) {
1002             if (!(of && bug_for_of_var) && rng(10) == 0) init = "var ";
1003         } else {
1004             init = declareVarName(key, of && bug_for_of_var);
1005         }
1006         if (!SUPPORT.destructuring || of && !(canThrow && rng(20) == 0) || rng(10)) {
1007             init += key;
1008         } else if (rng(5)) {
1009             init += "[ " + key + " ]";
1010         } else {
1011             init += "{ length: " + key + " }";
1012         }
1013         var s = "var expr" + loop + " = ";
1014         if (of) {
1015             var await = SUPPORT.for_await_of && async && rng(20) == 0;
1016             if (SUPPORT.generator && rng(20) == 0) {
1017                 var gen = getVarName();
1018                 if (canThrow && rng(20) == 0) {
1019                     s += gen + "; ";
1020                 } else {
1021                     s += gen + " && typeof " + gen + "[Symbol.";
1022                     s += await ? "asyncIterator" : "iterator";
1023                     s += '] == "function" ? ' + gen + " : " + createArrayLiteral(recurmax, stmtDepth, canThrow) + "; ";
1024                 }
1025             } else if (rng(5)) {
1026                 s += createArrayLiteral(recurmax, stmtDepth, canThrow) + "; ";
1027             } else if (canThrow && rng(20) == 0) {
1028                 s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ";
1029             } else {
1030                 s += '"" + (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "); ";
1031             }
1032             s += label.target + " for ";
1033             if (await) {
1034                 s += "await ";
1035                 has_await = true;
1036             }
1037             s += "(" + init + " of expr" + loop + ") {";
1038         } else {
1039             s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ";
1040             s += label.target + " for (" + init + " in expr" + loop + ") {";
1041         }
1042         if (/^key/.test(key)) VAR_NAMES.push(key);
1043         if (rng(3)) {
1044             s += "c = 1 + c; ";
1045             unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
1046             var name;
1047             do {
1048                 name = createVarName(MANDATORY);
1049             } while (name == key);
1050             unique_vars.length -= 6;
1051             s += declareVarName(name) + name + " = expr" + loop + "[" + key + "]; ";
1052         }
1053         s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
1054         VAR_NAMES.length = nameLenBefore;
1055         block_vars.length = block_len;
1056         return "{" + s + "}";
1057       case STMT_SEMI:
1058         return use_strict && rng(20) === 0 ? '"use strict";' : ";";
1059       case STMT_EXPR:
1060           if (SUPPORT.destructuring && stmtDepth == 1 && !export_default && rng(20) == 0) {
1061               export_default = true;
1062               return "export default " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";";
1063           }
1064         return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";";
1065       case STMT_SWITCH:
1066         // note: case args are actual expressions
1067         // note: default does not _need_ to be last
1068         return "switch (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ") { " + createSwitchParts(recurmax, 4, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
1069       case STMT_VAR:
1070         if (SUPPORT.destructuring && stmtDepth == 1 && rng(5) == 0) {
1071             var s = rng(2) ? " " + createImportAlias() : "";
1072             if (rng(10)) {
1073                 if (s) s += ",";
1074                 if (rng(2)) {
1075                     s += " * as " + createImportAlias();
1076                 } else {
1077                     var names = [];
1078                     for (var i = rng(4); --i >= 0;) {
1079                         var name = createImportAlias();
1080                         names.push(rng(2) ? getDotKey() + " as " + name : name);
1081                     }
1082                     s += " { " + names.join(", ") + " }";
1083                 }
1084             }
1085             if (s) s += " from";
1086             return "import" + s + ' "path/to/module.js";';
1087         } else if (SUPPORT.destructuring && rng(20) == 0) {
1088             var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow);
1089             return appendExport(stmtDepth) + "var " + pairs.names.map(function(name, index) {
1090                 return index in pairs.values ? name + " = " + pairs.values[index] : name;
1091             }).join(", ") + ";";
1092         } else switch (rng(3)) {
1093           case 0:
1094             unique_vars.push("c");
1095             var name = createVarName(MANDATORY);
1096             unique_vars.pop();
1097             return appendExport(stmtDepth) + "var " + name + ";";
1098           case 1:
1099             // initializer can only have one expression
1100             unique_vars.push("c");
1101             var name = createVarName(MANDATORY);
1102             unique_vars.pop();
1103             return appendExport(stmtDepth) + "var " + name + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1104           default:
1105             // initializer can only have one expression
1106             unique_vars.push("c");
1107             var n1 = createVarName(MANDATORY);
1108             var n2 = createVarName(MANDATORY);
1109             unique_vars.pop();
1110             return appendExport(stmtDepth) + "var " + n1 + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ", " + n2 + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1111         }
1112       case STMT_RETURN_ETC:
1113         switch (rng(8)) {
1114           case 0:
1115           case 1:
1116           case 2:
1117           case 3:
1118             if (canBreak && rng(5) === 0) return "break" + getLabel(canBreak) + ";";
1119             if (canContinue && rng(5) === 0) return "continue" + getLabel(canContinue) + ";";
1120             if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1121             if (rng(3) == 0) return "/*3*/return;";
1122             return "return " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1123           case 4:
1124             // this is actually more like a parser test, but perhaps it hits some dead code elimination traps
1125             // must wrap in curlies to prevent orphaned `else` statement
1126             // note: you can't `throw` without an expression so don't put a `throw` option in this case
1127             if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1128             return "{ /*2*/ return\n" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "}";
1129           default:
1130             // must wrap in curlies to prevent orphaned `else` statement
1131             if (canThrow && rng(5) === 0) return "{ throw " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "}";
1132             if (cannotReturn) return createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1133             return "{ return " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "}";
1134         }
1135       case STMT_FUNC_EXPR:
1136         // "In non-strict mode code, functions can only be declared at top level, inside a block, or ..."
1137         // (dont both with func decls in `if`; it's only a parser thing because you cant call them without a block)
1138         return "{" + createFunction(recurmax, NO_DEFUN, canThrow, stmtDepth) + "}";
1139       case STMT_TRY:
1140         // catch var could cause some problems
1141         // note: the "blocks" are syntactically mandatory for try/catch/finally
1142         var n = rng(3); // 0=only catch, 1=only finally, 2=catch+finally
1143         var s = "try {" + createStatement(recurmax, n === 1 ? CANNOT_THROW : CAN_THROW, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
1144         if (n !== 1) {
1145             // the catch var should only be accessible in the catch clause...
1146             // we have to do go through some trouble here to prevent leaking it
1147             mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
1148                 var block_len = block_vars.length;
1149                 var nameLenBefore = VAR_NAMES.length;
1150                 var unique_len = unique_vars.length;
1151                 if (SUPPORT.catch_omit_var && !rng(20)) {
1152                     s += " catch { ";
1153                 } else if (canThrow && SUPPORT.destructuring && !rng(20)) {
1154                     unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
1155                     var name = createVarName(MANDATORY);
1156                     block_vars.push(name);
1157                     var message = createVarName(MANDATORY);
1158                     block_vars.push(message);
1159                     unique_vars.length -= 6;
1160                     if (SUPPORT.computed_key && rng(10) == 0) {
1161                         s += " catch ({  message: " + message + ", ";
1162                         addAvoidVar(name);
1163                         s += "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]: " + name;
1164                         removeAvoidVar(name);
1165                         s += " }) { ";
1166                     } else {
1167                         s += " catch ({ name: " + name + ", message: " + message + " }) { ";
1168                     }
1169                 } else {
1170                     var name = createVarName(MANDATORY);
1171                     if (!catch_redef) unique_vars.push(name);
1172                     s += " catch (" + name + ") { ";
1173                 }
1174                 var catches = VAR_NAMES.length - nameLenBefore;
1175                 s += defns() + "\n";
1176                 s += _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
1177                 s += " }";
1178                 // remove catch variables
1179                 block_vars.length = block_len;
1180                 if (catches > 0) VAR_NAMES.splice(nameLenBefore, catches);
1181                 unique_vars.length = unique_len;
1182             });
1183         }
1184         if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
1185         return s;
1186       case STMT_C:
1187         return "c = c + 1;";
1188       default:
1189         throw "no";
1190     }
1191 }
1192
1193 function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
1194     var hadDefault = false;
1195     var s = [ "" ];
1196     canBreak = enableLoopControl(canBreak, CAN_BREAK);
1197     while (n-- > 0) {
1198         //hadDefault = n > 0; // disables weird `default` clause positioning (use when handling destabilizes)
1199         if (hadDefault || rng(5) > 0) {
1200             s.push(
1201                 "case " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ":",
1202                 _createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
1203                 rng(10) > 0 ? " break;" : "/* fall-through */",
1204                 ""
1205             );
1206         } else {
1207             hadDefault = true;
1208             s.push(
1209                 "default:",
1210                 _createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
1211                 ""
1212             );
1213         }
1214     }
1215     return s.join("\n");
1216 }
1217
1218 function createExpression(recurmax, noComma, stmtDepth, canThrow) {
1219     if (--recurmax < 0) {
1220         return "(c = 1 + c, " + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")"; // note: should return a simple non-recursing expression value!
1221     }
1222     // since `a` and `b` are our canaries we want them more frequently than other expressions (1/3rd chance of a canary)
1223     switch (rng(6)) {
1224       case 0:
1225         return "(a++ + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))";
1226       case 1:
1227         return "((--b) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))";
1228       case 2:
1229         return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented
1230       default:
1231         var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
1232         if (async && rng(50) == 0) {
1233             has_await = true;
1234             return "(await" + expr + ")";
1235         }
1236         if (generator && rng(50) == 0) return "(yield" + (canThrow && rng(20) == 0 ? "*" : "") + expr + ")";
1237         return expr;
1238     }
1239 }
1240
1241 function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
1242     var p = 0;
1243     switch (rng(_createExpression.N)) {
1244       case p++:
1245         if (generator && rng(50) == 0) return "yield";
1246       case p++:
1247         return createUnaryPrefix() + (rng(2) ? "a" : "b");
1248       case p++:
1249       case p++:
1250         return (rng(2) ? "a" : "b") + createUnaryPostfix();
1251       case p++:
1252       case p++:
1253         // parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
1254         return "b " + createAssignment() + " a";
1255       case p++:
1256       case p++:
1257         return rng(2) + " === 1 ? a : b";
1258       case p++:
1259         if (SUPPORT.template && rng(20) == 0) {
1260             var tmpl = createTemplateLiteral(recurmax, stmtDepth, canThrow);
1261             if (rng(10) == 0) tmpl = "String.raw" + tmpl;
1262             return tmpl;
1263         }
1264       case p++:
1265         return createValue();
1266       case p++:
1267         if (SUPPORT.destructuring && rng(20) == 0) {
1268             var name = "alias" + rng(imports + 2);
1269             return canThrow && rng(20) == 0 ? name : "typeof " + name + ' != "undefined" && ' + name;
1270         }
1271       case p++:
1272         return getVarName();
1273       case p++:
1274         switch (SUPPORT.destructuring ? rng(20) : 2) {
1275           case 0:
1276             return [
1277                 "[ ",
1278                 new Array(rng(3)).join(),
1279                 getVarName(NO_CONST, NO_LAMBDA),
1280                 new Array(rng(3)).join(),
1281                 " ] = ",
1282                 createArrayLiteral(recurmax, stmtDepth, canThrow),
1283             ].join("");
1284           case 1:
1285             return [
1286                 "{ ",
1287                 rng(2) ? "" : createObjectKey(recurmax, stmtDepth, canThrow) + ": ",
1288                 getVarName(NO_CONST, NO_LAMBDA),
1289                 " } = ",
1290                 createExpression(recurmax, COMMA_OK, stmtDepth, canThrow),
1291                 " || {}",
1292             ].join("");
1293           default:
1294             return getVarName(NO_CONST, NO_LAMBDA) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
1295         }
1296       case p++:
1297         return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
1298       case p++:
1299         return createExpression(recurmax, noComma, stmtDepth, canThrow) + " ? " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + " : " + createExpression(recurmax, noComma, stmtDepth, canThrow);
1300       case p++:
1301       case p++:
1302         var nameLenBefore = VAR_NAMES.length;
1303         var lambda_len = lambda_vars.length;
1304         var save_async = async;
1305         var save_generator = generator;
1306         async = SUPPORT.async && rng(200) == 0;
1307         generator = SUPPORT.generator && rng(50) == 0;
1308         if (async && generator && !SUPPORT.async_generator) {
1309             if (rng(2)) {
1310                 async = false;
1311             } else {
1312                 generator = false;
1313             }
1314         }
1315         unique_vars.push("a", "b", "c");
1316         var name = createVarName(MAYBE); // note: this name is only accessible from _within_ the function. and immutable at that.
1317         unique_vars.length -= 3;
1318         var s = [];
1319         switch (rng(5)) {
1320           case 0:
1321             if (SUPPORT.arrow && !name && !generator && rng(2)) {
1322                 var args, suffix;
1323                 (rng(2) ? createBlockVariables : function() {
1324                     arguments[3]();
1325                 })(recurmax, stmtDepth, canThrow, function(defns) {
1326                     var params;
1327                     if (SUPPORT.destructuring && rng(2)) {
1328                         var pairs = createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, save_async, save_generator);
1329                         params = pairs.names.join(", ");
1330                         if (!pairs.has_rest) params = addTrailingComma(params);
1331                         args = "(" + addTrailingComma(pairs.values.join(", ")) + ")";
1332                     } else {
1333                         params = createParams(save_async, save_generator, NO_DUPLICATE);
1334                     }
1335                     params = (async ? "async (" : "(") + params + ") => ";
1336                     if (defns) {
1337                         s.push(
1338                             "(" + params + "{",
1339                             strictMode(),
1340                             defns(),
1341                             _createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)
1342                         );
1343                         suffix = "})";
1344                     } else {
1345                         s.push("(" + params);
1346                         switch (rng(10)) {
1347                           case 0:
1348                             if (!in_class) {
1349                                 s.push('(typeof arguments != "undefined" && arguments && arguments[' + rng(3) + "])");
1350                                 break;
1351                             }
1352                           case 1:
1353                             s.push("(this && this." + getDotKey() + ")");
1354                             break;
1355                           default:
1356                             s.push(createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
1357                             break;
1358                         }
1359                         suffix = ")";
1360                     }
1361                 });
1362                 generator = save_generator;
1363                 async = save_async;
1364                 lambda_vars.length = lambda_len;
1365                 VAR_NAMES.length = nameLenBefore;
1366                 if (!args && rng(2)) args = createArgs(recurmax, stmtDepth, canThrow);
1367                 if (args) suffix += args;
1368                 s.push(suffix);
1369             } else {
1370                 s.push(
1371                     "(" + makeFunction(name) + "(){",
1372                     strictMode(),
1373                     createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1374                     rng(2) ? "})" : "})()" + invokeGenerator(save_generator)
1375                 );
1376             }
1377             break;
1378           case 1:
1379             s.push(
1380                 "+" + makeFunction(name) + "(){",
1381                 strictMode(),
1382                 createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1383                 "}()" + invokeGenerator(save_generator)
1384             );
1385             break;
1386           case 2:
1387             s.push(
1388                 "!" + makeFunction(name) + "(){",
1389                 strictMode(),
1390                 createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1391                 "}()" + invokeGenerator(save_generator)
1392             );
1393             break;
1394           case 3:
1395             s.push(
1396                 "void " + makeFunction(name) + "(){",
1397                 strictMode(),
1398                 createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1399                 "}()" + invokeGenerator(save_generator)
1400             );
1401             break;
1402           default:
1403             async = false;
1404             generator = false;
1405             var instantiate = rng(4) ? "new " : "";
1406             createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
1407                 s.push(
1408                     instantiate + makeFunction(name) + "(" + createParams(save_async, save_generator) + "){",
1409                     strictMode()
1410                 );
1411                 var add_new_target = SUPPORT.new_target && VALUES.indexOf("new.target") < 0;
1412                 if (add_new_target) VALUES.push("new.target");
1413                 s.push(defns());
1414                 if (instantiate) for (var i = rng(4); --i >= 0;) {
1415                     s.push((in_class ? "if (this) " : "") + createThisAssignment(recurmax, stmtDepth, canThrow));
1416                 }
1417                 s.push(_createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
1418                 if (add_new_target) VALUES.splice(VALUES.indexOf("new.target"), 1);
1419             });
1420             generator = save_generator;
1421             async = save_async;
1422             lambda_vars.length = lambda_len;
1423             VAR_NAMES.length = nameLenBefore;
1424             s.push(rng(2) ? "}" : "}" + createArgs(recurmax, stmtDepth, canThrow, instantiate));
1425             break;
1426         }
1427         generator = save_generator;
1428         async = save_async;
1429         lambda_vars.length = lambda_len;
1430         VAR_NAMES.length = nameLenBefore;
1431         return filterDirective(s).join("\n");
1432       case p++:
1433       case p++:
1434         return createTypeofExpr(recurmax, stmtDepth, canThrow);
1435       case p++:
1436       case p++:
1437         // more like a parser test but perhaps comment nodes mess up the analysis?
1438         // note: parens not needed for post-fix (since that's the default when ambiguous)
1439         // for prefix ops we need parens to prevent accidental syntax errors.
1440         switch (rng(6)) {
1441           case 0:
1442             return "a/* ignore */++";
1443           case 1:
1444             return "b/* ignore */--";
1445           case 2:
1446             return "++/* ignore */a";
1447           case 3:
1448             return "--/* ignore */b";
1449           case 4:
1450             // only groups that wrap a single variable return a "Reference", so this is still valid.
1451             // may just be a parser edge case that is invisible to uglify...
1452             return "--(b)";
1453           case 5:
1454             // classic 0.3-0.1 case; 1-0.1-0.1-0.1 is not 0.7 :)
1455             return "b + 1 - 0.1 - 0.1 - 0.1";
1456           default:
1457             return "--/* ignore */b";
1458         }
1459       case p++:
1460       case p++:
1461         return createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
1462       case p++:
1463       case p++:
1464         return createUnarySafePrefix() + "(" + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1465       case p++:
1466         return " (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || a || 3).toString() ";
1467       case p++:
1468         return " /[abc4]/.test((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || b || 5).toString()) ";
1469       case p++:
1470         return " /[abc4]/g.exec((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || b || 5).toString()) ";
1471       case p++:
1472         return " (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || " + rng(10) + ").toString()[" +
1473             createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
1474       case p++:
1475         return createArrayLiteral(recurmax, stmtDepth, canThrow);
1476       case p++:
1477         return createObjectLiteral(recurmax, stmtDepth, canThrow);
1478       case p++:
1479         return createArrayLiteral(recurmax, stmtDepth, canThrow) + "[" +
1480             createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
1481       case p++:
1482         return createObjectLiteral(recurmax, stmtDepth, canThrow) + "[" +
1483             createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
1484       case p++:
1485         return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
1486       case p++:
1487         return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
1488       case p++:
1489         return createValue() + " in " + createArrayLiteral(recurmax, stmtDepth, canThrow);
1490       case p++:
1491         return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow);
1492       case p++:
1493         var name = getVarName();
1494         var prop = "[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
1495         if (SUPPORT.optional_chaining && rng(50) == 0) return name + "?." + prop;
1496         if (canThrow && rng(20) == 0) return name + prop;
1497         return name + " && " + name + prop;
1498       case p++:
1499         var name = getVarName();
1500         var prop = getDotKey();
1501         if (SUPPORT.optional_chaining && rng(50) == 0) return name + "?." + prop;
1502         if (canThrow && rng(20) == 0) return name + "." + prop;
1503         return name + " && " + name + "." + prop;
1504       case p++:
1505       case p++:
1506         var name = getVarName();
1507         var fn = name + "." + getDotKey();
1508         var s = "typeof " + fn + ' == "function" && --_calls_ >= 0 && ';
1509         s += rng(5) ? fn : "(" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ", " + fn + ")";
1510         s += createArgs(recurmax, stmtDepth, canThrow);
1511         return mayDefer(canThrow && rng(20) == 0 ? s : name + " && " + s);
1512       case p++:
1513         if (SUPPORT.class) {
1514             if (classes.length && rng(20) == 0) {
1515                 return "--_calls_ >= 0 && new " + classes[rng(classes.length)] + createArgs(recurmax, stmtDepth, canThrow, NO_TEMPLATE);
1516             }
1517             if (rng(200) == 0) {
1518                 var s = "--_calls_ >= 0 && new ";
1519                 var nameLenBefore = VAR_NAMES.length;
1520                 var class_len = classes.length;
1521                 var name;
1522                 if (canThrow && rng(20) == 0) {
1523                     in_class++;
1524                     name = createVarName(MAYBE);
1525                     in_class--;
1526                 } else if (rng(2)) {
1527                     name = "C" + clazz++;
1528                     classes.push(name);
1529                 }
1530                 s += createClassLiteral(recurmax, stmtDepth, canThrow, name);
1531                 classes.length = class_len;
1532                 VAR_NAMES.length = nameLenBefore;
1533                 s += createArgs(recurmax, stmtDepth, canThrow, NO_TEMPLATE);
1534                 return s;
1535             }
1536         }
1537       case p++:
1538       case p++:
1539       case p++:
1540         var name;
1541         do {
1542             name = rng(3) == 0 ? getVarName() : "f" + rng(funcs + 2);
1543         } while (name in called && !called[name]);
1544         called[name] = true;
1545         var args = createArgs(recurmax, stmtDepth, canThrow);
1546         var call = "typeof " + name + ' == "function" && --_calls_ >= 0 && ' + name + args;
1547         if (canThrow) {
1548             if (SUPPORT.optional_chaining && args[0] != "`" && rng(50) == 0) {
1549                 call = "--_calls_ >= 0 && " + name + "?." + args;
1550             } else if (rng(20) == 0) {
1551                 call = "--_calls_ >= 0 && " + name + args;
1552             }
1553         }
1554         return mayDefer(call);
1555     }
1556     _createExpression.N = p;
1557     return _createExpression(recurmax, noComma, stmtDepth, canThrow);
1558 }
1559
1560 function createArrayLiteral(recurmax, stmtDepth, canThrow) {
1561     recurmax--;
1562     var arr = [];
1563     for (var i = rng(6); --i >= 0;) switch (SUPPORT.spread ? rng(50) : 3 + rng(47)) {
1564       case 0:
1565       case 1:
1566         var name = getVarName();
1567         if (canThrow && rng(20) == 0) {
1568             arr.push("..." + name);
1569         } else {
1570             arr.push('...("" + ' + name + ")");
1571         }
1572         break;
1573       case 2:
1574         arr.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow));
1575         break;
1576       case 3:
1577       case 4:
1578         // in rare cases produce an array hole element
1579         arr.push("");
1580         break;
1581       default:
1582         arr.push(createExpression(recurmax, COMMA_OK, stmtDepth, canThrow));
1583         break;
1584     }
1585     return "[" + arr.join(", ") + "]";
1586 }
1587
1588 function createTemplateLiteral(recurmax, stmtDepth, canThrow) {
1589     recurmax--;
1590     var s = [];
1591     addText();
1592     for (var i = rng(6); --i >= 0;) {
1593         s.push("${", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "}");
1594         addText();
1595     }
1596     return "`" + s.join(rng(5) ? "" : "\n") + "`";
1597
1598     function addText() {
1599         while (rng(5) == 0) s.push([
1600             " ",
1601             "$",
1602             "}",
1603             "\\`",
1604             "\\\\",
1605             "tmpl",
1606         ][rng(6)]);
1607     }
1608 }
1609
1610 var SAFE_KEYS = [
1611     "a",
1612     "b",
1613     "c",
1614     "foo",
1615     "NaN",
1616     "null",
1617     "Infinity",
1618     "undefined",
1619     "async",
1620     "done",
1621     "get",
1622     "in",
1623     "length",
1624     "next",
1625     "set",
1626     "static",
1627     "then",
1628     "value",
1629     "var",
1630 ];
1631 var KEYS = [
1632     "''",
1633     '"\t"',
1634     '"-2"',
1635     "0",
1636     "1.5",
1637     "3",
1638 ].concat(SAFE_KEYS);
1639 SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS);
1640 SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS);
1641 SAFE_KEYS = SAFE_KEYS.concat(SAFE_KEYS);
1642 SAFE_KEYS.push("__proto__");
1643
1644 function getDotKey(assign) {
1645     var key;
1646     do {
1647         key = SAFE_KEYS[rng(SAFE_KEYS.length)];
1648     } while (assign && key == "length");
1649     return key;
1650 }
1651
1652 function createObjectKey(recurmax, stmtDepth, canThrow) {
1653     if (SUPPORT.computed_key && rng(10) == 0) {
1654         return "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]";
1655     }
1656     return KEYS[rng(KEYS.length)];
1657 }
1658
1659 function createSuperAssignment(recurmax, stmtDepth, canThrow) {
1660     var s = rng(2) ? "super." + getDotKey() : "super[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
1661     return getVarName(NO_CONST, NO_LAMBDA) + createAssignment() + s + ";";
1662 }
1663
1664 function createThisAssignment(recurmax, stmtDepth, canThrow) {
1665     var s = rng(2) ? "this." + getDotKey(true) : "this[" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
1666     return s + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";";
1667 }
1668
1669 function createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz) {
1670     var nameLenBefore = VAR_NAMES.length;
1671     var save_async = async;
1672     var save_generator = generator;
1673     var s;
1674     var name;
1675     if (internal) {
1676         name = internal;
1677     } else if (isClazz) {
1678         var clazzName = classes.pop();
1679         name = createObjectKey(recurmax, stmtDepth, canThrow);
1680         classes.push(clazzName);
1681     } else {
1682         name = createObjectKey(recurmax, stmtDepth, canThrow);
1683     }
1684     var fn;
1685     switch (internal ? 2 : rng(SUPPORT.computed_key ? 3 : 2)) {
1686       case 0:
1687         async = false;
1688         generator = false;
1689         fn = function(defns) {
1690             s = [
1691                 "get " + name + "(){",
1692                 strictMode(),
1693                 defns(),
1694                 _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1695                 createStatement(recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth, STMT_RETURN_ETC),
1696                 "}",
1697             ];
1698         };
1699         break;
1700       case 1:
1701         var prop;
1702         do {
1703             prop = getDotKey();
1704         } while (name == prop);
1705         async = false;
1706         generator = false;
1707         fn = function(defns) {
1708             s = [
1709                 "set " + name + "(" + createVarName(MANDATORY) + "){",
1710                 strictMode(),
1711                 defns(),
1712                 _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1713                 "this." + prop + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
1714                 "}",
1715             ];
1716         };
1717         break;
1718       default:
1719         if (/^(constructor|super)$/.test(internal)) {
1720             async = false;
1721             generator = false;
1722             name = "constructor";
1723         } else {
1724             async = SUPPORT.async && rng(200) == 0;
1725             generator = SUPPORT.generator && rng(50) == 0;
1726             if (async && generator && !SUPPORT.async_generator) {
1727                 if (rng(2)) {
1728                     async = false;
1729                 } else {
1730                     generator = false;
1731                 }
1732             }
1733         }
1734         fn = function(defns) {
1735             if (generator) name = "*" + name;
1736             if (async) name = "async "+ name;
1737             var save_allow = allow_this;
1738             if (internal == "super") allow_this = false;
1739             s = [
1740                 name + "(" + createParams(save_async, save_generator, NO_DUPLICATE) + "){",
1741                 strictMode(),
1742                 defns(),
1743             ];
1744             s.push(_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, stmtDepth));
1745             if (internal == "super") s.push("super" + createArgs(recurmax, stmtDepth, canThrow, NO_TEMPLATE) + ";");
1746             allow_this = save_allow;
1747             if (/^(constructor|super)$/.test(internal) || rng(10) == 0) for (var i = rng(4); --i >= 0;) {
1748                 s.push(rng(2) ? createSuperAssignment(recurmax, stmtDepth, canThrow) : createThisAssignment(recurmax, stmtDepth, canThrow));
1749             }
1750             s.push(_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), "}");
1751         };
1752         break;
1753     }
1754     createBlockVariables(recurmax, stmtDepth, canThrow, fn);
1755     generator = save_generator;
1756     async = save_async;
1757     VAR_NAMES.length = nameLenBefore;
1758     return filterDirective(s).join("\n");
1759 }
1760
1761 function createObjectLiteral(recurmax, stmtDepth, canThrow) {
1762     recurmax--;
1763     var obj = [ "({" ];
1764     var offset = SUPPORT.spread_object ? 0 : SUPPORT.computed_key ? 2 : 4;
1765     var has_proto = false;
1766     for (var i = rng(6); --i >= 0;) switch (offset + rng(50 - offset)) {
1767       case 0:
1768         obj.push("..." + getVarName() + ",");
1769         break;
1770       case 1:
1771         obj.push("..." + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ",");
1772         break;
1773       case 2:
1774       case 3:
1775         obj.push(getVarName() + ",");
1776         break;
1777       case 4:
1778         obj.push(createObjectFunction(recurmax, stmtDepth, canThrow) + ",");
1779         break;
1780       default:
1781         if (has_proto || rng(200)) {
1782             obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ": " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ",");
1783         } else {
1784             obj.push("__proto__: " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || {},");
1785             has_proto = true;
1786         }
1787         break;
1788     }
1789     obj.push("})");
1790     return obj.join("\n");
1791 }
1792
1793 function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
1794     recurmax--;
1795     var save_async = async;
1796     var save_generator = generator;
1797     in_class++;
1798     var s = "class", constructor = "constructor";
1799     var isClazz = /^C/.test(name);
1800     if (name) s += " " + name;
1801     if (rng(10) == 0) {
1802         constructor = "super";
1803         s += " extends ";
1804         var p = getVarName();
1805         if (canThrow && rng(20) == 0) {
1806             s += p;
1807         } else {
1808             s += "(" + p + " && " + p + ".constructor === Function ? " + p + " : function() {})";
1809         }
1810     }
1811     s += " {\n";
1812     var declared = [];
1813     for (var i = rng(6); --i >= 0;) {
1814         var fixed = false;
1815         if (rng(5) == 0) {
1816             fixed = true;
1817             s += "static ";
1818         }
1819         var internal = null;
1820         if (SUPPORT.class_private && rng(10) == 0) {
1821             do {
1822                 internal = "#" + getDotKey();
1823             } while (declared.indexOf(internal) >= 0);
1824             declared.push(internal);
1825         }
1826         if (SUPPORT.class_field && rng(2)) {
1827             s += internal || createObjectKey(recurmax, stmtDepth, canThrow);
1828             if (rng(5)) {
1829                 async = bug_async_class_await && fixed && 0;
1830                 generator = false;
1831                 s += " = " + createExpression(recurmax, NO_COMMA, stmtDepth, fixed ? canThrow : CANNOT_THROW);
1832                 generator = save_generator;
1833                 async = save_async;
1834             }
1835             s += ";\n";
1836         } else {
1837             if (!fixed && !internal && constructor && rng(10) == 0) {
1838                 internal = constructor;
1839                 constructor = null;
1840             }
1841             s += createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz) + "\n";
1842         }
1843     }
1844     in_class--;
1845     generator = save_generator;
1846     async = save_async;
1847     return s + "}";
1848 }
1849
1850 function createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
1851     recurmax = 3; // note that this generates 2^recurmax expression parts... make sure to cap it
1852     return _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
1853 }
1854 function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
1855     return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow)
1856         + createBinaryOp(noComma, canThrow) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1857 }
1858 function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
1859     // intentionally generate more hardcore ops
1860     if (--recurmax < 0) return createValue();
1861     var assignee, expr;
1862     switch (rng(30)) {
1863       case 0:
1864         return "(c = c + 1, " + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1865       case 1:
1866         return "(" + createUnarySafePrefix() + "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + "))";
1867       case 2:
1868         assignee = getVarName(NO_CONST, NO_LAMBDA);
1869         return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1870       case 3:
1871         assignee = getVarName();
1872         switch (SUPPORT.destructuring ? rng(20) : 2) {
1873           case 0:
1874             expr = [
1875                 "([ ",
1876                 assignee,
1877                 "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
1878                 " ] = [ ",
1879                 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1880                 " ])",
1881             ].join("");
1882             break;
1883           case 1:
1884             var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
1885             var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
1886             expr = [
1887                 "({ ",
1888                 key1, ": ", assignee,
1889                 "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
1890                 " } = { ",
1891                 key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1892                 " })",
1893             ].join("");
1894             break;
1895           default:
1896             expr = [
1897                 "(",
1898                 assignee,
1899                 "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
1900                 createAssignment(),
1901                 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1902                 ")",
1903             ].join("");
1904             break;
1905         }
1906         if (in_class) return "(Object.isExtensible(" + assignee + ") && " + expr + ")";
1907         return canThrow && rng(20) == 0 ? expr : "(" + assignee + " && " + expr + ")";
1908       case 4:
1909         assignee = getVarName();
1910         switch (SUPPORT.destructuring ? rng(20) : 2) {
1911           case 0:
1912             expr = [
1913                 "([ ",
1914                 assignee,
1915                 ".", getDotKey(true),
1916                 " ] = [ ",
1917                 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1918                 " ])",
1919             ].join("");
1920             break;
1921           case 1:
1922             var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
1923             var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
1924             expr = [
1925                 "({ ",
1926                 key1, ": ", assignee,
1927                 ".", getDotKey(true),
1928                 " } = { ",
1929                 key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1930                 " })",
1931             ].join("");
1932             break;
1933           default:
1934             expr = [
1935                 "(",
1936                 assignee,
1937                 ".", getDotKey(true),
1938                 createAssignment(),
1939                 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1940                 ")",
1941             ].join("");
1942             break;
1943         }
1944         if (in_class) return "(Object.isExtensible(" + assignee + ") && " + expr + ")";
1945         return canThrow && rng(20) == 0 ? expr : "(" + assignee + " && " + expr + ")";
1946       default:
1947         return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
1948     }
1949 }
1950
1951 function createTypeofExpr(recurmax, stmtDepth, canThrow) {
1952     switch (rng(8)) {
1953       case 0:
1954         return "(typeof " + createVar() + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1955       case 1:
1956         return "(typeof " + createVar() + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1957       case 2:
1958         return "(typeof " + createVar() + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1959       case 3:
1960         return "(typeof " + createVar() + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1961       case 4:
1962         return "(typeof " + createVar() + ")";
1963       default:
1964         return "(typeof " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")";
1965     }
1966
1967     function createVar() {
1968         var save_async = async;
1969         var save_generator = generator;
1970         if (!async && avoid_vars.indexOf("await") >= 0) async = true;
1971         if (!generator && avoid_vars.indexOf("yield") >= 0) generator = true;
1972         var name = createVarName(MANDATORY, DONT_STORE);
1973         generator = save_generator;
1974         async = save_async;
1975         return name;
1976     }
1977 }
1978
1979 function createValue() {
1980     var v;
1981     do {
1982         v = VALUES[rng(VALUES.length)];
1983     } while (v == "new.target" && rng(200) || !allow_this && v == "this");
1984     return v;
1985 }
1986
1987 function createBinaryOp(noComma, canThrow) {
1988     var op;
1989     do {
1990         op = BINARY_OPS[rng(BINARY_OPS.length)];
1991     } while (noComma && op == "," || !canThrow && op == " in ");
1992     return op;
1993 }
1994
1995 function createAssignment() {
1996     return ASSIGNMENTS[rng(ASSIGNMENTS.length)];
1997 }
1998
1999 function createUnarySafePrefix() {
2000     var prefix;
2001     do {
2002         prefix = UNARY_SAFE[rng(UNARY_SAFE.length)];
2003     } while (prefix == "delete " && in_class);
2004     return prefix;
2005 }
2006
2007 function createUnaryPrefix() {
2008     var prefix;
2009     do {
2010         prefix = UNARY_PREFIX[rng(UNARY_PREFIX.length)];
2011     } while (prefix == "delete " && in_class);
2012     return prefix;
2013 }
2014
2015 function createUnaryPostfix() {
2016     return UNARY_POSTFIX[rng(UNARY_POSTFIX.length)];
2017 }
2018
2019 function addAvoidVar(name) {
2020     avoid_vars.push(name);
2021 }
2022
2023 function removeAvoidVar(name) {
2024     var index = avoid_vars.lastIndexOf(name);
2025     if (index >= 0) avoid_vars.splice(index, 1);
2026 }
2027
2028 function isBannedKeyword(name) {
2029     switch (name) {
2030       case "arguments":
2031       case "let":
2032         return in_class;
2033       case "await":
2034         return async !== false;
2035       case "yield":
2036         return generator || in_class;
2037     }
2038 }
2039
2040 function getVarName(noConst, noLambda) {
2041     // try to get a generated name reachable from current scope. default to just `a`
2042     var name, tries = 10;
2043     do {
2044         if (--tries < 0) return "a";
2045         name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
2046     } while (!name
2047         || avoid_vars.indexOf(name) >= 0
2048         || noConst && (block_vars.indexOf(name) >= 0
2049             || in_class && [
2050                 "Infinity",
2051                 "NaN",
2052                 "undefined",
2053             ].indexOf(name) >= 0)
2054         || noLambda && lambda_vars.indexOf(name) >= 0
2055         || isBannedKeyword(name));
2056     return name;
2057 }
2058
2059 function createVarName(maybe, dontStore) {
2060     if (!maybe || rng(2)) {
2061         var suffix = rng(3);
2062         var name, tries = 10;
2063         do {
2064             name = VAR_NAMES[rng(VAR_NAMES.length)];
2065             if (--tries < 0) suffix++;
2066             if (suffix) name += "_" + suffix;
2067         } while (unique_vars.indexOf(name) >= 0
2068             || block_vars.indexOf(name) >= 0
2069             || isBannedKeyword(name));
2070         if (!dontStore) VAR_NAMES.push(name);
2071         return name;
2072     }
2073     return "";
2074 }
2075
2076 if (require.main !== module) {
2077     exports.createTopLevelCode = createTopLevelCode;
2078     exports.num_iterations = num_iterations;
2079     exports.verbose = verbose;
2080     return;
2081 }
2082
2083 function run_code(code, toplevel, timeout) {
2084     return sandbox.run_code(sandbox.patch_module_statements(code), toplevel, timeout);
2085 }
2086
2087 function writeln(stream, msg) {
2088     if (typeof msg != "undefined") {
2089         stream.write(typeof msg == "string" ? msg : msg.stack || "" + msg);
2090     }
2091     stream.write("\n");
2092 }
2093
2094 function println(msg) {
2095     writeln(process.stdout, msg);
2096 }
2097
2098 function errorln(msg) {
2099     writeln(process.stderr, msg);
2100 }
2101
2102 function try_beautify(code, toplevel, result, printfn, options) {
2103     var beautified = UglifyJS.minify(code, JSON.parse(beautify_options));
2104     if (beautified.error) {
2105         printfn("// !!! beautify failed !!!");
2106         printfn(beautified.error);
2107         beautified = null;
2108     } else if (!sandbox.same_stdout(run_code(beautified.code, toplevel), result)) {
2109         beautified = null;
2110     } else if (options) {
2111         var uglified = UglifyJS.minify(beautified.code, JSON.parse(options));
2112         var expected, actual;
2113         if (typeof uglify_code != "string" || uglified.error) {
2114             expected = uglify_code;
2115             actual = uglified.error;
2116         } else {
2117             expected = uglify_result;
2118             actual = run_code(uglified.code, toplevel);
2119         }
2120         if (!sandbox.same_stdout(expected, actual)) {
2121             beautified = null;
2122         }
2123     }
2124     if (beautified) {
2125         printfn("// (beautified)");
2126         printfn(beautified.code);
2127     } else {
2128         printfn("//");
2129         printfn(code);
2130     }
2131 }
2132
2133 var default_options = UglifyJS.default_options();
2134
2135 function log_suspects(minify_options, component) {
2136     var options = component in minify_options ? minify_options[component] : true;
2137     if (!options) return;
2138     if (typeof options != "object") options = {};
2139     var defs = default_options[component];
2140     var toplevel = sandbox.has_toplevel(minify_options);
2141     var suspects = Object.keys(defs).filter(function(name) {
2142         var flip = name == "keep_fargs";
2143         if (flip !== (name in options ? options : defs)[name]) {
2144             var m = JSON.parse(JSON.stringify(minify_options));
2145             var o = JSON.parse(JSON.stringify(options));
2146             o[name] = flip;
2147             m[component] = o;
2148             m.validate = true;
2149             var result = UglifyJS.minify(original_code, m);
2150             if (typeof uglify_code != "string") {
2151                 return !sandbox.same_stdout(uglify_code, result.error);
2152             } else if (result.error) {
2153                 errorln("Error testing options." + component + "." + name);
2154                 errorln(result.error);
2155             } else {
2156                 var r = run_code(result.code, toplevel);
2157                 return !sandbox.same_stdout(uglify_result, r);
2158             }
2159         }
2160     });
2161     if (suspects.length > 0) {
2162         errorln("Suspicious " + component + " options:");
2163         suspects.forEach(function(name) {
2164             errorln("  " + name);
2165         });
2166         errorln();
2167     }
2168 }
2169
2170 function log_suspects_global(options, toplevel) {
2171     var suspects = Object.keys(default_options).filter(function(component) {
2172       return typeof default_options[component] != "object";
2173     }).filter(function(component) {
2174         var m = JSON.parse(options);
2175         m[component] = false;
2176         m.validate = true;
2177         var result = UglifyJS.minify(original_code, m);
2178         if (typeof uglify_code != "string") {
2179             return !sandbox.same_stdout(uglify_code, result.error);
2180         } else if (result.error) {
2181             errorln("Error testing options." + component);
2182             errorln(result.error);
2183         } else {
2184             var r = run_code(result.code, toplevel);
2185             return !sandbox.same_stdout(uglify_result, r);
2186         }
2187     });
2188     if (suspects.length > 0) {
2189         errorln("Suspicious options:");
2190         suspects.forEach(function(name) {
2191             errorln("  " + name);
2192         });
2193         errorln();
2194     }
2195 }
2196
2197 function log(options) {
2198     var toplevel = sandbox.has_toplevel(JSON.parse(options));
2199     if (!ok) errorln("\n\n\n\n\n\n!!!!!!!!!!\n\n\n");
2200     errorln("//=============================================================");
2201     if (!ok) errorln("// !!!!!! Failed... round " + round);
2202     errorln("// original code");
2203     try_beautify(original_code, toplevel, original_result, errorln, options);
2204     errorln();
2205     errorln();
2206     errorln("//-------------------------------------------------------------");
2207     if (typeof uglify_code == "string") {
2208         errorln("// uglified code");
2209         try_beautify(uglify_code, toplevel, uglify_result, errorln);
2210         errorln();
2211         errorln();
2212         errorln("original result:");
2213         errorln(original_result);
2214         errorln("uglified result:");
2215         errorln(uglify_result);
2216     } else {
2217         errorln("// !!! uglify failed !!!");
2218         errorln(uglify_code);
2219         if (errored) {
2220             errorln();
2221             errorln();
2222             errorln("original stacktrace:");
2223             errorln(original_result);
2224         }
2225     }
2226     errorln("//-------------------------------------------------------------");
2227     if (!ok) {
2228         var reduce_options = JSON.parse(options);
2229         reduce_options.validate = true;
2230         var reduced = reduce_test(original_code, reduce_options, {
2231             verbose: false,
2232         }).code;
2233         if (reduced) {
2234             errorln();
2235             errorln("// reduced test case (output will differ)");
2236             errorln();
2237             errorln(reduced);
2238             errorln();
2239             errorln("//-------------------------------------------------------------");
2240         }
2241     }
2242     errorln("minify(options):");
2243     errorln(JSON.stringify(JSON.parse(options), null, 2));
2244     errorln();
2245     if (!ok) {
2246         Object.keys(default_options).filter(function(component) {
2247           var defs = default_options[component];
2248           return defs && typeof defs == "object";
2249         }).forEach(log_suspects.bind(null, JSON.parse(options)));
2250         log_suspects_global(options, toplevel);
2251         errorln("!!!!!! Failed... round " + round);
2252     }
2253 }
2254
2255 function sort_globals(code) {
2256     var globals = run_code("throw Object.keys(this).sort(" + function(global) {
2257         return function(m, n) {
2258             return (typeof global[n] == "function") - (typeof global[m] == "function")
2259                 || (m < n ? -1 : m > n ? 1 : 0);
2260         };
2261     } + "(this));\n" + code);
2262     if (!Array.isArray(globals)) {
2263         errorln();
2264         errorln();
2265         errorln("//-------------------------------------------------------------");
2266         errorln("// !!! sort_globals() failed !!!");
2267         errorln("// expected Array, got:");
2268         if (!sandbox.is_error(globals)) try {
2269             globals = JSON.stringify(globals);
2270         } catch (e) {}
2271         errorln(globals);
2272         errorln("//");
2273         errorln(code);
2274         errorln();
2275         return code;
2276     }
2277     return globals.length ? "var " + globals.map(function(name) {
2278         return name + "=" + name;
2279     }).join() + ";" + code : code;
2280 }
2281
2282 function fuzzy_match(original, uglified) {
2283     var m = [], n = [];
2284     if (collect(original, m) !== collect(uglified, n)) return false;
2285     for (var i = 0; i < m.length; i++) {
2286         var a = m[i];
2287         var b = n[i];
2288         if (Math.abs((b - a) / a) > 1e-10) return false;
2289     }
2290     return true;
2291
2292     function collect(input, nums) {
2293         return input.replace(/-?([1-9][0-9]*(\.[0-9]+)?|0\.[0-9]+)(e-?[1-9][0-9]*)?/ig, function(num) {
2294             return "<|" + nums.push(+num) + "|>";
2295         });
2296     }
2297 }
2298
2299 function patch_proto() {
2300     [ Array, Boolean, Error, Function, Number, Object, RegExp, String ].forEach(function(type) {
2301         [ "toString", "valueOf" ].forEach(function(prop) {
2302             type.prototype[prop] = function(fn) {
2303                 return function() {
2304                     try {
2305                         return fn.apply(this, arguments);
2306                     } catch (e) {}
2307                 };
2308             }(type.prototype[prop]);
2309         });
2310     });
2311 }
2312
2313 function is_error_timeout(ex) {
2314     return /timed out/.test(ex.message);
2315 }
2316
2317 function is_error_in(ex) {
2318     return ex.name == "TypeError" && /'in'/.test(ex.message);
2319 }
2320
2321 function is_error_spread(ex) {
2322     return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function/.test(ex.message);
2323 }
2324
2325 function is_error_recursion(ex) {
2326     return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message);
2327 }
2328
2329 function is_error_redeclaration(ex) {
2330     return ex.name == "SyntaxError" && /already been declared|redeclaration/.test(ex.message);
2331 }
2332
2333 function is_error_destructuring(ex) {
2334     return ex.name == "TypeError" && /^Cannot destructure /.test(ex.message);
2335 }
2336
2337 function is_error_class_constructor(ex) {
2338     return ex.name == "TypeError" && /\bconstructors?\b/.test(ex.message) && /\bnew\b/.test(ex.message);
2339 }
2340
2341 function is_error_getter_only_property(ex) {
2342     return ex.name == "TypeError" && [ "getter", "only", "property" ].every(function(keyword) {
2343         return ex.message.indexOf(keyword) >= 0;
2344     });
2345 }
2346
2347 function patch_try_catch(orig, toplevel) {
2348     var stack = [ {
2349         code: orig,
2350         index: 0,
2351         offset: 0,
2352         tries: [],
2353     } ];
2354     var tail_throw = '\nif (typeof UFUZZ_ERROR == "object") throw UFUZZ_ERROR;\n';
2355     var re = /(?:(?:^|[\s{}):;])try|}\s*catch\s*\(([^()[{]+)\)|}\s*finally)\s*(?={)/g;
2356     while (stack.length) {
2357         var code = stack[0].code;
2358         var offset = stack[0].offset;
2359         var tries = stack[0].tries;
2360         var match;
2361         re.lastIndex = stack.shift().index;
2362         while (match = re.exec(code)) {
2363             var index = match.index + match[0].length + 1;
2364             if (/(?:^|[\s{}):;])try\s*$/.test(match[0])) {
2365                 tries.unshift({ try: index - offset });
2366                 continue;
2367             }
2368             var insert;
2369             if (/}\s*finally\s*$/.test(match[0])) {
2370                 tries.shift();
2371                 insert = tail_throw;
2372             } else {
2373                 while (tries.length && tries[0].catch) tries.shift();
2374                 tries[0].catch = index - offset;
2375                 insert = [
2376                     "if (!" + match[1] + ".ufuzz_var) {",
2377                         match[1] + '.ufuzz_var = "' + match[1] + '";',
2378                         match[1] + ".ufuzz_try = " + tries[0].try + ";",
2379                         match[1] + ".ufuzz_catch = " + tries[0].catch + ";",
2380                         "UFUZZ_ERROR = " + match[1] + ";",
2381                     "}",
2382                     "throw " + match[1] + ";",
2383                 ].join("\n");
2384             }
2385             var new_code = code.slice(0, index) + insert + code.slice(index) + tail_throw;
2386             var result = run_code(new_code, toplevel);
2387             if (!sandbox.is_error(result)) {
2388                 if (!stack.filled && match[1]) stack.push({
2389                     code: code,
2390                     index: index && index - 1,
2391                     offset: offset,
2392                     tries: JSON.parse(JSON.stringify(tries)),
2393                 });
2394                 offset += insert.length;
2395                 code = new_code;
2396             } else if (is_error_in(result)) {
2397                 index = result.ufuzz_catch;
2398                 return orig.slice(0, index) + result.ufuzz_var + ' = new Error("invalid `in`");' + orig.slice(index);
2399             } else if (is_error_spread(result)) {
2400                 index = result.ufuzz_catch;
2401                 return orig.slice(0, index) + result.ufuzz_var + ' = new Error("spread not iterable");' + orig.slice(index);
2402             } else if (is_error_recursion(result)) {
2403                 index = result.ufuzz_try;
2404                 return orig.slice(0, index) + 'throw new Error("skipping infinite recursion");' + orig.slice(index);
2405             } else if (is_error_destructuring(result)) {
2406                 index = result.ufuzz_catch;
2407                 return orig.slice(0, index) + result.ufuzz_var + ' = new Error("cannot destructure");' + orig.slice(index);
2408             } else if (is_error_class_constructor(result)) {
2409                 index = result.ufuzz_catch;
2410                 return orig.slice(0, index) + result.ufuzz_var + ' = new Error("missing new for class");' + orig.slice(index);
2411             } else if (is_error_getter_only_property(result)) {
2412                 index = result.ufuzz_catch;
2413                 return orig.slice(0, index) + result.ufuzz_var + ' = new Error("setting getter-only property");' + orig.slice(index);
2414             }
2415         }
2416         stack.filled = true;
2417     }
2418 }
2419
2420 var beautify_options = {
2421     compress: false,
2422     mangle: false,
2423     output: {
2424         beautify: true,
2425         braces: true,
2426     },
2427 };
2428 var minify_options = require("./options.json");
2429 if (typeof sandbox.run_code("A:if (0) B:; else B:;") != "string") {
2430     minify_options.forEach(function(o) {
2431         if (!("mangle" in o)) o.mangle = {};
2432         if (o.mangle) o.mangle.v8 = true;
2433     });
2434 }
2435 var bug_async_arrow_rest = function() {};
2436 if (SUPPORT.arrow && SUPPORT.async && SUPPORT.rest && typeof sandbox.run_code("async (a = f(...[], b)) => 0;") != "string") {
2437     bug_async_arrow_rest = function(ex) {
2438         return ex.name == "SyntaxError" && ex.message == "Rest parameter must be last formal parameter";
2439     };
2440 }
2441 var bug_async_class_await = SUPPORT.async && SUPPORT.class_field && typeof sandbox.run_code("var await; async function f() { class A { static p = await; } }") != "string";
2442 var bug_for_of_async = SUPPORT.for_await_of && typeof sandbox.run_code("var async; for (async of []);") != "string";
2443 var bug_for_of_var = SUPPORT.for_of && SUPPORT.let && typeof sandbox.run_code("try {} catch (e) { for (var e of []); }") != "string";
2444 if (SUPPORT.destructuring && typeof sandbox.run_code("console.log([ 1 ], {} = 2);") != "string") {
2445     beautify_options.output.v8 = true;
2446     minify_options.forEach(function(o) {
2447         if (!("output" in o)) o.output = {};
2448         o.output.v8 = true;
2449     });
2450 }
2451 beautify_options = JSON.stringify(beautify_options);
2452 minify_options = minify_options.map(JSON.stringify);
2453 var original_code, original_result, errored;
2454 var uglify_code, uglify_result, ok;
2455 for (var round = 1; round <= num_iterations; round++) {
2456     process.stdout.write(round + " of " + num_iterations + "\r");
2457
2458     original_code = createTopLevelCode();
2459     var orig_result = [ run_code(original_code), run_code(original_code, true) ];
2460     if (orig_result.some(function(result, toplevel) {
2461         if (typeof result == "string") return;
2462         println();
2463         println();
2464         println("//=============================================================");
2465         println("// original code" + (toplevel ? " (toplevel)" : ""));
2466         try_beautify(original_code, toplevel, result, println);
2467         println();
2468         println();
2469         println("original result:");
2470         println(result);
2471         println();
2472         // ignore v8 parser bug
2473         return bug_async_arrow_rest(result);
2474     })) continue;
2475     minify_options.forEach(function(options) {
2476         var o = JSON.parse(options);
2477         var toplevel = sandbox.has_toplevel(o);
2478         o.validate = true;
2479         uglify_code = UglifyJS.minify(original_code, o);
2480         original_result = orig_result[toplevel ? 1 : 0];
2481         errored = typeof original_result != "string";
2482         if (!uglify_code.error) {
2483             uglify_code = uglify_code.code;
2484             uglify_result = run_code(uglify_code, toplevel);
2485             ok = sandbox.same_stdout(original_result, uglify_result);
2486             // ignore v8 parser bug
2487             if (!ok && bug_async_arrow_rest(uglify_result)) ok = true;
2488             // handle difference caused by time-outs
2489             if (!ok && errored && is_error_timeout(original_result)) {
2490                 if (is_error_timeout(uglify_result)) {
2491                     // ignore difference in error message
2492                     ok = true;
2493                 } else {
2494                     // ignore spurious time-outs
2495                     if (!orig_result[toplevel ? 3 : 2]) orig_result[toplevel ? 3 : 2] = run_code(original_code, toplevel, 10000);
2496                     ok = sandbox.same_stdout(orig_result[toplevel ? 3 : 2], uglify_result);
2497                 }
2498             }
2499             // ignore declaration order of global variables
2500             if (!ok && !toplevel) {
2501                 ok = sandbox.same_stdout(run_code(sort_globals(original_code)), run_code(sort_globals(uglify_code)));
2502             }
2503             // ignore numerical imprecision caused by `unsafe_math`
2504             if (!ok && o.compress && o.compress.unsafe_math && typeof original_result == typeof uglify_result) {
2505                 if (typeof original_result == "string") {
2506                     ok = fuzzy_match(original_result, uglify_result);
2507                 } else if (sandbox.is_error(original_result)) {
2508                     ok = original_result.name == uglify_result.name && fuzzy_match(original_result.message, uglify_result.message);
2509                 }
2510                 if (!ok) {
2511                     var fuzzy_result = run_code(original_code.replace(/( - 0\.1){3}/g, " - 0.3"), toplevel);
2512                     ok = sandbox.same_stdout(fuzzy_result, uglify_result);
2513                 }
2514             }
2515             // ignore difference in error message caused by Temporal Dead Zone
2516             if (!ok && errored && uglify_result.name == "ReferenceError" && original_result.name == "ReferenceError") ok = true;
2517             // ignore difference due to implicit strict-mode in `class`
2518             if (!ok && /\bclass\b/.test(original_code)) {
2519                 var original_strict = run_code('"use strict";\n' + original_code, toplevel);
2520                 var uglify_strict = run_code('"use strict";\n' + uglify_code, toplevel);
2521                 if (typeof original_strict != "string") {
2522                     ok = typeof uglify_strict != "string";
2523                 } else {
2524                     ok = sandbox.same_stdout(original_strict, uglify_strict);
2525                 }
2526             }
2527             // ignore difference in error message caused by `import` symbol redeclaration
2528             if (!ok && errored && /\bimport\b/.test(original_code)) {
2529                 if (is_error_redeclaration(uglify_result) && is_error_redeclaration(original_result)) ok = true;
2530             }
2531             // ignore difference due to `__proto__` assignment
2532             if (!ok && /\b__proto__\b/.test(original_code)) {
2533                 var original_proto = run_code("(" + patch_proto + ")();\n" + original_code, toplevel);
2534                 var uglify_proto = run_code("(" + patch_proto + ")();\n" + uglify_code, toplevel);
2535                 ok = sandbox.same_stdout(original_proto, uglify_proto);
2536             }
2537             // ignore difference in error message caused by `in`
2538             if (!ok && errored && is_error_in(uglify_result) && is_error_in(original_result)) ok = true;
2539             // ignore difference in error message caused by spread syntax
2540             if (!ok && errored && is_error_spread(uglify_result) && is_error_spread(original_result)) ok = true;
2541             // ignore difference in depth of termination caused by infinite recursion
2542             if (!ok && errored && is_error_recursion(original_result)) {
2543                 if (is_error_recursion(uglify_result) || typeof uglify_result == "string") ok = true;
2544             }
2545             // ignore difference in error message caused by destructuring
2546             if (!ok && errored && is_error_destructuring(uglify_result) && is_error_destructuring(original_result)) {
2547                 ok = true;
2548             }
2549             // ignore difference in error message caused by call on class
2550             if (!ok && errored && is_error_class_constructor(uglify_result) && is_error_class_constructor(original_result)) {
2551                 ok = true;
2552             }
2553             // ignore difference in error message caused by setting getter-only property
2554             if (!ok && errored && is_error_getter_only_property(uglify_result) && is_error_getter_only_property(original_result)) {
2555                 ok = true;
2556             }
2557             // ignore errors above when caught by try-catch
2558             if (!ok) {
2559                 var orig_skipped = patch_try_catch(original_code, toplevel);
2560                 var uglify_skipped = patch_try_catch(uglify_code, toplevel);
2561                 if (orig_skipped && uglify_skipped) {
2562                     ok = sandbox.same_stdout(run_code(orig_skipped, toplevel), run_code(uglify_skipped, toplevel));
2563                 }
2564             }
2565         } else {
2566             uglify_code = uglify_code.error;
2567             ok = errored && uglify_code.name == original_result.name;
2568         }
2569         if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
2570         if (!ok && isFinite(num_iterations)) {
2571             println();
2572             process.exit(1);
2573         }
2574     });
2575 }
2576 println();