1 // derived from https://github.com/qfox/uglyfuzzer by Peter van der Zee
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
8 require("../../tools/tty");
10 var UglifyJS = require("../..");
11 var randomBytes = require("crypto").randomBytes;
12 var sandbox = require("../sandbox");
13 var reduce_test = require("../reduce");
15 var MAX_GENERATED_TOPLEVELS_PER_RUN = 1;
16 var MAX_GENERATION_RECURSION_DEPTH = 12;
17 var INTERVAL_COUNT = 100;
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;
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");
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?
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]) {
56 verbose_interval = true;
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");
63 MAX_GENERATION_RECURSION_DEPTH = +process.argv[++i];
64 if (!MAX_GENERATION_RECURSION_DEPTH) throw new Error("Recursion depth must be at least 1");
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");
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");
76 case "--no-catch-redef":
79 case "--no-directive":
80 generate_directive = false;
85 case "--stmt-depth-from-func":
86 STMT_COUNT_FROM_GLOBAL = false;
89 STMTS_TO_USE = process.argv[++i].split(",").map(function(name) {
90 return STMT_ARG_TO_ID[name];
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) {
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 **");
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");
129 var SUPPORT = function(matrix) {
130 for (var name in matrix) {
131 matrix[name] = typeof sandbox.run_code(matrix[name]) == "string";
136 async: "async function f(){}",
137 async_generator: "async function* f(){}",
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(){}",
152 logical_assignment: "[].p ??= 0;",
153 new_target: "function f() { new.target; }",
155 optional_chaining: "0?.p",
156 rest: "var [...a] = [];",
157 rest_object: "var {...a} = {};",
159 spread_object: "({...0});",
161 trailing_comma: "function f(a,) {}",
163 if (SUPPORT.exponentiation && sandbox.run_code("console.log(10 ** 100 === Math.pow(10, 100));") !== "true\n") {
164 SUPPORT.exponentiation = false;
187 "(-0)", // 0/-0 !== 0
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
208 VALUES = VALUES.concat(VALUES);
209 VALUES = VALUES.concat(VALUES);
210 VALUES = VALUES.concat(VALUES);
211 if (SUPPORT.bigint) VALUES = VALUES.concat([
215 "Number(0XDEADn << 16n | 0xbeefn)",
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");
224 " + ", // spaces needed to disambiguate with ++ cases (could otherwise cause syntax errors)
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 ");
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([
275 ASSIGNMENTS = ASSIGNMENTS.concat(ASSIGNMENTS);
276 if (SUPPORT.exponentiation) ASSIGNMENTS.push("**=");
277 if (SUPPORT.logical_assignment) ASSIGNMENTS = ASSIGNMENTS.concat([
291 var UNARY_POSTFIX = [
295 var UNARY_PREFIX = UNARY_POSTFIX.concat(UNARY_SAFE);
298 var COMMA_OK = false;
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;
311 var DONT_STORE = true;
313 var NO_DUPLICATE = true;
314 var NO_LAMBDA = true;
315 var NO_TEMPLATE = true;
326 "c", // prevent redeclaring this, avoid assigning to this
340 var INITIAL_NAMES_LEN = VAR_NAMES.length;
342 var TYPEOF_OUTCOMES = [
356 var block_vars = [ "let" ];
357 var lambda_vars = [];
358 var unique_vars = [];
360 var allow_this = true;
362 var has_await = false;
363 var export_default = false;
364 var generator = false;
370 var called = Object.create(null);
374 var r = randomBytes(2).readUInt16LE(0) / 65536;
375 return Math.floor(max * r);
378 function strictMode() {
379 return use_strict && rng(4) == 0 ? '"use strict";' : "";
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 ";
393 function mayDefer(code) {
394 if (SUPPORT.arrow && rng(200) == 0) {
396 return "void setImmediate(() => (" + code + "))";
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;
410 export_default = false;
417 called = Object.create(null);
420 appendExport(1) + "var _calls_ = 10, a = 100, b = 10, c = 0;",
422 createBlockVariables(MAX_GENERATION_RECURSION_DEPTH, 0, CANNOT_THROW, function(defns) {
425 s.push(createStatements(3, MAX_GENERATION_RECURSION_DEPTH, CANNOT_THROW, CANNOT_BREAK, CANNOT_CONTINUE, CANNOT_RETURN, 0));
427 s.push(createFunctions(rng(MAX_GENERATED_TOPLEVELS_PER_RUN) + 1, MAX_GENERATION_RECURSION_DEPTH, DEFUN_OK, CANNOT_THROW, 0));
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 + ")";
437 function createFunctions(n, recurmax, allowDefun, canThrow, stmtDepth) {
438 if (--recurmax < 0) { return ";"; }
441 s += createFunction(recurmax, allowDefun, canThrow, stmtDepth) + "\n";
446 function addTrailingComma(list) {
447 return SUPPORT.trailing_comma && list && rng(20) == 0 ? list + "," : list;
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;
457 for (var n = rng(4); --n >= 0;) {
458 var name = createVarName(MANDATORY);
459 if (noDuplicate || in_class) unique_vars.push(name);
462 unique_vars.length = len;
463 generator = save_generator;
465 return addTrailingComma(params.join(", "));
468 function createArgs(recurmax, stmtDepth, canThrow, noTemplate) {
470 if (SUPPORT.template && !noTemplate && rng(20) == 0) return createTemplateLiteral(recurmax, stmtDepth, canThrow);
472 for (var n = rng(4); --n >= 0;) switch (SUPPORT.spread ? rng(50) : 3) {
475 var name = getVarName();
476 if (canThrow && rng(20) == 0) {
477 args.push("..." + name);
479 args.push('...("" + ' + name + ")");
483 args.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow));
486 args.push(rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
489 return "(" + addTrailingComma(args.join(", ")) + ")";
492 function createAssignmentPairs(recurmax, stmtDepth, canThrow, nameLenBefore, was_async, was_generator) {
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;
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);
510 side_effects.push(value);
513 return declare_only.concat(side_effects);
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;
528 function fill(nameFn, valueFn) {
529 var save_async = async;
530 if (was_async != null) {
532 if (save_async || was_async) addAvoidVar("await");
534 var save_generator = generator;
535 if (was_generator != null) {
537 if (save_generator || was_generator) addAvoidVar("yield");
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");
546 if (was_async != null) {
548 if (save_async || was_async) removeAvoidVar("await");
550 if (valueFn) valueFn();
551 if (save_vars) [].push.apply(VAR_NAMES, save_vars);
552 avoid.forEach(removeAvoidVar);
553 generator = save_generator;
557 function createAssignmentValue(recurmax) {
558 return nameLenBefore && rng(2) ? createValue() : createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
561 function createDefaultValue(recurmax, noDefault) {
562 return !noDefault && SUPPORT.default_value && rng(20) == 0 ? " = " + createAssignmentValue(recurmax) : "";
565 function createKey(recurmax, keys) {
568 key = createObjectKey(recurmax, stmtDepth, canThrow);
569 } while (keys.indexOf(key) >= 0);
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;
582 unique_vars.length -= 6;
584 unique_vars.push(name);
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);
596 var name = createName();
598 names.unshift(name + createDefaultValue(recurmax, noDefault));
601 fill(null, function() {
602 values.unshift(createAssignmentValue(recurmax));
612 function createDestructured(recurmax, noDefault, names, values) {
615 if (--recurmax < 0) {
617 values.unshift('""');
619 var pairs = createPairs(recurmax);
622 default_value = createDefaultValue(recurmax, noDefault);
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)) {
631 pairs.values[index] = createAssignmentValue(recurmax);
633 pairs.values.length = index + 1;
636 convertToRest(pairs.names);
637 names.unshift("[ " + pairs.names.join(", ") + " ]" + default_value);
638 values.unshift("[ " + pairs.values.join(", ") + " ]");
643 if (--recurmax < 0) {
645 values.unshift('""');
647 var pairs = createPairs(recurmax);
649 pairs.names.forEach(function(name, index) {
650 if (/^[[{]/.test(name)) {
653 key = KEYS[rng(KEYS.length)];
654 } while (keys.indexOf(key) >= 0);
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;
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;
672 s = addTrailingComma(s.join(", "));
674 names.unshift("{ " + s + " }" + createDefaultValue(recurmax, noDefault));
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(", ")) + " }");
684 var name = createName();
686 names.unshift(name + createDefaultValue(recurmax, noDefault));
688 values.unshift(createAssignmentValue(recurmax));
695 function filterDirective(s) {
696 if (!generate_directive && !s[1] && /\("/.test(s[2])) s[2] = ";" + s[2];
700 function createBlockVariables(recurmax, stmtDepth, canThrow, fn) {
702 var block_len = block_vars.length;
703 var class_len = classes.length;
704 var nameLenBefore = VAR_NAMES.length;
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)) {
715 block_vars.push(name);
717 unique_vars.length -= 6;
719 consts.forEach(addAvoidVar);
720 lets.forEach(addAvoidVar);
722 if (SUPPORT.class) while (rng(200) == 0) {
723 var name = "C" + clazz++;
725 s.push(appendExport(stmtDepth, true) + createClassLiteral(recurmax, stmtDepth, canThrow, name));
728 s.push(createDefinitions("const", consts), createDefinitions("let", lets));
730 s.push(createDefinitions("let", lets), createDefinitions("const", consts));
735 VAR_NAMES.length = nameLenBefore;
736 classes.length = class_len;
737 block_vars.length = block_len;
739 function createDefinitions(type, names) {
740 if (!names.length) return "";
741 var s = appendExport(stmtDepth) + type + " ";
742 switch (SUPPORT.destructuring ? rng(10) : 2) {
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(", ") + " ];";
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(", ") + "};";
761 s += names.map(function(name) {
762 if (type == "let" && !rng(10)) {
763 removeAvoidVar(name);
766 var value = createExpression(recurmax, NO_COMMA, stmtDepth, canThrow);
767 removeAvoidVar(name);
768 return name + " = " + value;
773 names.forEach(removeAvoidVar);
778 function mayCreateBlockVariables(recurmax, stmtDepth, canThrow, fn) {
779 if (SUPPORT.const_block) {
780 createBlockVariables(recurmax, stmtDepth, canThrow, fn);
788 function makeFunction(name) {
789 lambda_vars.push(name);
791 name = "function* " + name;
793 name = "function " + name;
795 if (async) name = "async " + name;
799 function invokeGenerator(was_generator) {
800 if (generator && !was_generator) switch (rng(4)) {
804 return ".next().done";
806 return ".next().value";
811 function createFunction(recurmax, allowDefun, canThrow, stmtDepth) {
812 if (--recurmax < 0) { return ";"; }
813 if (!STMT_COUNT_FROM_GLOBAL) stmtDepth = 0;
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) {
830 createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
831 if (allowDefun || rng(5) > 0) {
832 name = "f" + funcs++;
834 unique_vars.push("a", "b", "c");
835 name = createVarName(MANDATORY, !allowDefun);
836 unique_vars.length -= 3;
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(", ")) + ")";
846 params = createParams(save_async, save_generator);
848 s.push(makeFunction(name) + "(" + params + "){", strictMode());
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));
854 // functions with statements
855 s.push(_createStatements(3, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth));
858 s = filterDirective(s).join("\n");
860 var call_next = invokeGenerator(save_generator);
861 generator = save_generator;
863 lambda_vars.length = lambda_len;
864 VAR_NAMES.length = nameLenBefore;
866 if (allowDefun) s = appendExport(stmtDepth, true) + s;
868 // avoid "function statements" (decl inside statements)
869 s = appendExport(stmtDepth) + "var " + createVarName(MANDATORY) + " = " + s;
870 s += args || createArgs(recurmax, stmtDepth, canThrow);
872 } else if (!(name in called) || args || rng(3)) {
873 s += appendExport(stmtDepth) + "var " + createVarName(MANDATORY) + " = " + name;
874 s += args || createArgs(recurmax, stmtDepth, canThrow);
881 function _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
882 if (--recurmax < 0) { return ";"; }
885 s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "\n";
890 function createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
892 mayCreateBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
894 s += _createStatements(n, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
899 function enableLoopControl(flag, defaultValue) {
900 return Array.isArray(flag) && flag.indexOf("") < 0 ? flag.concat("") : flag || defaultValue;
903 function createLabel(canBreak, canContinue) {
907 if (Array.isArray(canBreak)) {
908 canBreak = canBreak.slice();
910 canBreak = canBreak ? [ "" ] : [];
912 canBreak.push(label);
913 if (Array.isArray(canContinue)) {
914 canContinue = canContinue.slice();
916 canContinue = canContinue ? [ "" ] : [];
918 canContinue.push(label);
922 continue: canContinue,
923 target: label ? "L" + label + ": " : ""
927 function getLabel(label) {
928 if (!Array.isArray(label)) return "";
929 label = label[rng(label.length)];
930 return label && " L" + label;
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 ";
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;
948 function createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth, target) {
951 if (--recurmax < 0) {
952 return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";";
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)];
964 var label = createLabel(canBreak);
965 return label.target + "{" + createStatements(rng(5) + 1, recurmax, canThrow, label.break, canContinue, cannotReturn, stmtDepth) + "}";
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) : "");
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);}";
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) + "}";
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);
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;
993 } else if (bug_for_of_async && of) {
994 addAvoidVar("async");
995 key = getVarName(NO_CONST);
996 removeAvoidVar("async");
998 key = getVarName(NO_CONST);
1001 if (!/^key/.test(key)) {
1002 if (!(of && bug_for_of_var) && rng(10) == 0) init = "var ";
1004 init = declareVarName(key, of && bug_for_of_var);
1006 if (!SUPPORT.destructuring || of && !(canThrow && rng(20) == 0) || rng(10)) {
1008 } else if (rng(5)) {
1009 init += "[ " + key + " ]";
1011 init += "{ length: " + key + " }";
1013 var s = "var expr" + loop + " = ";
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) {
1021 s += gen + " && typeof " + gen + "[Symbol.";
1022 s += await ? "asyncIterator" : "iterator";
1023 s += '] == "function" ? ' + gen + " : " + createArrayLiteral(recurmax, stmtDepth, canThrow) + "; ";
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) + "; ";
1030 s += '"" + (' + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "); ";
1032 s += label.target + " for ";
1037 s += "(" + init + " of expr" + loop + ") {";
1039 s += createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "; ";
1040 s += label.target + " for (" + init + " in expr" + loop + ") {";
1042 if (/^key/.test(key)) VAR_NAMES.push(key);
1045 unique_vars.push("a", "b", "c", "undefined", "NaN", "Infinity");
1048 name = createVarName(MANDATORY);
1049 } while (name == key);
1050 unique_vars.length -= 6;
1051 s += declareVarName(name) + name + " = expr" + loop + "[" + key + "]; ";
1053 s += createStatement(recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + "}";
1054 VAR_NAMES.length = nameLenBefore;
1055 block_vars.length = block_len;
1056 return "{" + s + "}";
1058 return use_strict && rng(20) === 0 ? '"use strict";' : ";";
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) + ";";
1064 return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ";";
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) + "}";
1070 if (SUPPORT.destructuring && stmtDepth == 1 && rng(5) == 0) {
1071 var s = rng(2) ? " " + createImportAlias() : "";
1075 s += " * as " + createImportAlias();
1078 for (var i = rng(4); --i >= 0;) {
1079 var name = createImportAlias();
1080 names.push(rng(2) ? getDotKey() + " as " + name : name);
1082 s += " { " + names.join(", ") + " }";
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)) {
1094 unique_vars.push("c");
1095 var name = createVarName(MANDATORY);
1097 return appendExport(stmtDepth) + "var " + name + ";";
1099 // initializer can only have one expression
1100 unique_vars.push("c");
1101 var name = createVarName(MANDATORY);
1103 return appendExport(stmtDepth) + "var " + name + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1105 // initializer can only have one expression
1106 unique_vars.push("c");
1107 var n1 = createVarName(MANDATORY);
1108 var n2 = createVarName(MANDATORY);
1110 return appendExport(stmtDepth) + "var " + n1 + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ", " + n2 + " = " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + ";";
1112 case STMT_RETURN_ETC:
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) + ";";
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) + "}";
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) + "}";
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) + "}";
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) + " }";
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)) {
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 + ", ";
1163 s += "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]: " + name;
1164 removeAvoidVar(name);
1167 s += " catch ({ name: " + name + ", message: " + message + " }) { ";
1170 var name = createVarName(MANDATORY);
1171 if (!catch_redef) unique_vars.push(name);
1172 s += " catch (" + name + ") { ";
1174 var catches = VAR_NAMES.length - nameLenBefore;
1175 s += defns() + "\n";
1176 s += _createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth);
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;
1184 if (n !== 0) s += " finally { " + createStatements(3, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) + " }";
1187 return "c = c + 1;";
1193 function createSwitchParts(recurmax, n, canThrow, canBreak, canContinue, cannotReturn, stmtDepth) {
1194 var hadDefault = false;
1196 canBreak = enableLoopControl(canBreak, CAN_BREAK);
1198 //hadDefault = n > 0; // disables weird `default` clause positioning (use when handling destabilizes)
1199 if (hadDefault || rng(5) > 0) {
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 */",
1210 _createStatements(rng(3) + 1, recurmax, canThrow, canBreak, canContinue, cannotReturn, stmtDepth),
1215 return s.join("\n");
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!
1222 // since `a` and `b` are our canaries we want them more frequently than other expressions (1/3rd chance of a canary)
1225 return "(a++ + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))";
1227 return "((--b) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))";
1229 return "((c = c + 1) + (" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + "))"; // c only gets incremented
1231 var expr = "(" + _createExpression(recurmax, noComma, stmtDepth, canThrow) + ")";
1232 if (async && rng(50) == 0) {
1234 return "(await" + expr + ")";
1236 if (generator && rng(50) == 0) return "(yield" + (canThrow && rng(20) == 0 ? "*" : "") + expr + ")";
1241 function _createExpression(recurmax, noComma, stmtDepth, canThrow) {
1243 switch (rng(_createExpression.N)) {
1245 if (generator && rng(50) == 0) return "yield";
1247 return createUnaryPrefix() + (rng(2) ? "a" : "b");
1250 return (rng(2) ? "a" : "b") + createUnaryPostfix();
1253 // parens needed because assignments aren't valid unless they're the left-most op(s) in an expression
1254 return "b " + createAssignment() + " a";
1257 return rng(2) + " === 1 ? a : b";
1259 if (SUPPORT.template && rng(20) == 0) {
1260 var tmpl = createTemplateLiteral(recurmax, stmtDepth, canThrow);
1261 if (rng(10) == 0) tmpl = "String.raw" + tmpl;
1265 return createValue();
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;
1272 return getVarName();
1274 switch (SUPPORT.destructuring ? rng(20) : 2) {
1278 new Array(rng(3)).join(),
1279 getVarName(NO_CONST, NO_LAMBDA),
1280 new Array(rng(3)).join(),
1282 createArrayLiteral(recurmax, stmtDepth, canThrow),
1287 rng(2) ? "" : createObjectKey(recurmax, stmtDepth, canThrow) + ": ",
1288 getVarName(NO_CONST, NO_LAMBDA),
1290 createExpression(recurmax, COMMA_OK, stmtDepth, canThrow),
1294 return getVarName(NO_CONST, NO_LAMBDA) + createAssignment() + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
1297 return createExpression(recurmax, COMMA_OK, stmtDepth, canThrow);
1299 return createExpression(recurmax, noComma, stmtDepth, canThrow) + " ? " + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + " : " + createExpression(recurmax, noComma, stmtDepth, canThrow);
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) {
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;
1321 if (SUPPORT.arrow && !name && !generator && rng(2)) {
1323 (rng(2) ? createBlockVariables : function() {
1325 })(recurmax, stmtDepth, canThrow, function(defns) {
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(", ")) + ")";
1333 params = createParams(save_async, save_generator, NO_DUPLICATE);
1335 params = (async ? "async (" : "(") + params + ") => ";
1341 _createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth)
1345 s.push("(" + params);
1349 s.push('(typeof arguments != "undefined" && arguments && arguments[' + rng(3) + "])");
1353 s.push("(this && this." + getDotKey() + ")");
1356 s.push(createExpression(recurmax, NO_COMMA, stmtDepth, canThrow));
1362 generator = save_generator;
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;
1371 "(" + makeFunction(name) + "(){",
1373 createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1374 rng(2) ? "})" : "})()" + invokeGenerator(save_generator)
1380 "+" + makeFunction(name) + "(){",
1382 createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1383 "}()" + invokeGenerator(save_generator)
1388 "!" + makeFunction(name) + "(){",
1390 createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1391 "}()" + invokeGenerator(save_generator)
1396 "void " + makeFunction(name) + "(){",
1398 createStatements(rng(5) + 1, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1399 "}()" + invokeGenerator(save_generator)
1405 var instantiate = rng(4) ? "new " : "";
1406 createBlockVariables(recurmax, stmtDepth, canThrow, function(defns) {
1408 instantiate + makeFunction(name) + "(" + createParams(save_async, save_generator) + "){",
1411 var add_new_target = SUPPORT.new_target && VALUES.indexOf("new.target") < 0;
1412 if (add_new_target) VALUES.push("new.target");
1414 if (instantiate) for (var i = rng(4); --i >= 0;) {
1415 s.push((in_class ? "if (this) " : "") + createThisAssignment(recurmax, stmtDepth, canThrow));
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);
1420 generator = save_generator;
1422 lambda_vars.length = lambda_len;
1423 VAR_NAMES.length = nameLenBefore;
1424 s.push(rng(2) ? "}" : "}" + createArgs(recurmax, stmtDepth, canThrow, instantiate));
1427 generator = save_generator;
1429 lambda_vars.length = lambda_len;
1430 VAR_NAMES.length = nameLenBefore;
1431 return filterDirective(s).join("\n");
1434 return createTypeofExpr(recurmax, stmtDepth, canThrow);
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.
1442 return "a/* ignore */++";
1444 return "b/* ignore */--";
1446 return "++/* ignore */a";
1448 return "--/* ignore */b";
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...
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";
1457 return "--/* ignore */b";
1461 return createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
1464 return createUnarySafePrefix() + "(" + createNestedBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1466 return " (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || a || 3).toString() ";
1468 return " /[abc4]/.test((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || b || 5).toString()) ";
1470 return " /[abc4]/g.exec((" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || b || 5).toString()) ";
1472 return " (" + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || " + rng(10) + ").toString()[" +
1473 createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "] ";
1475 return createArrayLiteral(recurmax, stmtDepth, canThrow);
1477 return createObjectLiteral(recurmax, stmtDepth, canThrow);
1479 return createArrayLiteral(recurmax, stmtDepth, canThrow) + "[" +
1480 createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
1482 return createObjectLiteral(recurmax, stmtDepth, canThrow) + "[" +
1483 createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + "]";
1485 return createArrayLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
1487 return createObjectLiteral(recurmax, stmtDepth, canThrow) + "." + getDotKey();
1489 return createValue() + " in " + createArrayLiteral(recurmax, stmtDepth, canThrow);
1491 return createValue() + " in " + createObjectLiteral(recurmax, stmtDepth, canThrow);
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;
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;
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);
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);
1517 if (rng(200) == 0) {
1518 var s = "--_calls_ >= 0 && new ";
1519 var nameLenBefore = VAR_NAMES.length;
1520 var class_len = classes.length;
1522 if (canThrow && rng(20) == 0) {
1524 name = createVarName(MAYBE);
1526 } else if (rng(2)) {
1527 name = "C" + clazz++;
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);
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;
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;
1554 return mayDefer(call);
1556 _createExpression.N = p;
1557 return _createExpression(recurmax, noComma, stmtDepth, canThrow);
1560 function createArrayLiteral(recurmax, stmtDepth, canThrow) {
1563 for (var i = rng(6); --i >= 0;) switch (SUPPORT.spread ? rng(50) : 3 + rng(47)) {
1566 var name = getVarName();
1567 if (canThrow && rng(20) == 0) {
1568 arr.push("..." + name);
1570 arr.push('...("" + ' + name + ")");
1574 arr.push("..." + createArrayLiteral(recurmax, stmtDepth, canThrow));
1578 // in rare cases produce an array hole element
1582 arr.push(createExpression(recurmax, COMMA_OK, stmtDepth, canThrow));
1585 return "[" + arr.join(", ") + "]";
1588 function createTemplateLiteral(recurmax, stmtDepth, canThrow) {
1592 for (var i = rng(6); --i >= 0;) {
1593 s.push("${", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "}");
1596 return "`" + s.join(rng(5) ? "" : "\n") + "`";
1598 function addText() {
1599 while (rng(5) == 0) s.push([
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__");
1644 function getDotKey(assign) {
1647 key = SAFE_KEYS[rng(SAFE_KEYS.length)];
1648 } while (assign && key == "length");
1652 function createObjectKey(recurmax, stmtDepth, canThrow) {
1653 if (SUPPORT.computed_key && rng(10) == 0) {
1654 return "[" + createExpression(recurmax, NO_COMMA, stmtDepth, canThrow) + "]";
1656 return KEYS[rng(KEYS.length)];
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 + ";";
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) + ";";
1669 function createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz) {
1670 var nameLenBefore = VAR_NAMES.length;
1671 var save_async = async;
1672 var save_generator = generator;
1677 } else if (isClazz) {
1678 var clazzName = classes.pop();
1679 name = createObjectKey(recurmax, stmtDepth, canThrow);
1680 classes.push(clazzName);
1682 name = createObjectKey(recurmax, stmtDepth, canThrow);
1685 switch (internal ? 2 : rng(SUPPORT.computed_key ? 3 : 2)) {
1689 fn = function(defns) {
1691 "get " + name + "(){",
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),
1704 } while (name == prop);
1707 fn = function(defns) {
1709 "set " + name + "(" + createVarName(MANDATORY) + "){",
1712 _createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth),
1713 "this." + prop + createAssignment() + _createBinaryExpr(recurmax, COMMA_OK, stmtDepth, canThrow) + ";",
1719 if (/^(constructor|super)$/.test(internal)) {
1722 name = "constructor";
1724 async = SUPPORT.async && rng(200) == 0;
1725 generator = SUPPORT.generator && rng(50) == 0;
1726 if (async && generator && !SUPPORT.async_generator) {
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;
1740 name + "(" + createParams(save_async, save_generator, NO_DUPLICATE) + "){",
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));
1750 s.push(_createStatements(2, recurmax, canThrow, CANNOT_BREAK, CANNOT_CONTINUE, CAN_RETURN, stmtDepth), "}");
1754 createBlockVariables(recurmax, stmtDepth, canThrow, fn);
1755 generator = save_generator;
1757 VAR_NAMES.length = nameLenBefore;
1758 return filterDirective(s).join("\n");
1761 function createObjectLiteral(recurmax, stmtDepth, canThrow) {
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)) {
1768 obj.push("..." + getVarName() + ",");
1771 obj.push("..." + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ",");
1775 obj.push(getVarName() + ",");
1778 obj.push(createObjectFunction(recurmax, stmtDepth, canThrow) + ",");
1781 if (has_proto || rng(200)) {
1782 obj.push(createObjectKey(recurmax, stmtDepth, canThrow) + ": " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ",");
1784 obj.push("__proto__: " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + " || {},");
1790 return obj.join("\n");
1793 function createClassLiteral(recurmax, stmtDepth, canThrow, name) {
1795 var save_async = async;
1796 var save_generator = generator;
1798 var s = "class", constructor = "constructor";
1799 var isClazz = /^C/.test(name);
1800 if (name) s += " " + name;
1802 constructor = "super";
1804 var p = getVarName();
1805 if (canThrow && rng(20) == 0) {
1808 s += "(" + p + " && " + p + ".constructor === Function ? " + p + " : function() {})";
1813 for (var i = rng(6); --i >= 0;) {
1819 var internal = null;
1820 if (SUPPORT.class_private && rng(10) == 0) {
1822 internal = "#" + getDotKey();
1823 } while (declared.indexOf(internal) >= 0);
1824 declared.push(internal);
1826 if (SUPPORT.class_field && rng(2)) {
1827 s += internal || createObjectKey(recurmax, stmtDepth, canThrow);
1829 async = bug_async_class_await && fixed && 0;
1831 s += " = " + createExpression(recurmax, NO_COMMA, stmtDepth, fixed ? canThrow : CANNOT_THROW);
1832 generator = save_generator;
1837 if (!fixed && !internal && constructor && rng(10) == 0) {
1838 internal = constructor;
1841 s += createObjectFunction(recurmax, stmtDepth, canThrow, internal, isClazz) + "\n";
1845 generator = save_generator;
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);
1854 function _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
1855 return "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow)
1856 + createBinaryOp(noComma, canThrow) + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1858 function _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) {
1859 // intentionally generate more hardcore ops
1860 if (--recurmax < 0) return createValue();
1864 return "(c = c + 1, " + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1866 return "(" + createUnarySafePrefix() + "(" + _createSimpleBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + "))";
1868 assignee = getVarName(NO_CONST, NO_LAMBDA);
1869 return "(" + assignee + createAssignment() + _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow) + ")";
1871 assignee = getVarName();
1872 switch (SUPPORT.destructuring ? rng(20) : 2) {
1877 "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
1879 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1884 var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
1885 var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
1888 key1, ": ", assignee,
1889 "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
1891 key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1899 "[", createExpression(recurmax, COMMA_OK, stmtDepth, canThrow), "]",
1901 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1906 if (in_class) return "(Object.isExtensible(" + assignee + ") && " + expr + ")";
1907 return canThrow && rng(20) == 0 ? expr : "(" + assignee + " && " + expr + ")";
1909 assignee = getVarName();
1910 switch (SUPPORT.destructuring ? rng(20) : 2) {
1915 ".", getDotKey(true),
1917 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1922 var key1 = createObjectKey(recurmax, stmtDepth, canThrow);
1923 var key2 = /^\[/.test(key1) ? createObjectKey(recurmax, stmtDepth, canThrow) : key1;
1926 key1, ": ", assignee,
1927 ".", getDotKey(true),
1929 key2, ": ", _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1937 ".", getDotKey(true),
1939 _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow),
1944 if (in_class) return "(Object.isExtensible(" + assignee + ") && " + expr + ")";
1945 return canThrow && rng(20) == 0 ? expr : "(" + assignee + " && " + expr + ")";
1947 return _createBinaryExpr(recurmax, noComma, stmtDepth, canThrow);
1951 function createTypeofExpr(recurmax, stmtDepth, canThrow) {
1954 return "(typeof " + createVar() + ' === "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1956 return "(typeof " + createVar() + ' !== "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1958 return "(typeof " + createVar() + ' == "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1960 return "(typeof " + createVar() + ' != "' + TYPEOF_OUTCOMES[rng(TYPEOF_OUTCOMES.length)] + '")';
1962 return "(typeof " + createVar() + ")";
1964 return "(typeof " + createExpression(recurmax, COMMA_OK, stmtDepth, canThrow) + ")";
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;
1979 function createValue() {
1982 v = VALUES[rng(VALUES.length)];
1983 } while (v == "new.target" && rng(200) || !allow_this && v == "this");
1987 function createBinaryOp(noComma, canThrow) {
1990 op = BINARY_OPS[rng(BINARY_OPS.length)];
1991 } while (noComma && op == "," || !canThrow && op == " in ");
1995 function createAssignment() {
1996 return ASSIGNMENTS[rng(ASSIGNMENTS.length)];
1999 function createUnarySafePrefix() {
2002 prefix = UNARY_SAFE[rng(UNARY_SAFE.length)];
2003 } while (prefix == "delete " && in_class);
2007 function createUnaryPrefix() {
2010 prefix = UNARY_PREFIX[rng(UNARY_PREFIX.length)];
2011 } while (prefix == "delete " && in_class);
2015 function createUnaryPostfix() {
2016 return UNARY_POSTFIX[rng(UNARY_POSTFIX.length)];
2019 function addAvoidVar(name) {
2020 avoid_vars.push(name);
2023 function removeAvoidVar(name) {
2024 var index = avoid_vars.lastIndexOf(name);
2025 if (index >= 0) avoid_vars.splice(index, 1);
2028 function isBannedKeyword(name) {
2034 return async !== false;
2036 return generator || in_class;
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;
2044 if (--tries < 0) return "a";
2045 name = VAR_NAMES[INITIAL_NAMES_LEN + rng(VAR_NAMES.length - INITIAL_NAMES_LEN)];
2047 || avoid_vars.indexOf(name) >= 0
2048 || noConst && (block_vars.indexOf(name) >= 0
2053 ].indexOf(name) >= 0)
2054 || noLambda && lambda_vars.indexOf(name) >= 0
2055 || isBannedKeyword(name));
2059 function createVarName(maybe, dontStore) {
2060 if (!maybe || rng(2)) {
2061 var suffix = rng(3);
2062 var name, tries = 10;
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);
2076 if (require.main !== module) {
2077 exports.createTopLevelCode = createTopLevelCode;
2078 exports.num_iterations = num_iterations;
2079 exports.verbose = verbose;
2083 function run_code(code, toplevel, timeout) {
2084 return sandbox.run_code(sandbox.patch_module_statements(code), toplevel, timeout);
2087 function writeln(stream, msg) {
2088 if (typeof msg != "undefined") {
2089 stream.write(typeof msg == "string" ? msg : msg.stack || "" + msg);
2094 function println(msg) {
2095 writeln(process.stdout, msg);
2098 function errorln(msg) {
2099 writeln(process.stderr, msg);
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);
2108 } else if (!sandbox.same_stdout(run_code(beautified.code, toplevel), result)) {
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;
2117 expected = uglify_result;
2118 actual = run_code(uglified.code, toplevel);
2120 if (!sandbox.same_stdout(expected, actual)) {
2125 printfn("// (beautified)");
2126 printfn(beautified.code);
2133 var default_options = UglifyJS.default_options();
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));
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);
2156 var r = run_code(result.code, toplevel);
2157 return !sandbox.same_stdout(uglify_result, r);
2161 if (suspects.length > 0) {
2162 errorln("Suspicious " + component + " options:");
2163 suspects.forEach(function(name) {
2164 errorln(" " + name);
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;
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);
2184 var r = run_code(result.code, toplevel);
2185 return !sandbox.same_stdout(uglify_result, r);
2188 if (suspects.length > 0) {
2189 errorln("Suspicious options:");
2190 suspects.forEach(function(name) {
2191 errorln(" " + name);
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);
2206 errorln("//-------------------------------------------------------------");
2207 if (typeof uglify_code == "string") {
2208 errorln("// uglified code");
2209 try_beautify(uglify_code, toplevel, uglify_result, errorln);
2212 errorln("original result:");
2213 errorln(original_result);
2214 errorln("uglified result:");
2215 errorln(uglify_result);
2217 errorln("// !!! uglify failed !!!");
2218 errorln(uglify_code);
2222 errorln("original stacktrace:");
2223 errorln(original_result);
2226 errorln("//-------------------------------------------------------------");
2228 var reduce_options = JSON.parse(options);
2229 reduce_options.validate = true;
2230 var reduced = reduce_test(original_code, reduce_options, {
2235 errorln("// reduced test case (output will differ)");
2239 errorln("//-------------------------------------------------------------");
2242 errorln("minify(options):");
2243 errorln(JSON.stringify(JSON.parse(options), null, 2));
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);
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);
2261 } + "(this));\n" + code);
2262 if (!Array.isArray(globals)) {
2265 errorln("//-------------------------------------------------------------");
2266 errorln("// !!! sort_globals() failed !!!");
2267 errorln("// expected Array, got:");
2268 if (!sandbox.is_error(globals)) try {
2269 globals = JSON.stringify(globals);
2277 return globals.length ? "var " + globals.map(function(name) {
2278 return name + "=" + name;
2279 }).join() + ";" + code : code;
2282 function fuzzy_match(original, uglified) {
2284 if (collect(original, m) !== collect(uglified, n)) return false;
2285 for (var i = 0; i < m.length; i++) {
2288 if (Math.abs((b - a) / a) > 1e-10) return false;
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) + "|>";
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) {
2305 return fn.apply(this, arguments);
2308 }(type.prototype[prop]);
2313 function is_error_timeout(ex) {
2314 return /timed out/.test(ex.message);
2317 function is_error_in(ex) {
2318 return ex.name == "TypeError" && /'in'/.test(ex.message);
2321 function is_error_spread(ex) {
2322 return ex.name == "TypeError" && /Found non-callable @@iterator| is not iterable| not a function/.test(ex.message);
2325 function is_error_recursion(ex) {
2326 return ex.name == "RangeError" && /Invalid string length|Maximum call stack size exceeded/.test(ex.message);
2329 function is_error_redeclaration(ex) {
2330 return ex.name == "SyntaxError" && /already been declared|redeclaration/.test(ex.message);
2333 function is_error_destructuring(ex) {
2334 return ex.name == "TypeError" && /^Cannot destructure /.test(ex.message);
2337 function is_error_class_constructor(ex) {
2338 return ex.name == "TypeError" && /\bconstructors?\b/.test(ex.message) && /\bnew\b/.test(ex.message);
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;
2347 function patch_try_catch(orig, toplevel) {
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;
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 });
2369 if (/}\s*finally\s*$/.test(match[0])) {
2371 insert = tail_throw;
2373 while (tries.length && tries[0].catch) tries.shift();
2374 tries[0].catch = index - offset;
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] + ";",
2382 "throw " + match[1] + ";",
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({
2390 index: index && index - 1,
2392 tries: JSON.parse(JSON.stringify(tries)),
2394 offset += insert.length;
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);
2416 stack.filled = true;
2420 var beautify_options = {
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;
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";
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 = {};
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");
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;
2464 println("//=============================================================");
2465 println("// original code" + (toplevel ? " (toplevel)" : ""));
2466 try_beautify(original_code, toplevel, result, println);
2469 println("original result:");
2472 // ignore v8 parser bug
2473 return bug_async_arrow_rest(result);
2475 minify_options.forEach(function(options) {
2476 var o = JSON.parse(options);
2477 var toplevel = sandbox.has_toplevel(o);
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
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);
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)));
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);
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);
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";
2524 ok = sandbox.same_stdout(original_strict, uglify_strict);
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;
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);
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;
2545 // ignore difference in error message caused by destructuring
2546 if (!ok && errored && is_error_destructuring(uglify_result) && is_error_destructuring(original_result)) {
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)) {
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)) {
2557 // ignore errors above when caught by try-catch
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));
2566 uglify_code = uglify_code.error;
2567 ok = errored && uglify_code.name == original_result.name;
2569 if (verbose || (verbose_interval && !(round % INTERVAL_COUNT)) || !ok) log(options);
2570 if (!ok && isFinite(num_iterations)) {