[fuzzer] Initial import of v8's fuzzer
Copied from https://chromium.googlesource.com/v8/v8/+/master/tools/clusterfuzz/js_fuzzer/
Sathya Gunasekaran committed
Jul 6, 2023 at 11:44 UTC
31ec959e9ba06c4fae70dc5dddfeb2257a4d0e48
148 files changed
+8946
compiler/forget/packages/js-fuzzer/.eslintrc.js
new
+22
@@ -0,0 +1,22 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+module.exports = {
6
+ "env": {
7
+ "node": true,
8
+ "commonjs": true,
9
+ "es6": true,
10
+ "mocha": true
11
+ },
12
+ "extends": "eslint:recommended",
13
+ "globals": {
14
+ "Atomics": "readonly",
15
+ "SharedArrayBuffer": "readonly"
16
+ },
17
+ "parserOptions": {
18
+ "ecmaVersion": 2018
19
+ },
20
+ "rules": {
21
+ }
22
+};
compiler/forget/packages/js-fuzzer/.gitignore
new
+6
@@ -0,0 +1,6 @@
1
+/node_modules
2
+/ochang_js_fuzzer*
3
+/db/
4
+/output.zip
5
+/output/
6
+/workdir/
compiler/forget/packages/js-fuzzer/DIR_METADATA
new
+11
@@ -0,0 +1,11 @@
1
+# Metadata information for this directory.
2
+#
3
+# For more information on DIR_METADATA files, see:
4
+# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/README.md
5
+#
6
+# For the schema of this file, see Metadata message:
7
+# https://source.chromium.org/chromium/infra/infra/+/master:go/src/infra/tools/dirmd/proto/dir_metadata.proto
8
+
9
+monorail {
10
+ component: "Infra>Client>V8"
11
+}
\ No newline at end of file
compiler/forget/packages/js-fuzzer/OWNERS
new
+7
@@ -0,0 +1,7 @@
1
+set noparent
2
+
3
+file:../../../INFRA_OWNERS
4
+
5
+msarms@chromium.org
6
+mslekova@chromium.org
7
+ochang@chromium.org
compiler/forget/packages/js-fuzzer/README.md
new
+122
@@ -0,0 +1,122 @@
1
+# JS-Fuzzer
2
+
3
+Javascript fuzzer for stand-alone shells like D8, Chakra, JSC or Spidermonkey.
4
+
5
+Original author: Oliver Chang
6
+
7
+# Building
8
+
9
+This fuzzer may require versions of node that are newer than available on
10
+ClusterFuzz, so we use [pkg](https://github.com/zeit/pkg) to create a self
11
+contained binary) out of this.
12
+
13
+## Prereqs
14
+You need to intall nodejs and npm. Run `npm install` in this directory.
15
+
16
+## Fuzzing DB
17
+This fuzzer requires a fuzzing DB. To build one, get the latest `web_tests.zip`
18
+from [gs://clusterfuzz-data/web_tests.zip](
19
+https://storage.cloud.google.com/clusterfuzz-data/web_tests.zip) and unzip it
20
+(note https://crbug.com/v8/10891 for making this data publicly available).
21
+Then run:
22
+
23
+```bash
24
+$ mkdir db
25
+$ node build_db.js -i /path/to/web_tests -o db chakra v8 spidermonkey WebKit/JSTests
26
+```
27
+
28
+This may take a while. Optionally test the fuzzing DB with:
29
+
30
+```bash
31
+$ node test_db.js -i db
32
+```
33
+
34
+## Building fuzzer
35
+Then, to build the fuzzer,
36
+```bash
37
+$ ./node_modules/.bin/pkg -t node10-linux-x64 .
38
+```
39
+
40
+Replace "linux" with either "win" or "macos" for those platforms.
41
+
42
+This builds a binary named `ochang_js_fuzzer` for Linux / macOS OR
43
+`ochang_js_fuzzer.exe` for Windows.
44
+
45
+## Packaging
46
+Use `./package.sh`, `./package.sh win` or `./package.sh macos` to build and
47
+create the `output.zip` archive or use these raw commands:
48
+```bash
49
+$ mkdir output
50
+$ cd output
51
+$ ln -s ../db db
52
+$ ln -s ../ochang_js_fuzzer run
53
+$ zip -r /path/output.zip *
54
+```
55
+
56
+**NOTE**: Add `.exe` to `ochang_js_fuzzer` and `run` filename above if archiving
57
+for Windows platform.
58
+
59
+# Development
60
+
61
+Run the tests with:
62
+
63
+```bash
64
+$ npm test
65
+```
66
+
67
+When test expectations change, generate them with:
68
+
69
+```bash
70
+$ GENERATE=1 npm test
71
+```
72
+
73
+# Generating exceptional configurations
74
+
75
+Tests that fail to parse or show very bad performance can be automatically
76
+skipped or soft-skipped with the following script (takes >1h):
77
+
78
+```bash
79
+$ WEB_TESTS=/path/to/web_tests OUTPUT=/path/to/output/folder ./gen_exceptions.sh
80
+```
81
+
82
+# Experimenting (limited to differential fuzzing)
83
+
84
+To locally evaluate the fuzzer, setup a work directory as follows:
85
+
86
+```bash
87
+$ workdir/
88
+$ workdir/app_dir
89
+$ workdir/fuzzer
90
+$ workdir/input
91
+$ workdir/output
92
+```
93
+
94
+The `app_dir` folder can be a symlink or should contain the bundled
95
+version of `d8` with all files required for execution.
96
+Copy the packaged `ochang_js_fuzzer` executable and the `db` folder
97
+to the `fuzzer` directory or use a symlink.
98
+The `input` directory is the root folder of the corpus, i.e. pointing
99
+to the unzipped data of `gs://clusterfuzz-data/web_tests.zip`.
100
+The `output` directory is expected to be empty. It'll contain all
101
+output of the fuzzing session. Start the experiments with:
102
+
103
+```bash
104
+$ # Around ~40000 corresponds to 24h of fuzzing on a workstation.
105
+$ NUM_RUNS = 40000
106
+$ python tools/workbench.py $NUM_RUNS
107
+```
108
+
109
+You can check current stats with:
110
+
111
+```bash
112
+$ cat workdir/output/stats.json | python -m json.tool
113
+```
114
+
115
+When failures are found, you can forge minimization command lines with:
116
+
117
+```bash
118
+$ MINIMIZER_PATH = path/to/minimizer
119
+$ python tools/minimize.py $MINIMIZER_PATH
120
+```
121
+
122
+The path should point to a local checkout of the [minimizer](https://chrome-internal.googlesource.com/chrome/tools/clusterfuzz/+/refs/heads/master/src/python/bot/minimizer/).
compiler/forget/packages/js-fuzzer/build_db.js
new
+65
@@ -0,0 +1,65 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Collect JS nodes.
7
+ */
8
+
9
+const program = require('commander');
10
+
11
+const corpus = require('./corpus.js');
12
+const db = require('./db.js');
13
+const path = require('path');
14
+
15
+const sourceHelpers = require('./source_helpers.js');
16
+
17
+function main() {
18
+ Error.stackTraceLimit = Infinity;
19
+
20
+ program
21
+ .version('0.0.1')
22
+ .option('-i, --input_dir <path>', 'Input directory.')
23
+ .option('-o, --output_dir <path>', 'Output directory.')
24
+ .parse(process.argv);
25
+
26
+ if (!program.args.length) {
27
+ console.log('Need to specify corpora.');
28
+ return;
29
+ }
30
+
31
+ if (!program.output_dir) {
32
+ console.log('Need to specify output dir.');
33
+ return;
34
+ }
35
+
36
+ const mutateDb = new db.MutateDbWriter(program.output_dir);
37
+
38
+ const inputDir = path.resolve(program.input_dir);
39
+ for (const corpusName of program.args) {
40
+ const curCorpus = new corpus.Corpus(inputDir, corpusName);
41
+ for (const relPath of curCorpus.relFiles()) {
42
+ let source;
43
+ try {
44
+ source = sourceHelpers.loadSource(inputDir, relPath);
45
+ } catch (e) {
46
+ console.log(e);
47
+ continue;
48
+ }
49
+
50
+ if (!source) {
51
+ continue;
52
+ }
53
+
54
+ try{
55
+ mutateDb.process(source);
56
+ } catch (e) {
57
+ console.log(e);
58
+ }
59
+ }
60
+ }
61
+
62
+ mutateDb.writeIndex();
63
+}
64
+
65
+main();
compiler/forget/packages/js-fuzzer/corpus.js
new
+141
@@ -0,0 +1,141 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Corpus
7
+ */
8
+
9
+const program = require('commander');
10
+const fs = require('fs');
11
+const path = require('path');
12
+
13
+const exceptions = require('./exceptions.js');
14
+const random = require('./random.js');
15
+const sourceHelpers = require('./source_helpers.js');
16
+
17
+function* walkDirectory(directory, filter) {
18
+ // Generator for recursively walk a directory.
19
+ for (const filePath of fs.readdirSync(directory)) {
20
+ const currentPath = path.join(directory, filePath);
21
+ const stat = fs.lstatSync(currentPath);
22
+ if (stat.isFile()) {
23
+ if (!filter || filter(currentPath)) {
24
+ yield currentPath;
25
+ }
26
+ continue;
27
+ }
28
+
29
+ if (stat.isDirectory()) {
30
+ for (let childFilePath of walkDirectory(currentPath, filter)) {
31
+ yield childFilePath;
32
+ }
33
+ }
34
+ }
35
+}
36
+
37
+class Corpus {
38
+ // Input corpus.
39
+ constructor(inputDir, corpusName, extraStrict=false) {
40
+ this.inputDir = inputDir;
41
+ this.extraStrict = extraStrict;
42
+
43
+ // Filter for permitted JS files.
44
+ function isPermittedJSFile(absPath) {
45
+ return (absPath.endsWith('.js') &&
46
+ !exceptions.isTestSkippedAbs(absPath));
47
+ }
48
+
49
+ // Cache relative paths of all files in corpus.
50
+ this.skippedFiles = [];
51
+ this.softSkippedFiles = [];
52
+ this.permittedFiles = [];
53
+ const directory = path.join(inputDir, corpusName);
54
+ for (const absPath of walkDirectory(directory, isPermittedJSFile)) {
55
+ const relPath = path.relative(this.inputDir, absPath);
56
+ if (exceptions.isTestSkippedRel(relPath)) {
57
+ this.skippedFiles.push(relPath);
58
+ } else if (exceptions.isTestSoftSkippedAbs(absPath) ||
59
+ exceptions.isTestSoftSkippedRel(relPath)) {
60
+ this.softSkippedFiles.push(relPath);
61
+ } else {
62
+ this.permittedFiles.push(relPath);
63
+ }
64
+ }
65
+ random.shuffle(this.softSkippedFiles);
66
+ random.shuffle(this.permittedFiles);
67
+ }
68
+
69
+ // Relative paths of all files in corpus.
70
+ *relFiles() {
71
+ for (const relPath of this.permittedFiles) {
72
+ yield relPath;
73
+ }
74
+ for (const relPath of this.softSkippedFiles) {
75
+ yield relPath;
76
+ }
77
+ }
78
+
79
+ // Relative paths of all files in corpus including generated skipped.
80
+ *relFilesForGenSkipped() {
81
+ for (const relPath of this.relFiles()) {
82
+ yield relPath;
83
+ }
84
+ for (const relPath of this.skippedFiles) {
85
+ yield relPath;
86
+ }
87
+ }
88
+
89
+ /**
90
+ * Returns "count" relative test paths, randomly selected from soft-skipped
91
+ * and permitted files. Permitted files have a 4 times higher chance to
92
+ * be chosen.
93
+ */
94
+ getRandomTestcasePaths(count) {
95
+ return random.twoBucketSample(
96
+ this.softSkippedFiles, this.permittedFiles, 4, count);
97
+ }
98
+
99
+ loadTestcase(relPath, strict, label) {
100
+ const start = Date.now();
101
+ try {
102
+ const source = sourceHelpers.loadSource(this.inputDir, relPath, strict);
103
+ if (program.verbose) {
104
+ const duration = Date.now() - start;
105
+ console.log(`Parsing ${relPath} ${label} took ${duration} ms.`);
106
+ }
107
+ return source;
108
+ } catch (e) {
109
+ console.log(`WARNING: failed to ${label} parse ${relPath}`);
110
+ console.log(e);
111
+ }
112
+ return undefined;
113
+ }
114
+
115
+ *loadTestcases(relPaths) {
116
+ for (const relPath of relPaths) {
117
+ if (this.extraStrict) {
118
+ // When re-generating the files marked sloppy, we additionally test if
119
+ // the file parses in strict mode.
120
+ this.loadTestcase(relPath, true, 'strict');
121
+ }
122
+ const source = this.loadTestcase(relPath, false, 'sloppy');
123
+ if (source) {
124
+ yield source;
125
+ }
126
+ }
127
+ }
128
+
129
+ getRandomTestcases(count) {
130
+ return Array.from(this.loadTestcases(this.getRandomTestcasePaths(count)));
131
+ }
132
+
133
+ getAllTestcases() {
134
+ return this.loadTestcases(this.relFilesForGenSkipped());
135
+ }
136
+}
137
+
138
+module.exports = {
139
+ Corpus: Corpus,
140
+ walkDirectory: walkDirectory,
141
+}
compiler/forget/packages/js-fuzzer/db.js
new
+485
@@ -0,0 +1,485 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Mutation Db.
7
+ */
8
+
9
+const crypto = require('crypto');
10
+const fs = require('fs');
11
+const fsPath = require('path');
12
+
13
+const babelGenerator = require('@babel/generator').default;
14
+const babelTemplate = require('@babel/template').default;
15
+const babelTraverse = require('@babel/traverse').default;
16
+const babelTypes = require('@babel/types');
17
+const globals = require('globals');
18
+
19
+const random = require('./random.js');
20
+const sourceHelpers = require('./source_helpers.js');
21
+
22
+const globalIdentifiers = new Set(Object.keys(globals.builtin));
23
+const propertyNames = new Set([
24
+ // Parsed from https://github.com/tc39/ecma262/blob/master/spec.html
25
+ 'add',
26
+ 'anchor',
27
+ 'apply',
28
+ 'big',
29
+ 'bind',
30
+ 'blink',
31
+ 'bold',
32
+ 'buffer',
33
+ 'byteLength',
34
+ 'byteOffset',
35
+ 'BYTES_PER_ELEMENT',
36
+ 'call',
37
+ 'catch',
38
+ 'charAt',
39
+ 'charCodeAt',
40
+ 'clear',
41
+ 'codePointAt',
42
+ 'compile',
43
+ 'concat',
44
+ 'constructor',
45
+ 'copyWithin',
46
+ '__defineGetter__',
47
+ '__defineSetter__',
48
+ 'delete',
49
+ 'endsWith',
50
+ 'entries',
51
+ 'every',
52
+ 'exec',
53
+ 'fill',
54
+ 'filter',
55
+ 'find',
56
+ 'findIndex',
57
+ 'fixed',
58
+ 'flags',
59
+ 'fontcolor',
60
+ 'fontsize',
61
+ 'forEach',
62
+ 'get',
63
+ 'getDate',
64
+ 'getDay',
65
+ 'getFloat32',
66
+ 'getFloat64',
67
+ 'getFullYear',
68
+ 'getHours',
69
+ 'getInt16',
70
+ 'getInt32',
71
+ 'getInt8',
72
+ 'getMilliseconds',
73
+ 'getMinutes',
74
+ 'getMonth',
75
+ 'getSeconds',
76
+ 'getTime',
77
+ 'getTimezoneOffset',
78
+ 'getUint16',
79
+ 'getUint32',
80
+ 'getUint8',
81
+ 'getUTCDate',
82
+ 'getUTCDay',
83
+ 'getUTCFullYear',
84
+ 'getUTCHours',
85
+ 'getUTCMilliseconds',
86
+ 'getUTCMinutes',
87
+ 'getUTCMonth',
88
+ 'getUTCSeconds',
89
+ 'getYear',
90
+ 'global',
91
+ 'has',
92
+ 'hasInstance',
93
+ 'hasOwnProperty',
94
+ 'ignoreCase',
95
+ 'includes',
96
+ 'indexOf',
97
+ 'isConcatSpreadable',
98
+ 'isPrototypeOf',
99
+ 'italics',
100
+ 'iterator',
101
+ 'join',
102
+ 'keys',
103
+ 'lastIndexOf',
104
+ 'length',
105
+ 'link',
106
+ 'localeCompare',
107
+ '__lookupGetter__',
108
+ '__lookupSetter__',
109
+ 'map',
110
+ 'match',
111
+ 'match',
112
+ 'message',
113
+ 'multiline',
114
+ 'name',
115
+ 'next',
116
+ 'normalize',
117
+ 'padEnd',
118
+ 'padStart',
119
+ 'pop',
120
+ 'propertyIsEnumerable',
121
+ '__proto__',
122
+ 'prototype',
123
+ 'push',
124
+ 'reduce',
125
+ 'reduceRight',
126
+ 'repeat',
127
+ 'replace',
128
+ 'replace',
129
+ 'return',
130
+ 'reverse',
131
+ 'search',
132
+ 'search',
133
+ 'set',
134
+ 'set',
135
+ 'setDate',
136
+ 'setFloat32',
137
+ 'setFloat64',
138
+ 'setFullYear',
139
+ 'setHours',
140
+ 'setInt16',
141
+ 'setInt32',
142
+ 'setInt8',
143
+ 'setMilliseconds',
144
+ 'setMinutes',
145
+ 'setMonth',
146
+ 'setSeconds',
147
+ 'setTime',
148
+ 'setUint16',
149
+ 'setUint32',
150
+ 'setUint8',
151
+ 'setUTCDate',
152
+ 'setUTCFullYear',
153
+ 'setUTCHours',
154
+ 'setUTCMilliseconds',
155
+ 'setUTCMinutes',
156
+ 'setUTCMonth',
157
+ 'setUTCSeconds',
158
+ 'setYear',
159
+ 'shift',
160
+ 'size',
161
+ 'slice',
162
+ 'slice',
163
+ 'small',
164
+ 'some',
165
+ 'sort',
166
+ 'source',
167
+ 'species',
168
+ 'splice',
169
+ 'split',
170
+ 'split',
171
+ 'startsWith',
172
+ 'sticky',
173
+ 'strike',
174
+ 'sub',
175
+ 'subarray',
176
+ 'substr',
177
+ 'substring',
178
+ 'sup',
179
+ 'test',
180
+ 'then',
181
+ 'throw',
182
+ 'toDateString',
183
+ 'toExponential',
184
+ 'toFixed',
185
+ 'toGMTString',
186
+ 'toISOString',
187
+ 'toJSON',
188
+ 'toLocaleDateString',
189
+ 'toLocaleLowerCase',
190
+ 'toLocaleString',
191
+ 'toLocaleTimeString',
192
+ 'toLocaleUpperCase',
193
+ 'toLowerCase',
194
+ 'toPrecision',
195
+ 'toPrimitive',
196
+ 'toString',
197
+ 'toStringTag',
198
+ 'toTimeString',
199
+ 'toUpperCase',
200
+ 'toUTCString',
201
+ 'trim',
202
+ 'unicode',
203
+ 'unscopables',
204
+ 'unshift',
205
+ 'valueOf',
206
+ 'values',
207
+]);
208
+
209
+const MAX_DEPENDENCIES = 2;
210
+
211
+class Expression {
212
+ constructor(type, source, isStatement, originalPath,
213
+ dependencies, needsSuper) {
214
+ this.type = type;
215
+ this.source = source;
216
+ this.isStatement = isStatement;
217
+ this.originalPath = originalPath;
218
+ this.dependencies = dependencies;
219
+ this.needsSuper = needsSuper;
220
+ }
221
+}
222
+
223
+function dedupKey(expression) {
224
+ if (!expression.dependencies) {
225
+ return expression.source;
226
+ }
227
+
228
+ let result = expression.source;
229
+ for (let dependency of expression.dependencies) {
230
+ result = result.replace(new RegExp(dependency, 'g'), 'ID');
231
+ }
232
+
233
+ return result;
234
+}
235
+
236
+function _markSkipped(path) {
237
+ while (path) {
238
+ path.node.__skipped = true;
239
+ path = path.parentPath;
240
+ }
241
+}
242
+
243
+/**
244
+ * Returns true if an expression can be applied or false otherwise.
245
+ */
246
+function isValid(expression) {
247
+ const expressionTemplate = babelTemplate(
248
+ expression.source,
249
+ sourceHelpers.BABYLON_REPLACE_VAR_OPTIONS);
250
+
251
+ const dependencies = {};
252
+ if (expression.dependencies) {
253
+ for (const dependency of expression.dependencies) {
254
+ dependencies[dependency] = babelTypes.identifier('__v_0');
255
+ }
256
+ }
257
+
258
+ try {
259
+ expressionTemplate(dependencies);
260
+ } catch (e) {
261
+ return false;
262
+ }
263
+ return true;
264
+}
265
+
266
+class MutateDbWriter {
267
+ constructor(outputDir) {
268
+ this.seen = new Set();
269
+ this.outputDir = fsPath.resolve(outputDir);
270
+ this.index = {
271
+ statements: [],
272
+ superStatements: [],
273
+ all: [],
274
+ };
275
+ }
276
+
277
+ process(source) {
278
+ let self = this;
279
+
280
+ let varIndex = 0;
281
+
282
+ // First pass to collect dependency information.
283
+ babelTraverse(source.ast, {
284
+ Super(path) {
285
+ while (path) {
286
+ path.node.__needsSuper = true;
287
+ path = path.parentPath;
288
+ }
289
+ },
290
+
291
+ YieldExpression(path) {
292
+ // Don't include yield expressions in DB.
293
+ _markSkipped(path);
294
+ },
295
+
296
+ Identifier(path) {
297
+ if (globalIdentifiers.has(path.node.name) &&
298
+ path.node.name != 'eval') {
299
+ // Global name.
300
+ return;
301
+ }
302
+
303
+ if (propertyNames.has(path.node.name) &&
304
+ path.parentPath.isMemberExpression() &&
305
+ path.parentKey !== 'object') {
306
+ // Builtin property name.
307
+ return;
308
+ }
309
+
310
+ let binding = path.scope.getBinding(path.node.name);
311
+ if (!binding) {
312
+ // Unknown dependency. Don't handle this.
313
+ _markSkipped(path);
314
+ return;
315
+ }
316
+
317
+ let newName;
318
+ if (path.node.name.startsWith('VAR_')) {
319
+ newName = path.node.name;
320
+ } else if (babelTypes.isFunctionDeclaration(binding.path.node) ||
321
+ babelTypes.isFunctionExpression(binding.path.node) ||
322
+ babelTypes.isDeclaration(binding.path.node) ||
323
+ babelTypes.isFunctionExpression(binding.path.node)) {
324
+ // Unknown dependency. Don't handle this.
325
+ _markSkipped(path);
326
+ return;
327
+ } else {
328
+ newName = 'VAR_' + varIndex++;
329
+ path.scope.rename(path.node.name, newName);
330
+ }
331
+
332
+ // Mark all parents as having a dependency.
333
+ while (path) {
334
+ path.node.__idDependencies = path.node.__idDependencies || [];
335
+ if (path.node.__idDependencies.length <= MAX_DEPENDENCIES) {
336
+ path.node.__idDependencies.push(newName);
337
+ }
338
+ path = path.parentPath;
339
+ }
340
+ }
341
+ });
342
+
343
+ babelTraverse(source.ast, {
344
+ Expression(path) {
345
+ if (!path.parentPath.isExpressionStatement()) {
346
+ return;
347
+ }
348
+
349
+ if (path.node.__skipped ||
350
+ (path.node.__idDependencies &&
351
+ path.node.__idDependencies.length > MAX_DEPENDENCIES)) {
352
+ return;
353
+ }
354
+
355
+ if (path.isIdentifier() || path.isMemberExpression() ||
356
+ path.isConditionalExpression() ||
357
+ path.isBinaryExpression() || path.isDoExpression() ||
358
+ path.isLiteral() ||
359
+ path.isObjectExpression() || path.isArrayExpression()) {
360
+ // Skip:
361
+ // - Identifiers.
362
+ // - Member expressions (too many and too context dependent).
363
+ // - Conditional expressions (too many and too context dependent).
364
+ // - Binary expressions (too many).
365
+ // - Literals (too many).
366
+ // - Object/array expressions (too many).
367
+ return;
368
+ }
369
+
370
+ if (path.isAssignmentExpression()) {
371
+ if (!babelTypes.isMemberExpression(path.node.left)) {
372
+ // Skip assignments that aren't to properties.
373
+ return;
374
+ }
375
+
376
+ if (babelTypes.isIdentifier(path.node.left.object)) {
377
+ if (babelTypes.isNumericLiteral(path.node.left.property)) {
378
+ // Skip VAR[\d+] = ...;
379
+ // There are too many and they generally aren't very useful.
380
+ return;
381
+ }
382
+
383
+ if (babelTypes.isStringLiteral(path.node.left.property) &&
384
+ !propertyNames.has(path.node.left.property.value)) {
385
+ // Skip custom properties. e.g.
386
+ // VAR["abc"] = ...;
387
+ // There are too many and they generally aren't very useful.
388
+ return;
389
+ }
390
+ }
391
+ }
392
+
393
+ if (path.isCallExpression() &&
394
+ babelTypes.isIdentifier(path.node.callee) &&
395
+ !globalIdentifiers.has(path.node.callee.name)) {
396
+ // Skip VAR(...) calls since there's too much context we're missing.
397
+ return;
398
+ }
399
+
400
+ if (path.isUnaryExpression() && path.node.operator == '-') {
401
+ // Skip -... since there are too many.
402
+ return;
403
+ }
404
+
405
+ // Make the template.
406
+ let generated = babelGenerator(path.node, { concise: true }).code;
407
+ let expression = new Expression(
408
+ path.node.type,
409
+ generated,
410
+ path.parentPath.isExpressionStatement(),
411
+ source.relPath,
412
+ path.node.__idDependencies,
413
+ Boolean(path.node.__needsSuper));
414
+
415
+ // Try to de-dupe similar expressions.
416
+ let key = dedupKey(expression);
417
+ if (self.seen.has(key)) {
418
+ return;
419
+ }
420
+
421
+ // Test results.
422
+ if (!isValid(expression)) {
423
+ return;
424
+ }
425
+
426
+ // Write results.
427
+ let dirPath = fsPath.join(self.outputDir, expression.type);
428
+ if (!fs.existsSync(dirPath)) {
429
+ fs.mkdirSync(dirPath);
430
+ }
431
+
432
+ let sha1sum = crypto.createHash('sha1');
433
+ sha1sum.update(key);
434
+
435
+ let filePath = fsPath.join(dirPath, sha1sum.digest('hex') + '.json');
436
+ fs.writeFileSync(filePath, JSON.stringify(expression));
437
+
438
+ let relPath = fsPath.relative(self.outputDir, filePath);
439
+
440
+ // Update index.
441
+ self.seen.add(key);
442
+ self.index.all.push(relPath);
443
+
444
+ if (expression.needsSuper) {
445
+ self.index.superStatements.push(relPath);
446
+ } else {
447
+ self.index.statements.push(relPath);
448
+ }
449
+ }
450
+ });
451
+ }
452
+
453
+ writeIndex() {
454
+ fs.writeFileSync(
455
+ fsPath.join(this.outputDir, 'index.json'),
456
+ JSON.stringify(this.index));
457
+ }
458
+}
459
+
460
+class MutateDb {
461
+ constructor(outputDir) {
462
+ this.outputDir = fsPath.resolve(outputDir);
463
+ this.index = JSON.parse(
464
+ fs.readFileSync(fsPath.join(outputDir, 'index.json'), 'utf-8'));
465
+ }
466
+
467
+ getRandomStatement({canHaveSuper=false} = {}) {
468
+ let choices;
469
+ if (canHaveSuper) {
470
+ choices = random.randInt(0, 1) ?
471
+ this.index.all : this.index.superStatements;
472
+ } else {
473
+ choices = this.index.statements;
474
+ }
475
+
476
+ let path = fsPath.join(
477
+ this.outputDir, choices[random.randInt(0, choices.length - 1)]);
478
+ return JSON.parse(fs.readFileSync(path), 'utf-8');
479
+ }
480
+}
481
+
482
+module.exports = {
483
+ MutateDb: MutateDb,
484
+ MutateDbWriter: MutateDbWriter,
485
+}
compiler/forget/packages/js-fuzzer/differential_script_mutator.js
new
+168
@@ -0,0 +1,168 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Script mutator for differential fuzzing.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const fs = require('fs');
13
+const path = require('path');
14
+
15
+const common = require('./mutators/common.js');
16
+const random = require('./random.js');
17
+const sourceHelpers = require('./source_helpers.js');
18
+
19
+const { filterDifferentialFuzzFlags } = require('./exceptions.js');
20
+const { DifferentialFuzzMutator, DifferentialFuzzSuppressions } = require(
21
+ './mutators/differential_fuzz_mutator.js');
22
+const { ScriptMutator } = require('./script_mutator.js');
23
+
24
+
25
+const USE_ORIGINAL_FLAGS_PROB = 0.2;
26
+
27
+/**
28
+ * Randomly chooses a configuration from experiments. The configuration
29
+ * parameters are expected to be passed from a bundled V8 build. Constraints
30
+ * mentioned below are enforced by PRESUBMIT checks on the V8 side.
31
+ *
32
+ * @param {Object[]} experiments List of tuples (probability, first config name,
33
+ * second config name, second d8 name). The probabilities are integers in
34
+ * [0,100]. We assume the sum of all probabilities is 100.
35
+ * @param {Object[]} additionalFlags List of tuples (probability, flag strings).
36
+ * Probability is in [0,1).
37
+ * @return {string[]} List of flags for v8_foozzie.py.
38
+ */
39
+function chooseRandomFlags(experiments, additionalFlags) {
40
+ // Add additional flags to second config based on experiment percentages.
41
+ const extra_flags = [];
42
+ for (const [p, flags] of additionalFlags) {
43
+ if (random.choose(p)) {
44
+ for (const flag of flags.split(' ')) {
45
+ extra_flags.push('--second-config-extra-flags=' + flag);
46
+ }
47
+ }
48
+ }
49
+
50
+ // Calculate flags determining the experiment.
51
+ let acc = 0;
52
+ const threshold = random.random() * 100;
53
+ for (let [prob, first_config, second_config, second_d8] of experiments) {
54
+ acc += prob;
55
+ if (acc > threshold) {
56
+ return [
57
+ '--first-config=' + first_config,
58
+ '--second-config=' + second_config,
59
+ '--second-d8=' + second_d8,
60
+ ].concat(extra_flags);
61
+ }
62
+ }
63
+ // Unreachable.
64
+ assert(false);
65
+}
66
+
67
+function loadJSONFromBuild(name) {
68
+ assert(process.env.APP_DIR);
69
+ const fullPath = path.join(path.resolve(process.env.APP_DIR), name);
70
+ return JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
71
+}
72
+
73
+function hasMjsunit(dependencies) {
74
+ return dependencies.some(dep => dep.relPath.endsWith('mjsunit.js'));
75
+}
76
+
77
+function hasJSTests(dependencies) {
78
+ return dependencies.some(dep => dep.relPath.endsWith('jstest_stubs.js'));
79
+}
80
+
81
+class DifferentialScriptMutator extends ScriptMutator {
82
+ constructor(settings, db_path) {
83
+ super(settings, db_path);
84
+
85
+ // Mutators for differential fuzzing.
86
+ this.differential = [
87
+ new DifferentialFuzzSuppressions(settings),
88
+ new DifferentialFuzzMutator(settings),
89
+ ];
90
+
91
+ // Flag configurations from the V8 build directory.
92
+ this.experiments = loadJSONFromBuild('v8_fuzz_experiments.json');
93
+ this.additionalFlags = loadJSONFromBuild('v8_fuzz_flags.json');
94
+ }
95
+
96
+ /**
97
+ * Performes the high-level mutation and afterwards adds flags for the
98
+ * v8_foozzie.py harness.
99
+ */
100
+ mutateMultiple(inputs) {
101
+ const result = super.mutateMultiple(inputs);
102
+ const originalFlags = [];
103
+
104
+ // Keep original JS flags in some cases. Let the harness pass them to
105
+ // baseline _and_ comparison run.
106
+ if (random.choose(USE_ORIGINAL_FLAGS_PROB)) {
107
+ for (const flag of filterDifferentialFuzzFlags(result.flags)) {
108
+ originalFlags.push('--first-config-extra-flags=' + flag);
109
+ originalFlags.push('--second-config-extra-flags=' + flag);
110
+ }
111
+ }
112
+
113
+ // Add flags for the differnetial-fuzzing settings.
114
+ const fuzzFlags = chooseRandomFlags(this.experiments, this.additionalFlags);
115
+ result.flags = fuzzFlags.concat(originalFlags);
116
+ return result;
117
+ }
118
+
119
+ /**
120
+ * Mutatates a set of inputs.
121
+ *
122
+ * Additionally we prepare inputs by tagging each with the original source
123
+ * path for later printing. The mutated sources are post-processed by the
124
+ * differential-fuzz mutators, adding extra printing and other substitutions.
125
+ */
126
+ mutateInputs(inputs) {
127
+ inputs.forEach(input => common.setOriginalPath(input, input.relPath));
128
+
129
+ const result = super.mutateInputs(inputs);
130
+ this.differential.forEach(mutator => mutator.mutate(result));
131
+ return result;
132
+ }
133
+
134
+ /**
135
+ * Adds extra dependencies for differential fuzzing.
136
+ */
137
+ resolveDependencies(inputs) {
138
+ const dependencies = super.resolveDependencies(inputs);
139
+ // The suppression file neuters functions not working with differential
140
+ // fuzzing. It can also be used to temporarily silence some functionality
141
+ // leading to dupes of an active bug.
142
+ dependencies.push(
143
+ sourceHelpers.loadResource('differential_fuzz_suppressions.js'));
144
+ // Extra printing and tracking functionality.
145
+ dependencies.push(
146
+ sourceHelpers.loadResource('differential_fuzz_library.js'));
147
+ // Make Chakra tests print more.
148
+ dependencies.push(
149
+ sourceHelpers.loadResource('differential_fuzz_chakra.js'));
150
+
151
+ if (hasMjsunit(dependencies)) {
152
+ // Make V8 tests print more. We guard this as the functionality
153
+ // relies on mjsunit.js.
154
+ dependencies.push(sourceHelpers.loadResource('differential_fuzz_v8.js'));
155
+ }
156
+
157
+ if (hasJSTests(dependencies)) {
158
+ dependencies.push(
159
+ sourceHelpers.loadResource('differential_fuzz_jstest.js'));
160
+ }
161
+
162
+ return dependencies;
163
+ }
164
+}
165
+
166
+module.exports = {
167
+ DifferentialScriptMutator: DifferentialScriptMutator,
168
+};
compiler/forget/packages/js-fuzzer/exceptions.js
new
+256
@@ -0,0 +1,256 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Blacklists for fuzzer.
7
+ */
8
+
9
+'use strict';
10
+
11
+const fs = require('fs');
12
+const path = require('path');
13
+
14
+const random = require('./random.js');
15
+
16
+const {generatedSloppy, generatedSoftSkipped, generatedSkipped} = require(
17
+ './generated/exceptions.js');
18
+
19
+const SKIPPED_FILES = [
20
+ // Disabled for unexpected test behavior, specific to d8 shell.
21
+ 'd8-os.js',
22
+ 'd8-readbuffer.js',
23
+
24
+ // Passes JS flags.
25
+ 'd8-arguments.js',
26
+
27
+ // Slow tests or tests that are too large to be used as input.
28
+ /numops-fuzz-part.*.js/,
29
+ 'regexp-pcre.js',
30
+ 'unicode-test.js',
31
+ 'unicodelctest.js',
32
+ 'unicodelctest-no-optimization.js',
33
+
34
+ // Unsupported modules.
35
+ /^modules.*\.js/,
36
+
37
+ // Unsupported property escapes.
38
+ /^regexp-property-.*\.js/,
39
+
40
+ // Bad testcases that just loads a script that always throws errors.
41
+ 'regress-444805.js',
42
+ 'regress-crbug-489597.js',
43
+ 'regress-crbug-620253.js',
44
+
45
+ // Just recursively loads itself.
46
+ 'regress-8510.js',
47
+];
48
+
49
+const SKIPPED_DIRECTORIES = [
50
+ // Slow tests or tests that are too large to be used as input.
51
+ 'embenchen',
52
+ 'poppler',
53
+ 'sqlite',
54
+
55
+ // Causes lots of failures.
56
+ 'test262',
57
+
58
+ // Unavailable debug.Debug.
59
+ 'v8/test/debugger',
60
+ 'v8/test/inspector',
61
+
62
+ // Unsupported modules.
63
+ 'v8/test/js-perf-test/Modules',
64
+
65
+ // Contains tests expected to error out on parsing.
66
+ 'v8/test/message',
67
+
68
+ // Needs specific dependencies for load of various tests.
69
+ 'v8/test/mjsunit/tools',
70
+
71
+ // Unsupported e4x standard.
72
+ 'mozilla/data/e4x',
73
+
74
+ // Bails out fast without ReadableStream support.
75
+ 'spidermonkey/non262/ReadableStream',
76
+];
77
+
78
+// Files used with a lower probability.
79
+const SOFT_SKIPPED_FILES = [
80
+ // Tests with large binary content.
81
+ /^binaryen.*\.js/,
82
+
83
+ // Tests slow to parse.
84
+ // CrashTests:
85
+ /^jquery.*\.js/,
86
+ // Spidermonkey:
87
+ 'regress-308085.js',
88
+ 'regress-74474-002.js',
89
+ 'regress-74474-003.js',
90
+ // V8:
91
+ 'object-literal.js',
92
+];
93
+
94
+// Flags that lead to false positives or that are already passed by default.
95
+const DISALLOWED_FLAGS = [
96
+ // Disallowed because features prefixed with "experimental" are not
97
+ // stabilized yet and would cause too much noise when enabled.
98
+ /^--experimental-.*/,
99
+
100
+ // Disallowed due to noise. We explicitly add --harmony to job
101
+ // definitions, and all of these features are staged before launch.
102
+ /^--harmony-.*/,
103
+
104
+ // Disallowed because they are passed explicitly on the command line.
105
+ '--allow-natives-syntax',
106
+ '--debug-code',
107
+ '--harmony',
108
+ '--wasm-staging',
109
+ '--expose-gc',
110
+ '--expose_gc',
111
+ '--icu-data-file',
112
+ '--random-seed',
113
+
114
+ // Disallowed due to false positives.
115
+ '--check-handle-count',
116
+ '--correctness-fuzzer-suppressions',
117
+ '--expose-debug-as',
118
+ '--expose-natives-as',
119
+ '--expose-trigger-failure',
120
+ '--mock-arraybuffer-allocator',
121
+ 'natives', // Used in conjuction with --expose-natives-as.
122
+ /^--trace-path.*/,
123
+];
124
+
125
+// Flags only used with 25% probability.
126
+const LOW_PROB_FLAGS_PROB = 0.25;
127
+const LOW_PROB_FLAGS = [
128
+ // Flags that lead to slow test performance.
129
+ /^--gc-interval.*/,
130
+ /^--deopt-every-n-times.*/,
131
+];
132
+
133
+
134
+// Flags printing data, leading to false positives in differential fuzzing.
135
+const DISALLOWED_DIFFERENTIAL_FUZZ_FLAGS = [
136
+ /^--gc-interval.*/,
137
+ /^--perf.*/,
138
+ /^--print.*/,
139
+ /^--stress-runs.*/,
140
+ /^--trace.*/,
141
+ '--expose-externalize-string',
142
+ '--interpreted-frames-native-stack',
143
+ '--validate-asm',
144
+];
145
+
146
+const MAX_FILE_SIZE_BYTES = 128 * 1024; // 128KB
147
+const MEDIUM_FILE_SIZE_BYTES = 32 * 1024; // 32KB
148
+
149
+function _findMatch(iterable, candidate) {
150
+ for (const entry of iterable) {
151
+ if (typeof entry === 'string') {
152
+ if (entry === candidate) {
153
+ return true;
154
+ }
155
+ } else {
156
+ if (entry.test(candidate)) {
157
+ return true;
158
+ }
159
+ }
160
+ }
161
+
162
+ return false;
163
+}
164
+
165
+function _doesntMatch(iterable, candidate) {
166
+ return !_findMatch(iterable, candidate);
167
+}
168
+
169
+// Convert Windows path separators.
170
+function normalize(testPath) {
171
+ return path.normalize(testPath).replace(/\\/g, '/');
172
+}
173
+
174
+function isTestSkippedAbs(absPath) {
175
+ const basename = path.basename(absPath);
176
+ if (_findMatch(SKIPPED_FILES, basename)) {
177
+ return true;
178
+ }
179
+
180
+ const normalizedTestPath = normalize(absPath);
181
+ for (const entry of SKIPPED_DIRECTORIES) {
182
+ if (normalizedTestPath.includes(entry)) {
183
+ return true;
184
+ }
185
+ }
186
+
187
+ // Avoid OOM/hangs through huge inputs.
188
+ const stat = fs.statSync(absPath);
189
+ return (stat && stat.size >= MAX_FILE_SIZE_BYTES);
190
+}
191
+
192
+function isTestSkippedRel(relPath) {
193
+ return generatedSkipped.has(normalize(relPath));
194
+}
195
+
196
+// For testing.
197
+function getSoftSkipped() {
198
+ return SOFT_SKIPPED_FILES;
199
+}
200
+
201
+// For testing.
202
+function getGeneratedSoftSkipped() {
203
+ return generatedSoftSkipped;
204
+}
205
+
206
+// For testing.
207
+function getGeneratedSloppy() {
208
+ return generatedSloppy;
209
+}
210
+
211
+function isTestSoftSkippedAbs(absPath) {
212
+ const basename = path.basename(absPath);
213
+ if (_findMatch(this.getSoftSkipped(), basename)) {
214
+ return true;
215
+ }
216
+
217
+ // Graylist medium size files.
218
+ const stat = fs.statSync(absPath);
219
+ return (stat && stat.size >= MEDIUM_FILE_SIZE_BYTES);
220
+}
221
+
222
+function isTestSoftSkippedRel(relPath) {
223
+ return this.getGeneratedSoftSkipped().has(normalize(relPath));
224
+}
225
+
226
+function isTestSloppyRel(relPath) {
227
+ return this.getGeneratedSloppy().has(normalize(relPath));
228
+}
229
+
230
+function filterFlags(flags) {
231
+ return flags.filter(flag => {
232
+ return (
233
+ _doesntMatch(DISALLOWED_FLAGS, flag) &&
234
+ (_doesntMatch(LOW_PROB_FLAGS, flag) ||
235
+ random.choose(LOW_PROB_FLAGS_PROB)));
236
+ });
237
+}
238
+
239
+function filterDifferentialFuzzFlags(flags) {
240
+ return flags.filter(
241
+ flag => _doesntMatch(DISALLOWED_DIFFERENTIAL_FUZZ_FLAGS, flag));
242
+}
243
+
244
+
245
+module.exports = {
246
+ filterDifferentialFuzzFlags: filterDifferentialFuzzFlags,
247
+ filterFlags: filterFlags,
248
+ getGeneratedSoftSkipped: getGeneratedSoftSkipped,
249
+ getGeneratedSloppy: getGeneratedSloppy,
250
+ getSoftSkipped: getSoftSkipped,
251
+ isTestSkippedAbs: isTestSkippedAbs,
252
+ isTestSkippedRel: isTestSkippedRel,
253
+ isTestSoftSkippedAbs: isTestSoftSkippedAbs,
254
+ isTestSoftSkippedRel: isTestSoftSkippedRel,
255
+ isTestSloppyRel: isTestSloppyRel,
256
+}
compiler/forget/packages/js-fuzzer/foozzie_launcher.py
new
+49
@@ -0,0 +1,49 @@
1
+#!/usr/bin/env python3
2
+# Copyright 2020 the V8 project authors. All rights reserved.
3
+# Use of this source code is governed by a BSD-style license that can be
4
+# found in the LICENSE file.
5
+
6
+
7
+"""
8
+Launcher for the foozzie differential-fuzzing harness. Wraps foozzie
9
+with Python2 for backwards-compatibility when bisecting.
10
+
11
+Obsolete now after switching to Python3 entirely. We keep the launcher
12
+for a transition period.
13
+"""
14
+
15
+import os
16
+import re
17
+import shutil
18
+import subprocess
19
+import sys
20
+
21
+def find_harness_code(args):
22
+ for arg in args:
23
+ if arg.endswith('v8_foozzie.py'):
24
+ with open(arg) as f:
25
+ return f.read()
26
+ assert False, 'Foozzie harness not found'
27
+
28
+if __name__ == '__main__':
29
+ # In some cases or older versions, the python executable is passed as
30
+ # first argument. Let's be robust either way, with or without full
31
+ # path or version.
32
+ if re.match(r'.*python.*', sys.argv[1]):
33
+ args = sys.argv[2:]
34
+ else:
35
+ args = sys.argv[1:]
36
+
37
+ python_exe = 'python3'
38
+
39
+ # To ease bisection of really old bugs, attempt to use Python2 as long
40
+ # as it is supported. This enables bisection before the point where the
41
+ # harness switched to Python3.
42
+ script = find_harness_code(args)
43
+ use_python3 = script.startswith('#!/usr/bin/env python3')
44
+ if not use_python3 and shutil.which('python2'):
45
+ python_exe = 'python2'
46
+
47
+ process = subprocess.Popen([python_exe] + args)
48
+ process.communicate()
49
+ sys.exit(process.returncode)
compiler/forget/packages/js-fuzzer/gen_exceptions.js
new
+196
@@ -0,0 +1,196 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Generate exceptions from full corpus test report.
7
+ */
8
+
9
+const program = require('commander');
10
+
11
+const assert = require('assert');
12
+const babelGenerator = require('@babel/generator').default;
13
+const babelTemplate = require('@babel/template').default;
14
+const babelTypes = require('@babel/types');
15
+const fs = require('fs');
16
+const p = require('path');
17
+const prettier = require("prettier");
18
+
19
+const SPLIT_LINES_RE = /^.*([\n\r]+|$)/gm;
20
+const PARSE_RE = /^Parsing (.*) sloppy took (\d+) ms\.\n$/;
21
+const MUTATE_RE = /^Mutating (.*) took (\d+) ms\.\n$/;
22
+const PARSE_FAILED_RE = /^WARNING: failed to sloppy parse (.*)\n$/;
23
+const PARSE_STRICT_FAILED_RE = /^WARNING: failed to strict parse (.*)\n$/;
24
+const MUTATE_FAILED_RE = /^ERROR: Exception during mutate: (.*)\n$/;
25
+
26
+// Add tests matching error regexp to result array.
27
+function matchError(regexp, line, resultArray){
28
+ const match = line.match(regexp);
29
+ if (!match) return false;
30
+ const relPath = match[1];
31
+ assert(relPath);
32
+ resultArray.push(relPath);
33
+ return true;
34
+}
35
+
36
+// Sum up total duration of tests matching the duration regexp and
37
+// map test -> duration in result map.
38
+function matchDuration(regexp, line, resultMap){
39
+ const match = line.match(regexp);
40
+ if (!match) return false;
41
+ const relPath = match[1];
42
+ assert(relPath);
43
+ resultMap[relPath] = (resultMap[relPath] || 0) + parseInt(match[2]);
44
+ return true;
45
+}
46
+
47
+// Create lists of failed and slow tests from stdout of a fuzzer run.
48
+function processFuzzOutput(outputFile){
49
+ const text = fs.readFileSync(outputFile, 'utf-8');
50
+ const lines = text.match(SPLIT_LINES_RE);
51
+
52
+ const failedParse = [];
53
+ const failedParseStrict = [];
54
+ const failedMutate = [];
55
+ const durationsMap = {};
56
+
57
+ for (const line of lines) {
58
+ if (matchError(PARSE_FAILED_RE, line, failedParse))
59
+ continue;
60
+ if (matchError(PARSE_STRICT_FAILED_RE, line, failedParseStrict))
61
+ continue;
62
+ if (matchError(MUTATE_FAILED_RE, line, failedMutate))
63
+ continue;
64
+ if (matchDuration(PARSE_RE, line, durationsMap))
65
+ continue;
66
+ if (matchDuration(MUTATE_RE, line, durationsMap))
67
+ continue;
68
+ }
69
+
70
+ // Tuples (absPath, duration).
71
+ const total = Object.entries(durationsMap);
72
+ // Tuples (absPath, duration) with 2s < duration <= 10s.
73
+ const slow = total.filter(t => t[1] > 2000 && t[1] <= 10000);
74
+ // Tuples (absPath, duration) with 10s < duration.
75
+ const verySlow = total.filter(t => t[1] > 10000);
76
+
77
+ // Assert there's nothing horribly wrong with the results.
78
+ // We have at least 2500 tests in the output.
79
+ assert(total.length > 2500);
80
+ // No more than 5% parse/mutation errors.
81
+ assert(failedParse.length + failedMutate.length < total.length / 20);
82
+ // No more than 10% slow tests
83
+ assert(slow.length < total.length / 10);
84
+ // No more than 2% very slow tests.
85
+ assert(verySlow.length < total.length / 50);
86
+
87
+ // Sort everything.
88
+ failedParse.sort();
89
+ failedParseStrict.sort();
90
+ failedMutate.sort();
91
+
92
+ function slowestFirst(a, b) {
93
+ return b[1] - a[1];
94
+ }
95
+
96
+ slow.sort(slowestFirst);
97
+ verySlow.sort(slowestFirst);
98
+
99
+ return [failedParse, failedParseStrict, failedMutate, slow, verySlow];
100
+}
101
+
102
+// List of string literals of failed tests.
103
+function getLiteralsForFailed(leadingComment, failedList) {
104
+ const result = failedList.map(path => babelTypes.stringLiteral(path));
105
+ if (result.length) {
106
+ babelTypes.addComment(result[0], 'leading', leadingComment);
107
+ }
108
+ return result;
109
+}
110
+
111
+// List of string literals of slow tests with duration comments.
112
+function getLiteralsForSlow(leadingComment, slowList) {
113
+ const result = slowList.map(([path, duration]) => {
114
+ const literal = babelTypes.stringLiteral(path);
115
+ babelTypes.addComment(
116
+ literal, 'trailing', ` ${duration / 1000}s`, true);
117
+ return literal;
118
+ });
119
+ if (result.length) {
120
+ babelTypes.addComment(result[0], 'leading', leadingComment);
121
+ }
122
+ return result;
123
+}
124
+
125
+function main() {
126
+ program
127
+ .version('0.0.1')
128
+ .parse(process.argv);
129
+
130
+ if (!program.args.length) {
131
+ console.log('Need to specify stdout reports of fuzz runs.');
132
+ return;
133
+ }
134
+
135
+ let skipped = [];
136
+ let softSkipped = [];
137
+ let sloppy = [];
138
+ for (const outputFile of program.args) {
139
+ const [failedParse, failedParseStrict, failedMutate, slow, verySlow] = (
140
+ processFuzzOutput(outputFile));
141
+ const name = p.basename(outputFile, p.extname(outputFile));
142
+
143
+ // Skip tests that fail to parse/mutate or are very slow.
144
+ skipped = skipped.concat(getLiteralsForFailed(
145
+ ` Tests with parse errors from ${name} `, failedParse));
146
+ skipped = skipped.concat(getLiteralsForFailed(
147
+ ` Tests with mutation errors from ${name} `, failedMutate));
148
+ skipped = skipped.concat(getLiteralsForSlow(
149
+ ` Very slow tests from ${name} `, verySlow));
150
+
151
+ // Soft-skip slow but not very slow tests.
152
+ softSkipped = softSkipped.concat(getLiteralsForSlow(
153
+ ` Slow tests from ${name} `, slow));
154
+
155
+ // Mark sloppy tests.
156
+ sloppy = sloppy.concat(getLiteralsForFailed(
157
+ ` Tests requiring sloppy mode from ${name} `, failedParseStrict));
158
+ }
159
+
160
+ const fileTemplate = babelTemplate(`
161
+ /**
162
+ * @fileoverview Autogenerated exceptions. Created with gen_exceptions.js.
163
+ */
164
+
165
+ 'use strict';
166
+
167
+ const skipped = SKIPPED;
168
+
169
+ const softSkipped = SOFTSKIPPED;
170
+
171
+ const sloppy = SLOPPY;
172
+
173
+ module.exports = {
174
+ generatedSkipped: new Set(skipped),
175
+ generatedSoftSkipped: new Set(softSkipped),
176
+ generatedSloppy: new Set(sloppy),
177
+ }
178
+ `, {preserveComments: true});
179
+
180
+ const skippedArray = babelTypes.arrayExpression(skipped);
181
+ const softSkippedArray = babelTypes.arrayExpression(softSkipped);
182
+ const sloppyArray = babelTypes.arrayExpression(sloppy);
183
+
184
+ const statements = fileTemplate({
185
+ SKIPPED: skippedArray,
186
+ SOFTSKIPPED: softSkippedArray,
187
+ SLOPPY: sloppyArray,
188
+ });
189
+
190
+ const resultProgram = babelTypes.program(statements);
191
+ const code = babelGenerator(resultProgram, { comments: true }).code;
192
+ const prettyCode = prettier.format(code, { parser: "babel" });
193
+ fs.writeFileSync('generated/exceptions.js', prettyCode);
194
+}
195
+
196
+main();
compiler/forget/packages/js-fuzzer/gen_exceptions.sh
new
+12
@@ -0,0 +1,12 @@
1
+#!/bin/bash
2
+# Copyright 2020 the V8 project authors. All rights reserved.
3
+# Use of this source code is governed by a BSD-style license that can be
4
+# found in the LICENSE file.
5
+
6
+APP_NAME=d8 node run.js -i $WEB_TESTS -o $OUTPUT -z -v -e -c chakra > chakra.log
7
+APP_NAME=d8 node run.js -i $WEB_TESTS -o $OUTPUT -z -v -e -c v8 > v8.log
8
+APP_NAME=d8 node run.js -i $WEB_TESTS -o $OUTPUT -z -v -e -c spidermonkey > spidermonkey.log
9
+APP_NAME=d8 node run.js -i $WEB_TESTS -o $OUTPUT -z -v -e -c WebKit/JSTests > jstests.log
10
+APP_NAME=d8 node run.js -i $WEB_TESTS -o $OUTPUT -z -v -e -c CrashTests > crashtests.log
11
+
12
+node gen_exceptions.js v8.log spidermonkey.log chakra.log jstests.log crashtests.log
compiler/forget/packages/js-fuzzer/mutators/array_mutator.js
new
+115
@@ -0,0 +1,115 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Mutator for array expressions.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTypes = require('@babel/types');
12
+
13
+const common = require('./common.js');
14
+const mutator = require('./mutator.js');
15
+const random = require('../random.js');
16
+
17
+// Blueprint for choosing the maximum number of mutations. Bias towards
18
+// performing only one mutation.
19
+const MUTATION_CHOICES = [1, 1, 1, 1, 1, 2, 2, 2, 3];
20
+
21
+const MAX_ARRAY_LENGTH = 50;
22
+
23
+class ArrayMutator extends mutator.Mutator {
24
+ constructor(settings) {
25
+ super();
26
+ this.settings = settings;
27
+ }
28
+
29
+ get visitor() {
30
+ const thisMutator = this;
31
+
32
+ return {
33
+ ArrayExpression(path) {
34
+ const elements = path.node.elements;
35
+ if (!random.choose(thisMutator.settings.MUTATE_ARRAYS) ||
36
+ elements.length > MAX_ARRAY_LENGTH) {
37
+ return;
38
+ }
39
+
40
+ // Annotate array expression with the action taken, indicating
41
+ // if we also replaced elements.
42
+ function annotate(message, replace) {
43
+ if (replace) message += ' (replaced)';
44
+ thisMutator.annotate(path.node, message);
45
+ }
46
+
47
+ // Add or replace elements at a random index.
48
+ function randomSplice(replace, ...args) {
49
+ // Choose an index that's small enough to replace all desired items.
50
+ const index = random.randInt(0, elements.length - replace);
51
+ elements.splice(index, replace, ...args);
52
+ }
53
+
54
+ function duplicateElement(replace) {
55
+ const element = random.single(elements);
56
+ if (!element || common.isLargeNode(element)) {
57
+ return;
58
+ }
59
+ annotate('Duplicate an element', replace);
60
+ randomSplice(replace, babelTypes.cloneDeep(element));
61
+ }
62
+
63
+ function insertRandomValue(replace) {
64
+ annotate('Insert a random value', replace);
65
+ randomSplice(replace, common.randomValue(path));
66
+ }
67
+
68
+ function insertHole(replace) {
69
+ annotate('Insert a hole', replace);
70
+ randomSplice(replace, null);
71
+ }
72
+
73
+ function removeElements(count) {
74
+ annotate('Remove elements');
75
+ randomSplice(random.randInt(1, count));
76
+ }
77
+
78
+ function shuffle() {
79
+ annotate('Shuffle array');
80
+ random.shuffle(elements);
81
+ }
82
+
83
+ // Mutation options. Repeated mutations have a higher probability.
84
+ const mutations = [
85
+ () => duplicateElement(1),
86
+ () => duplicateElement(1),
87
+ () => duplicateElement(1),
88
+ () => duplicateElement(0),
89
+ () => duplicateElement(0),
90
+ () => insertRandomValue(1),
91
+ () => insertRandomValue(1),
92
+ () => insertRandomValue(0),
93
+ () => insertHole(1),
94
+ () => insertHole(0),
95
+ () => removeElements(1),
96
+ () => removeElements(elements.length),
97
+ shuffle,
98
+ ];
99
+
100
+ // Perform several mutations.
101
+ const count = random.single(MUTATION_CHOICES);
102
+ for (let i = 0; i < count; i++) {
103
+ random.single(mutations)();
104
+ }
105
+
106
+ // Don't recurse on nested arrays.
107
+ path.skip();
108
+ },
109
+ }
110
+ }
111
+}
112
+
113
+module.exports = {
114
+ ArrayMutator: ArrayMutator,
115
+};
compiler/forget/packages/js-fuzzer/mutators/common.js
new
+376
@@ -0,0 +1,376 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Common mutator utilities.
7
+ */
8
+
9
+const babelTemplate = require('@babel/template').default;
10
+const babelTypes = require('@babel/types');
11
+const babylon = require('@babel/parser');
12
+
13
+const sourceHelpers = require('../source_helpers.js');
14
+const random = require('../random.js');
15
+
16
+const INTERESTING_NUMBER_VALUES = [
17
+ -1, -0.0, 0, 1,
18
+
19
+ // Float values.
20
+ -0.000000000000001, 0.000000000000001,
21
+
22
+ // Special values.
23
+ NaN, +Infinity, -Infinity,
24
+
25
+ // Boundaries of int, signed, unsigned, SMI (near +/- 2^(30, 31, 32).
26
+ 0x03fffffff, 0x040000000, 0x040000001,
27
+ -0x03fffffff, -0x040000000, -0x040000001,
28
+ 0x07fffffff, 0x080000000, 0x080000001,
29
+ -0x07fffffff, -0x080000000, -0x080000001,
30
+ 0x0ffffffff, 0x100000000, 0x100000001,
31
+ -0x0ffffffff, -0x100000000, -0x100000001,
32
+
33
+ // Boundaries of maximum safe integer (near +/- 2^53).
34
+ 9007199254740990, 9007199254740991, 9007199254740992,
35
+ -9007199254740990, -9007199254740991, -9007199254740992,
36
+
37
+ // Boundaries of double.
38
+ 5e-324, 1.7976931348623157e+308,
39
+ -5e-324,-1.7976931348623157e+308,
40
+]
41
+
42
+const INTERESTING_NON_NUMBER_VALUES = [
43
+ // Simple arrays.
44
+ '[]',
45
+ 'Array(0x8000).fill("a")',
46
+
47
+ // Simple object.
48
+ '{}',
49
+ '{a: "foo", b: 10, c: {}}',
50
+
51
+ // Simple strings.
52
+ '"foo"',
53
+ '""',
54
+
55
+ // Simple regex.
56
+ '/0/',
57
+ '"/0/"',
58
+
59
+ // Simple symbol.
60
+ 'Symbol("foo")',
61
+
62
+ // Long string.
63
+ 'Array(0x8000).join("a")',
64
+
65
+ // Math.PI
66
+ 'Math.PI',
67
+
68
+ // Others.
69
+ 'false',
70
+ 'true',
71
+ 'undefined',
72
+ 'null',
73
+ 'this',
74
+ 'this[0]',
75
+ 'this[1]',
76
+
77
+ // Empty function.
78
+ '(function() {return 0;})',
79
+
80
+ // Objects with functions.
81
+ '({toString:function(){return "0";}})',
82
+ '({valueOf:function(){return 0;}})',
83
+ '({valueOf:function(){return "0";}})',
84
+
85
+ // Objects for primitive types created using new.
86
+ '(new Boolean(false))',
87
+ '(new Boolean(true))',
88
+ '(new String(""))',
89
+ '(new Number(0))',
90
+ '(new Number(-0))',
91
+]
92
+
93
+const LARGE_NODE_SIZE = 100;
94
+const MAX_ARGUMENT_COUNT = 10;
95
+
96
+function _identifier(identifier) {
97
+ return babelTypes.identifier(identifier);
98
+}
99
+
100
+function _numericLiteral(number) {
101
+ return babelTypes.numericLiteral(number);
102
+}
103
+
104
+function _unwrapExpressionStatement(value) {
105
+ if (babelTypes.isExpressionStatement(value)) {
106
+ return value.expression;
107
+ }
108
+
109
+ return value;
110
+}
111
+
112
+function isVariableIdentifier(name) {
113
+ return /__v_[0-9]+/.test(name);
114
+}
115
+
116
+function isFunctionIdentifier(name) {
117
+ return /__f_[0-9]+/.test(name);
118
+}
119
+
120
+function isInForLoopCondition(path) {
121
+ // Return whether if we're in the init/test/update parts of a for loop (but
122
+ // not the body). Mutating variables in the init/test/update will likely
123
+ // modify loop variables and cause infinite loops.
124
+ const forStatementChild = path.find(
125
+ p => p.parent && babelTypes.isForStatement(p.parent));
126
+
127
+ return (forStatementChild && forStatementChild.parentKey !== 'body');
128
+}
129
+
130
+function isInWhileLoop(path) {
131
+ // Return whether if we're in a while loop.
132
+ const whileStatement = path.find(p => babelTypes.isWhileStatement(p));
133
+ return Boolean(whileStatement);
134
+}
135
+
136
+function _availableIdentifiers(path, filter) {
137
+ // TODO(ochang): Consider globals that aren't declared with let/var etc.
138
+ const available = new Array();
139
+ const allBindings = path.scope.getAllBindings();
140
+ for (const key of Object.keys(allBindings)) {
141
+ if (!filter(key)) {
142
+ continue;
143
+ }
144
+
145
+ if (filter === isVariableIdentifier &&
146
+ path.willIMaybeExecuteBefore(allBindings[key].path)) {
147
+ continue;
148
+ }
149
+
150
+ available.push(_identifier(key));
151
+ }
152
+
153
+ return available;
154
+}
155
+
156
+function availableVariables(path) {
157
+ return _availableIdentifiers(path, isVariableIdentifier);
158
+}
159
+
160
+function availableFunctions(path) {
161
+ return _availableIdentifiers(path, isFunctionIdentifier);
162
+}
163
+
164
+function randomVariable(path) {
165
+ return random.single(availableVariables(path));
166
+}
167
+
168
+function randomFunction(path) {
169
+ return random.single(availableFunctions(path));
170
+}
171
+
172
+function randomSeed() {
173
+ return random.randInt(0, 2**20);
174
+}
175
+
176
+function randomObject(seed) {
177
+ if (seed === undefined) {
178
+ seed = randomSeed();
179
+ }
180
+
181
+ const template = babelTemplate('__getRandomObject(SEED)');
182
+ return template({
183
+ SEED: _numericLiteral(seed),
184
+ }).expression;
185
+}
186
+
187
+function randomProperty(identifier, seed) {
188
+ if (seed === undefined) {
189
+ seed = randomSeed();
190
+ }
191
+
192
+ const template = babelTemplate('__getRandomProperty(IDENTIFIER, SEED)');
193
+ return template({
194
+ IDENTIFIER: identifier,
195
+ SEED: _numericLiteral(seed),
196
+ }).expression;
197
+}
198
+
199
+function randomArguments(path) {
200
+ const numArgs = random.randInt(0, MAX_ARGUMENT_COUNT);
201
+ const args = [];
202
+
203
+ for (let i = 0; i < numArgs; i++) {
204
+ args.push(randomValue(path));
205
+ }
206
+
207
+ return args.map(_unwrapExpressionStatement);
208
+}
209
+
210
+function randomValue(path) {
211
+ const probability = random.random();
212
+
213
+ if (probability < 0.01) {
214
+ const randomFunc = randomFunction(path);
215
+ if (randomFunc) {
216
+ return randomFunc;
217
+ }
218
+ }
219
+
220
+ if (probability < 0.25) {
221
+ const randomVar = randomVariable(path);
222
+ if (randomVar) {
223
+ return randomVar;
224
+ }
225
+ }
226
+
227
+ if (probability < 0.5) {
228
+ return randomInterestingNumber();
229
+ }
230
+
231
+ if (probability < 0.75) {
232
+ return randomInterestingNonNumber();
233
+ }
234
+
235
+ return randomObject();
236
+}
237
+
238
+function callRandomFunction(path, identifier, seed) {
239
+ if (seed === undefined) {
240
+ seed = randomSeed();
241
+ }
242
+
243
+ let args = [
244
+ identifier,
245
+ _numericLiteral(seed)
246
+ ];
247
+
248
+ args = args.map(_unwrapExpressionStatement);
249
+ args = args.concat(randomArguments(path));
250
+
251
+ return babelTypes.callExpression(
252
+ babelTypes.identifier('__callRandomFunction'),
253
+ args);
254
+}
255
+
256
+function nearbyRandomNumber(value) {
257
+ const probability = random.random();
258
+
259
+ if (probability < 0.9) {
260
+ return _numericLiteral(value + random.randInt(-0x10, 0x10));
261
+ } else if (probability < 0.95) {
262
+ return _numericLiteral(value + random.randInt(-0x100, 0x100));
263
+ } else if (probability < 0.99) {
264
+ return _numericLiteral(value + random.randInt(-0x1000, 0x1000));
265
+ }
266
+
267
+ return _numericLiteral(value + random.randInt(-0x10000, 0x10000));
268
+}
269
+
270
+function randomInterestingNumber() {
271
+ const value = random.single(INTERESTING_NUMBER_VALUES);
272
+ if (random.choose(0.05)) {
273
+ return nearbyRandomNumber(value);
274
+ }
275
+ return _numericLiteral(value);
276
+}
277
+
278
+function randomInterestingNonNumber() {
279
+ return babylon.parseExpression(random.single(INTERESTING_NON_NUMBER_VALUES));
280
+}
281
+
282
+function concatFlags(inputs) {
283
+ const flags = new Set();
284
+ for (const input of inputs) {
285
+ for (const flag of input.flags || []) {
286
+ flags.add(flag);
287
+ }
288
+ }
289
+ return Array.from(flags.values());
290
+}
291
+
292
+function concatPrograms(inputs) {
293
+ // Concatentate programs.
294
+ const resultProgram = babelTypes.program([]);
295
+ const result = babelTypes.file(resultProgram, [], null);
296
+
297
+ for (const input of inputs) {
298
+ const ast = input.ast.program;
299
+ resultProgram.body = resultProgram.body.concat(ast.body);
300
+ resultProgram.directives = resultProgram.directives.concat(ast.directives);
301
+ }
302
+
303
+ // TODO(machenbach): Concat dependencies here as soon as they are cached.
304
+ const combined = new sourceHelpers.ParsedSource(
305
+ result, '', '', concatFlags(inputs));
306
+ // If any input file is sloppy, the combined result is sloppy.
307
+ combined.sloppy = inputs.some(input => input.isSloppy());
308
+ return combined;
309
+}
310
+
311
+function setSourceLoc(source, index, total) {
312
+ const noop = babelTypes.noop();
313
+ noop.__loc = index / total;
314
+ noop.__self = noop;
315
+ source.ast.program.body.unshift(noop);
316
+}
317
+
318
+function getSourceLoc(node) {
319
+ // Source location is invalid in cloned nodes.
320
+ if (node !== node.__self) {
321
+ return undefined;
322
+ }
323
+ return node.__loc;
324
+}
325
+
326
+function setOriginalPath(source, originalPath) {
327
+ const noop = babelTypes.noop();
328
+ noop.__path = originalPath;
329
+ noop.__self = noop;
330
+ source.ast.program.body.unshift(noop);
331
+}
332
+
333
+function getOriginalPath(node) {
334
+ // Original path is invalid in cloned nodes.
335
+ if (node !== node.__self) {
336
+ return undefined;
337
+ }
338
+ return node.__path;
339
+}
340
+
341
+// Estimate the size of a node in raw source characters.
342
+function isLargeNode(node) {
343
+ // Ignore array holes inserted by us (null) or previously cloned nodes
344
+ // (they have no start/end).
345
+ if (!node || node.start === undefined || node.end === undefined ) {
346
+ return false;
347
+ }
348
+ return node.end - node.start > LARGE_NODE_SIZE;
349
+}
350
+
351
+module.exports = {
352
+ callRandomFunction: callRandomFunction,
353
+ concatFlags: concatFlags,
354
+ concatPrograms: concatPrograms,
355
+ availableVariables: availableVariables,
356
+ availableFunctions: availableFunctions,
357
+ randomFunction: randomFunction,
358
+ randomVariable: randomVariable,
359
+ isInForLoopCondition: isInForLoopCondition,
360
+ isInWhileLoop: isInWhileLoop,
361
+ isLargeNode: isLargeNode,
362
+ isVariableIdentifier: isVariableIdentifier,
363
+ isFunctionIdentifier: isFunctionIdentifier,
364
+ nearbyRandomNumber: nearbyRandomNumber,
365
+ randomArguments: randomArguments,
366
+ randomInterestingNonNumber: randomInterestingNonNumber,
367
+ randomInterestingNumber: randomInterestingNumber,
368
+ randomObject: randomObject,
369
+ randomProperty: randomProperty,
370
+ randomSeed: randomSeed,
371
+ randomValue: randomValue,
372
+ getOriginalPath: getOriginalPath,
373
+ setOriginalPath: setOriginalPath,
374
+ getSourceLoc: getSourceLoc,
375
+ setSourceLoc: setSourceLoc,
376
+}
compiler/forget/packages/js-fuzzer/mutators/crossover_mutator.js
new
+85
@@ -0,0 +1,85 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Expression mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTemplate = require('@babel/template').default;
12
+
13
+const common = require('./common.js');
14
+const random = require('../random.js');
15
+const mutator = require('./mutator.js');
16
+const sourceHelpers = require('../source_helpers.js');
17
+
18
+class CrossOverMutator extends mutator.Mutator {
19
+ constructor(settings, db) {
20
+ super();
21
+ this.settings = settings;
22
+ this.db = db;
23
+ }
24
+
25
+ get visitor() {
26
+ const thisMutator = this;
27
+
28
+ return [{
29
+ ExpressionStatement(path) {
30
+ if (!random.choose(thisMutator.settings.MUTATE_CROSSOVER_INSERT)) {
31
+ return;
32
+ }
33
+
34
+ const canHaveSuper = Boolean(path.findParent(x => x.isClassMethod()));
35
+ const randomExpression = thisMutator.db.getRandomStatement(
36
+ {canHaveSuper: canHaveSuper});
37
+
38
+ // Insert the statement.
39
+ let toInsert = babelTemplate(
40
+ randomExpression.source,
41
+ sourceHelpers.BABYLON_REPLACE_VAR_OPTIONS);
42
+ const dependencies = {};
43
+
44
+ if (randomExpression.dependencies) {
45
+ const variables = common.availableVariables(path);
46
+ if (!variables.length) {
47
+ return;
48
+ }
49
+ for (const dependency of randomExpression.dependencies) {
50
+ dependencies[dependency] = random.single(variables);
51
+ }
52
+ }
53
+
54
+ try {
55
+ toInsert = toInsert(dependencies);
56
+ } catch (e) {
57
+ if (thisMutator.settings.testing) {
58
+ // Fail early in tests.
59
+ throw e;
60
+ }
61
+ console.log('ERROR: Failed to parse:', randomExpression.source);
62
+ console.log(e);
63
+ return;
64
+ }
65
+
66
+ thisMutator.annotate(
67
+ toInsert,
68
+ 'Crossover from ' + randomExpression.originalPath);
69
+
70
+ if (random.choose(0.5)) {
71
+ thisMutator.insertBeforeSkip(path, toInsert);
72
+ } else {
73
+ thisMutator.insertAfterSkip(path, toInsert);
74
+ }
75
+
76
+ path.skip();
77
+ },
78
+ }, {
79
+ }];
80
+ }
81
+}
82
+
83
+module.exports = {
84
+ CrossOverMutator: CrossOverMutator,
85
+};
compiler/forget/packages/js-fuzzer/mutators/differential_fuzz_mutator.js
new
+225
@@ -0,0 +1,225 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Mutator for differential fuzzing.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTemplate = require('@babel/template').default;
12
+const babelTypes = require('@babel/types');
13
+
14
+const common = require('./common.js');
15
+const mutator = require('./mutator.js');
16
+const random = require('../random.js');
17
+
18
+// Templates for various statements.
19
+const incCaught = babelTemplate('__caught++;');
20
+const printValue = babelTemplate('print(VALUE);');
21
+const printCaught = babelTemplate('print("Caught: " + __caught);');
22
+const printHash = babelTemplate('print("Hash: " + __hash);');
23
+const prettyPrint = babelTemplate('__prettyPrint(ID);');
24
+const prettyPrintExtra = babelTemplate('__prettyPrintExtra(ID);');
25
+
26
+// This section prefix is expected by v8_foozzie.py. Existing prefixes
27
+// (e.g. from CrashTests) are cleaned up with CLEANED_PREFIX.
28
+const SECTION_PREFIX = 'v8-foozzie source: ';
29
+const CLEANED_PREFIX = 'v***************e: ';
30
+
31
+/**
32
+ * Babel statement for calling deep printing from the fuzz library.
33
+ */
34
+function prettyPrintStatement(variable) {
35
+ return prettyPrint({ ID: babelTypes.cloneDeep(variable) });
36
+}
37
+
38
+/**
39
+ * As above, but using the "extra" variant, which will reduce printing
40
+ * after too many calls to prevent I/O flooding.
41
+ */
42
+function prettyPrintExtraStatement(variable) {
43
+ return prettyPrintExtra({ ID: babelTypes.cloneDeep(variable) });
44
+}
45
+
46
+/**
47
+ * Mutator for suppressing known and/or unfixable issues.
48
+ */
49
+class DifferentialFuzzSuppressions extends mutator.Mutator {
50
+ get visitor() {
51
+ let thisMutator = this;
52
+
53
+ return {
54
+ // Clean up strings containing the magic section prefix. Those can come
55
+ // e.g. from CrashTests and would confuse the deduplication in
56
+ // v8_foozzie.py.
57
+ StringLiteral(path) {
58
+ if (path.node.value.startsWith(SECTION_PREFIX)) {
59
+ const postfix = path.node.value.substring(SECTION_PREFIX.length);
60
+ path.node.value = CLEANED_PREFIX + postfix;
61
+ thisMutator.annotate(path.node, 'Replaced magic string');
62
+ }
63
+ },
64
+ // Known precision differences: https://crbug.com/1063568
65
+ BinaryExpression(path) {
66
+ if (path.node.operator == '**') {
67
+ path.node.operator = '+';
68
+ thisMutator.annotate(path.node, 'Replaced **');
69
+ }
70
+ },
71
+ // Unsupported language feature: https://crbug.com/1020573
72
+ MemberExpression(path) {
73
+ if (path.node.property.name == "arguments") {
74
+ let replacement = common.randomVariable(path);
75
+ if (!replacement) {
76
+ replacement = babelTypes.thisExpression();
77
+ }
78
+ thisMutator.annotate(replacement, 'Replaced .arguments');
79
+ thisMutator.replaceWithSkip(path, replacement);
80
+ }
81
+ },
82
+ };
83
+ }
84
+}
85
+
86
+/**
87
+ * Mutator for tracking original input files and for extra printing.
88
+ */
89
+class DifferentialFuzzMutator extends mutator.Mutator {
90
+ constructor(settings) {
91
+ super();
92
+ this.settings = settings;
93
+ }
94
+
95
+ /**
96
+ * Looks for the dummy node that marks the beginning of an input file
97
+ * from the corpus.
98
+ */
99
+ isSectionStart(path) {
100
+ return !!common.getOriginalPath(path.node);
101
+ }
102
+
103
+ /**
104
+ * Create print statements for printing the magic section prefix that's
105
+ * expected by v8_foozzie.py to differentiate different source files.
106
+ */
107
+ getSectionHeader(path) {
108
+ const orig = common.getOriginalPath(path.node);
109
+ return printValue({
110
+ VALUE: babelTypes.stringLiteral(SECTION_PREFIX + orig),
111
+ });
112
+ }
113
+
114
+ /**
115
+ * Create statements for extra printing at the end of a section. We print
116
+ * the number of caught exceptions, a generic hash of all observed values
117
+ * and the contents of all variables in scope.
118
+ */
119
+ getSectionFooter(path) {
120
+ const variables = common.availableVariables(path);
121
+ const statements = variables.map(prettyPrintStatement);
122
+ statements.unshift(printCaught());
123
+ statements.unshift(printHash());
124
+ const statement = babelTypes.tryStatement(
125
+ babelTypes.blockStatement(statements),
126
+ babelTypes.catchClause(
127
+ babelTypes.identifier('e'),
128
+ babelTypes.blockStatement([])));
129
+ this.annotate(statement, 'Print variables and exceptions from section');
130
+ return statement;
131
+ }
132
+
133
+ /**
134
+ * Helper for printing the contents of several variables.
135
+ */
136
+ printVariables(path, nodes) {
137
+ const statements = [];
138
+ for (const node of nodes) {
139
+ if (!babelTypes.isIdentifier(node) ||
140
+ !common.isVariableIdentifier(node.name))
141
+ continue;
142
+ statements.push(prettyPrintExtraStatement(node));
143
+ }
144
+ if (statements.length) {
145
+ this.annotate(statements[0], 'Extra variable printing');
146
+ this.insertAfterSkip(path, statements);
147
+ }
148
+ }
149
+
150
+ get visitor() {
151
+ const thisMutator = this;
152
+ const settings = this.settings;
153
+
154
+ return {
155
+ // Replace existing normal print statements with deep printing.
156
+ CallExpression(path) {
157
+ if (babelTypes.isIdentifier(path.node.callee) &&
158
+ path.node.callee.name == 'print') {
159
+ path.node.callee = babelTypes.identifier('__prettyPrintExtra');
160
+ thisMutator.annotate(path.node, 'Pretty printing');
161
+ }
162
+ },
163
+ // Either print or track caught exceptions, guarded by a probability.
164
+ CatchClause(path) {
165
+ const probability = random.random();
166
+ if (probability < settings.DIFF_FUZZ_EXTRA_PRINT &&
167
+ path.node.param &&
168
+ babelTypes.isIdentifier(path.node.param)) {
169
+ const statement = prettyPrintExtraStatement(path.node.param);
170
+ path.node.body.body.unshift(statement);
171
+ } else if (probability < settings.DIFF_FUZZ_TRACK_CAUGHT) {
172
+ path.node.body.body.unshift(incCaught());
173
+ }
174
+ },
175
+ // Insert section headers and footers between the contents of two
176
+ // original source files. We detect the dummy no-op nodes that were
177
+ // previously tagged with the original path of the file.
178
+ Noop(path) {
179
+ if (!thisMutator.isSectionStart(path)) {
180
+ return;
181
+ }
182
+ const header = thisMutator.getSectionHeader(path);
183
+ const footer = thisMutator.getSectionFooter(path);
184
+ thisMutator.insertBeforeSkip(path, footer);
185
+ thisMutator.insertBeforeSkip(path, header);
186
+ },
187
+ // Additionally we print one footer in the end.
188
+ Program: {
189
+ exit(path) {
190
+ const footer = thisMutator.getSectionFooter(path);
191
+ path.node.body.push(footer);
192
+ },
193
+ },
194
+ // Print contents of variables after assignments, guarded by a
195
+ // probability.
196
+ ExpressionStatement(path) {
197
+ if (!babelTypes.isAssignmentExpression(path.node.expression) ||
198
+ !random.choose(settings.DIFF_FUZZ_EXTRA_PRINT)) {
199
+ return;
200
+ }
201
+ const left = path.node.expression.left;
202
+ if (babelTypes.isMemberExpression(left)) {
203
+ thisMutator.printVariables(path, [left.object]);
204
+ } else {
205
+ thisMutator.printVariables(path, [left]);
206
+ }
207
+ },
208
+ // Print contents of variables after declaration, guarded by a
209
+ // probability.
210
+ VariableDeclaration(path) {
211
+ if (babelTypes.isLoop(path.parent) ||
212
+ !random.choose(settings.DIFF_FUZZ_EXTRA_PRINT)) {
213
+ return;
214
+ }
215
+ const identifiers = path.node.declarations.map(decl => decl.id);
216
+ thisMutator.printVariables(path, identifiers);
217
+ },
218
+ };
219
+ }
220
+}
221
+
222
+module.exports = {
223
+ DifferentialFuzzMutator: DifferentialFuzzMutator,
224
+ DifferentialFuzzSuppressions: DifferentialFuzzSuppressions,
225
+};
compiler/forget/packages/js-fuzzer/mutators/expression_mutator.js
new
+63
@@ -0,0 +1,63 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Expression mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTypes = require('@babel/types');
12
+
13
+const random = require('../random.js');
14
+const mutator = require('./mutator.js');
15
+
16
+class ExpressionMutator extends mutator.Mutator {
17
+ constructor(settings) {
18
+ super();
19
+ this.settings = settings;
20
+ }
21
+
22
+ get visitor() {
23
+ const thisMutator = this;
24
+
25
+ return {
26
+ ExpressionStatement(path) {
27
+ if (!random.choose(thisMutator.settings.MUTATE_EXPRESSIONS)) {
28
+ return;
29
+ }
30
+
31
+ const probability = random.random();
32
+
33
+ if (probability < 0.7) {
34
+ const repeated = babelTypes.cloneDeep(path.node);
35
+ thisMutator.annotate(repeated, 'Repeated');
36
+ thisMutator.insertBeforeSkip(path, repeated);
37
+ } else if (path.key > 0) {
38
+ // Get a random previous sibling.
39
+ const prev = path.getSibling(random.randInt(0, path.key - 1));
40
+ if (!prev || !prev.node) {
41
+ return;
42
+ }
43
+ // Either select a previous or the current node to clone.
44
+ const [selected, destination] = random.shuffle([prev, path]);
45
+ if (selected.isDeclaration()) {
46
+ return;
47
+ }
48
+ const cloned = babelTypes.cloneDeep(selected.node);
49
+ thisMutator.annotate(cloned, 'Cloned sibling');
50
+ if (random.choose(0.5)) {
51
+ thisMutator.insertBeforeSkip(destination, cloned);
52
+ } else {
53
+ thisMutator.insertAfterSkip(destination, cloned);
54
+ }
55
+ }
56
+ },
57
+ };
58
+ }
59
+}
60
+
61
+module.exports = {
62
+ ExpressionMutator: ExpressionMutator,
63
+};
compiler/forget/packages/js-fuzzer/mutators/function_call_mutator.js
new
+149
@@ -0,0 +1,149 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Function calls mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTemplate = require('@babel/template').default;
12
+const babelTypes = require('@babel/types');
13
+
14
+const common = require('./common.js');
15
+const random = require('../random.js');
16
+const mutator = require('./mutator.js');
17
+
18
+function _liftExpressionsToStatements(path, nodes) {
19
+ // If the node we're replacing is an expression in an expression statement,
20
+ // lift the replacement nodes into statements too.
21
+ if (!babelTypes.isExpressionStatement(path.parent)) {
22
+ return nodes;
23
+ }
24
+
25
+ return nodes.map(n => babelTypes.expressionStatement(n));
26
+}
27
+
28
+class FunctionCallMutator extends mutator.Mutator {
29
+ constructor(settings) {
30
+ super();
31
+ this.settings = settings;
32
+ }
33
+
34
+ get visitor() {
35
+ const thisMutator = this;
36
+
37
+ return {
38
+ CallExpression(path) {
39
+ if (!babelTypes.isIdentifier(path.node.callee)) {
40
+ return;
41
+ }
42
+
43
+ if (!common.isFunctionIdentifier(path.node.callee.name)) {
44
+ return;
45
+ }
46
+
47
+ if (!random.choose(thisMutator.settings.MUTATE_FUNCTION_CALLS)) {
48
+ return;
49
+ }
50
+
51
+ const probability = random.random();
52
+ if (probability < 0.3) {
53
+ const randFunc = common.randomFunction(path);
54
+ if (randFunc) {
55
+ thisMutator.annotate(
56
+ path.node,
57
+ `Replaced ${path.node.callee.name} with ${randFunc.name}`);
58
+
59
+ path.node.callee = randFunc;
60
+ }
61
+ } else if (probability < 0.7 && thisMutator.settings.engine == 'V8') {
62
+ const prepareTemplate = babelTemplate(
63
+ '__V8BuiltinPrepareFunctionForOptimization(ID)');
64
+ const optimizationMode = random.choose(0.7) ? 'Function' : 'Maglev';
65
+ const optimizeTemplate = babelTemplate(
66
+ `__V8BuiltinOptimize${optimizationMode}OnNextCall(ID)`);
67
+
68
+ const nodes = [
69
+ prepareTemplate({
70
+ ID: babelTypes.cloneDeep(path.node.callee),
71
+ }).expression,
72
+ babelTypes.cloneDeep(path.node),
73
+ babelTypes.cloneDeep(path.node),
74
+ optimizeTemplate({
75
+ ID: babelTypes.cloneDeep(path.node.callee),
76
+ }).expression,
77
+ ];
78
+
79
+ thisMutator.annotate(
80
+ path.node,
81
+ `Optimizing ${path.node.callee.name}`);
82
+ if (!babelTypes.isExpressionStatement(path.parent)) {
83
+ nodes.push(path.node);
84
+ thisMutator.replaceWithSkip(
85
+ path, babelTypes.sequenceExpression(nodes));
86
+ } else {
87
+ thisMutator.insertBeforeSkip(
88
+ path, _liftExpressionsToStatements(path, nodes));
89
+ }
90
+ } else if (probability < 0.8 && thisMutator.settings.engine == 'V8') {
91
+ const template = babelTemplate(
92
+ '__V8BuiltinCompileBaseline(ID)');
93
+
94
+ const nodes = [
95
+ template({
96
+ ID: babelTypes.cloneDeep(path.node.callee),
97
+ }).expression,
98
+ ];
99
+
100
+ thisMutator.annotate(
101
+ nodes[0],
102
+ `Compiling baseline ${path.node.callee.name}`);
103
+
104
+ if (!babelTypes.isExpressionStatement(path.parent)) {
105
+ nodes.push(path.node);
106
+ thisMutator.replaceWithSkip(
107
+ path, babelTypes.sequenceExpression(nodes));
108
+ } else {
109
+ thisMutator.insertBeforeSkip(
110
+ path, _liftExpressionsToStatements(path, nodes));
111
+ }
112
+ } else if (probability < 0.9 &&
113
+ thisMutator.settings.engine == 'V8') {
114
+ const template = babelTemplate(
115
+ '__V8BuiltinDeoptimizeFunction(ID)');
116
+ const insert = _liftExpressionsToStatements(path, [
117
+ template({
118
+ ID: babelTypes.cloneDeep(path.node.callee),
119
+ }).expression,
120
+ ]);
121
+
122
+ thisMutator.annotate(
123
+ path.node,
124
+ `Deoptimizing ${path.node.callee.name}`);
125
+
126
+ thisMutator.insertAfterSkip(path, insert);
127
+ } else {
128
+ const template = babelTemplate(
129
+ 'runNearStackLimit(() => { return CALL });');
130
+ thisMutator.annotate(
131
+ path.node,
132
+ `Run to stack limit ${path.node.callee.name}`);
133
+
134
+ thisMutator.replaceWithSkip(
135
+ path,
136
+ template({
137
+ CALL: path.node,
138
+ }).expression);
139
+ }
140
+
141
+ path.skip();
142
+ },
143
+ }
144
+ }
145
+}
146
+
147
+module.exports = {
148
+ FunctionCallMutator: FunctionCallMutator,
149
+};
compiler/forget/packages/js-fuzzer/mutators/mutator.js
new
+98
@@ -0,0 +1,98 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Mutator
7
+ */
8
+'use strict';
9
+
10
+const babelTraverse = require('@babel/traverse').default;
11
+const babelTypes = require('@babel/types');
12
+
13
+class Mutator {
14
+ get visitor() {
15
+ return null;
16
+ }
17
+
18
+ _traverse(ast, visitor) {
19
+ let oldEnter = null;
20
+ if (Object.prototype.hasOwnProperty.call(visitor, 'enter')) {
21
+ oldEnter = visitor['enter'];
22
+ }
23
+
24
+ // Transparently skip nodes that are marked.
25
+ visitor['enter'] = (path) => {
26
+ if (this.shouldSkip(path.node)) {
27
+ path.skip();
28
+ return;
29
+ }
30
+
31
+ if (oldEnter) {
32
+ oldEnter(path);
33
+ }
34
+ }
35
+
36
+ babelTraverse(ast, visitor);
37
+ }
38
+
39
+ mutate(source) {
40
+ if (Array.isArray(this.visitor)) {
41
+ for (const visitor of this.visitor) {
42
+ this._traverse(source.ast, visitor);
43
+ }
44
+ } else {
45
+ this._traverse(source.ast, this.visitor);
46
+ }
47
+ }
48
+
49
+ get _skipPropertyName() {
50
+ return '__skip' + this.constructor.name;
51
+ }
52
+
53
+ shouldSkip(node) {
54
+ return Boolean(node[this._skipPropertyName]);
55
+ }
56
+
57
+ skipMutations(node) {
58
+ // Mark a node to skip further mutations of the same kind.
59
+ if (Array.isArray(node)) {
60
+ for (const item of node) {
61
+ item[this._skipPropertyName] = true;
62
+ }
63
+ } else {
64
+ node[this._skipPropertyName] = true;
65
+ }
66
+
67
+ return node;
68
+ }
69
+
70
+ insertBeforeSkip(path, node) {
71
+ this.skipMutations(node);
72
+ path.insertBefore(node);
73
+ }
74
+
75
+ insertAfterSkip(path, node) {
76
+ this.skipMutations(node);
77
+ path.insertAfter(node);
78
+ }
79
+
80
+ replaceWithSkip(path, node) {
81
+ this.skipMutations(node);
82
+ path.replaceWith(node);
83
+ }
84
+
85
+ replaceWithMultipleSkip(path, node) {
86
+ this.skipMutations(node);
87
+ path.replaceWithMultiple(node);
88
+ }
89
+
90
+ annotate(node, message) {
91
+ babelTypes.addComment(
92
+ node, 'leading', ` ${this.constructor.name}: ${message} `);
93
+ }
94
+}
95
+
96
+module.exports = {
97
+ Mutator: Mutator,
98
+}
compiler/forget/packages/js-fuzzer/mutators/normalizer.js
new
+89
@@ -0,0 +1,89 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Normalizer.
7
+ * This renames variables so that we don't have collisions when combining
8
+ * different files. It also simplifies other logic when e.g. determining the
9
+ * type of an identifier.
10
+ */
11
+'use strict';
12
+
13
+const babelTypes = require('@babel/types');
14
+
15
+const mutator = require('./mutator.js');
16
+
17
+class NormalizerContext {
18
+ constructor() {
19
+ this.funcIndex = 0;
20
+ this.varIndex = 0;
21
+ this.classIndex = 0;
22
+ }
23
+}
24
+
25
+class IdentifierNormalizer extends mutator.Mutator {
26
+ constructor() {
27
+ super();
28
+ this.context = new NormalizerContext();
29
+ }
30
+
31
+ get visitor() {
32
+ const context = this.context;
33
+ const renamed = new WeakSet();
34
+ const globalMappings = new Map();
35
+
36
+ return [{
37
+ Scope(path) {
38
+ for (const [name, binding] of Object.entries(path.scope.bindings)) {
39
+ if (renamed.has(binding.identifier)) {
40
+ continue;
41
+ }
42
+
43
+ renamed.add(binding.identifier);
44
+
45
+ if (babelTypes.isClassDeclaration(binding.path.node) ||
46
+ babelTypes.isClassExpression(binding.path.node)) {
47
+ path.scope.rename(name, '__c_' + context.classIndex++);
48
+ } else if (babelTypes.isFunctionDeclaration(binding.path.node) ||
49
+ babelTypes.isFunctionExpression(binding.path.node)) {
50
+ path.scope.rename(name, '__f_' + context.funcIndex++);
51
+ } else {
52
+ path.scope.rename(name, '__v_' + context.varIndex++);
53
+ }
54
+ }
55
+ },
56
+
57
+ AssignmentExpression(path) {
58
+ // Find assignments for which we have no binding in the scope. We assume
59
+ // that these are globals which are local to our script (which weren't
60
+ // declared with var/let/const etc).
61
+ const ids = path.getBindingIdentifiers();
62
+ for (const name in ids) {
63
+ if (!path.scope.getBinding(name)) {
64
+ globalMappings.set(name, '__v_' + context.varIndex++);
65
+ }
66
+ }
67
+ }
68
+ }, {
69
+ // Second pass to rename globals that weren't declared with
70
+ // var/let/const etc.
71
+ Identifier(path) {
72
+ if (!globalMappings.has(path.node.name)) {
73
+ return;
74
+ }
75
+
76
+ if (path.scope.getBinding(path.node.name)) {
77
+ // Don't rename if there is a binding that hides the global.
78
+ return;
79
+ }
80
+
81
+ path.node.name = globalMappings.get(path.node.name);
82
+ }
83
+ }];
84
+ }
85
+}
86
+
87
+module.exports = {
88
+ IdentifierNormalizer: IdentifierNormalizer,
89
+};
compiler/forget/packages/js-fuzzer/mutators/number_mutator.js
new
+105
@@ -0,0 +1,105 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Numbers mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTypes = require('@babel/types');
12
+
13
+const common = require('./common.js');
14
+const random = require('../random.js');
15
+const mutator = require('./mutator.js');
16
+
17
+const MIN_SAFE_INTEGER = -9007199254740991;
18
+const MAX_SAFE_INTEGER = 9007199254740991;
19
+
20
+
21
+function isObjectKey(path) {
22
+ return (path.parent &&
23
+ babelTypes.isObjectMember(path.parent) &&
24
+ path.parent.key === path.node);
25
+}
26
+
27
+function createRandomNumber(value) {
28
+ // TODO(ochang): Maybe replace with variable.
29
+ const probability = random.random();
30
+ if (probability < 0.01) {
31
+ return babelTypes.numericLiteral(
32
+ random.randInt(MIN_SAFE_INTEGER, MAX_SAFE_INTEGER));
33
+ } else if (probability < 0.06) {
34
+ return common.randomInterestingNumber();
35
+ } else {
36
+ return common.nearbyRandomNumber(value);
37
+ }
38
+}
39
+
40
+class NumberMutator extends mutator.Mutator {
41
+ constructor(settings) {
42
+ super();
43
+ this.settings = settings;
44
+ }
45
+
46
+ ignore(path) {
47
+ return !random.choose(this.settings.MUTATE_NUMBERS) ||
48
+ common.isInForLoopCondition(path) ||
49
+ common.isInWhileLoop(path);
50
+ }
51
+
52
+ randomReplace(path, value, forcePositive=false) {
53
+ const randomNumber = createRandomNumber(value);
54
+
55
+ if (forcePositive) {
56
+ randomNumber.value = Math.abs(randomNumber.value);
57
+ }
58
+
59
+ this.annotate(
60
+ path.node,
61
+ `Replaced ${value} with ${randomNumber.value}`);
62
+
63
+ this.replaceWithSkip(path, randomNumber);
64
+ }
65
+
66
+ get visitor() {
67
+ const thisMutator = this;
68
+
69
+ return {
70
+ NumericLiteral(path) {
71
+ if (thisMutator.ignore(path)) {
72
+ return;
73
+ }
74
+
75
+ // We handle negative unary expressions separately to replace the whole
76
+ // expression below. E.g. -5 is UnaryExpression(-, NumericLiteral(5)).
77
+ if (path.parent && babelTypes.isUnaryExpression(path.parent) &&
78
+ path.parent.operator === '-') {
79
+ return;
80
+ }
81
+
82
+ // Enfore positive numbers if the literal is the key of an object
83
+ // property or method. Negative keys cause syntax errors.
84
+ const forcePositive = isObjectKey(path);
85
+
86
+ thisMutator.randomReplace(path, path.node.value, forcePositive);
87
+ },
88
+ UnaryExpression(path) {
89
+ if (thisMutator.ignore(path)) {
90
+ return;
91
+ }
92
+
93
+ // Handle the case we ignore above.
94
+ if (path.node.operator === '-' &&
95
+ babelTypes.isNumericLiteral(path.node.argument)) {
96
+ thisMutator.randomReplace(path, -path.node.argument.value);
97
+ }
98
+ }
99
+ };
100
+ }
101
+}
102
+
103
+module.exports = {
104
+ NumberMutator: NumberMutator,
105
+};
compiler/forget/packages/js-fuzzer/mutators/object_mutator.js
new
+135
@@ -0,0 +1,135 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Mutator for object expressions.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTypes = require('@babel/types');
12
+
13
+const common = require('./common.js');
14
+const mutator = require('./mutator.js');
15
+const random = require('../random.js');
16
+
17
+const MAX_PROPERTIES = 50;
18
+
19
+/**
20
+ * Turn the key of an object property into a string literal.
21
+ */
22
+function keyToString(key) {
23
+ if (babelTypes.isNumericLiteral(key)) {
24
+ return babelTypes.stringLiteral(key.value.toString());
25
+ }
26
+ if (babelTypes.isIdentifier(key)) {
27
+ return babelTypes.stringLiteral(key.name);
28
+ }
29
+ // Already a string literal.
30
+ return key;
31
+}
32
+
33
+class ObjectMutator extends mutator.Mutator {
34
+ constructor(settings) {
35
+ super();
36
+ this.settings = settings;
37
+ }
38
+
39
+ get visitor() {
40
+ const thisMutator = this;
41
+
42
+ return {
43
+ ObjectExpression(path) {
44
+ const properties = path.node.properties;
45
+ if (!random.choose(thisMutator.settings.MUTATE_OBJECTS) ||
46
+ properties.length > MAX_PROPERTIES) {
47
+ return;
48
+ }
49
+
50
+ // Use the indices of object properties for mutations. We ignore
51
+ // getters and setters.
52
+ const propertyIndicies = [];
53
+ for (const [index, property] of properties.entries()) {
54
+ if (babelTypes.isObjectProperty(property)) {
55
+ propertyIndicies.push(index);
56
+ }
57
+ }
58
+
59
+ // The mutations below require at least one property.
60
+ if (!propertyIndicies.length) {
61
+ return;
62
+ }
63
+
64
+ // Annotate object expression with the action taken.
65
+ function annotate(message) {
66
+ thisMutator.annotate(path.node, message);
67
+ }
68
+
69
+ function getOneRandomProperty() {
70
+ return properties[random.single(propertyIndicies)];
71
+ }
72
+
73
+ function getTwoRandomProperties() {
74
+ const [a, b] = random.sample(propertyIndicies, 2);
75
+ return [properties[a], properties[b]];
76
+ }
77
+
78
+ function swapPropertyValues() {
79
+ if (propertyIndicies.length > 1) {
80
+ annotate('Swap properties');
81
+ const [a, b] = getTwoRandomProperties();
82
+ [a.value, b.value] = [b.value, a.value];
83
+ }
84
+ }
85
+
86
+ function duplicatePropertyValue() {
87
+ if (propertyIndicies.length > 1) {
88
+ const [a, b] = random.shuffle(getTwoRandomProperties());
89
+ if (common.isLargeNode(b.value)) {
90
+ return;
91
+ }
92
+ annotate('Duplicate a property value');
93
+ a.value = babelTypes.cloneDeep(b.value);
94
+ }
95
+ }
96
+
97
+ function insertRandomValue() {
98
+ annotate('Insert a random value');
99
+ const property = getOneRandomProperty();
100
+ property.value = common.randomValue(path);
101
+ }
102
+
103
+ function stringifyKey() {
104
+ annotate('Stringify a property key');
105
+ const property = getOneRandomProperty();
106
+ property.key = keyToString(property.key);
107
+ }
108
+
109
+ function removeProperty() {
110
+ annotate('Remove a property');
111
+ properties.splice(random.single(propertyIndicies), 1);
112
+ }
113
+
114
+ // Mutation options. Repeated mutations have a higher probability.
115
+ const mutations = [
116
+ swapPropertyValues,
117
+ swapPropertyValues,
118
+ duplicatePropertyValue,
119
+ duplicatePropertyValue,
120
+ insertRandomValue,
121
+ insertRandomValue,
122
+ removeProperty,
123
+ stringifyKey,
124
+ ];
125
+
126
+ // Perform mutation.
127
+ random.single(mutations)();
128
+ },
129
+ }
130
+ }
131
+}
132
+
133
+module.exports = {
134
+ ObjectMutator: ObjectMutator,
135
+};
compiler/forget/packages/js-fuzzer/mutators/try_catch.js
new
+175
@@ -0,0 +1,175 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Try catch wrapper.
7
+ */
8
+
9
+const babelTypes = require('@babel/types');
10
+
11
+const common = require('./common.js');
12
+const mutator = require('./mutator.js');
13
+const random = require('../random.js');
14
+
15
+// Default target probability for skipping try-catch completely.
16
+const DEFAULT_SKIP_PROB = 0.2;
17
+
18
+// Default target probability to wrap only on toplevel, i.e. to not nest
19
+// try-catch.
20
+const DEFAULT_TOPLEVEL_PROB = 0.3;
21
+
22
+// Probability to deviate from defaults and use extreme cases.
23
+const IGNORE_DEFAULT_PROB = 0.05;
24
+
25
+// Member expressions to be wrapped. List of (object, property) identifier
26
+// tuples.
27
+const WRAPPED_MEMBER_EXPRESSIONS = [
28
+ ['WebAssembly', 'Module'],
29
+ ['WebAssembly', 'Instantiate'],
30
+];
31
+
32
+function wrapTryCatch(node) {
33
+ return babelTypes.tryStatement(
34
+ babelTypes.blockStatement([node]),
35
+ babelTypes.catchClause(
36
+ babelTypes.identifier('e'),
37
+ babelTypes.blockStatement([])));
38
+}
39
+
40
+function wrapTryCatchInFunction(node) {
41
+ const ret = wrapTryCatch(babelTypes.returnStatement(node));
42
+ const anonymousFun = babelTypes.functionExpression(
43
+ null, [], babelTypes.blockStatement([ret]));
44
+ return babelTypes.callExpression(anonymousFun, []);
45
+}
46
+
47
+// Wrap particular member expressions after `new` that are known to appear
48
+// in initializer lists of `let` and `const`.
49
+function replaceNewExpression(path) {
50
+ const callee = path.node.callee;
51
+ if (!babelTypes.isMemberExpression(callee) ||
52
+ !babelTypes.isIdentifier(callee.object) ||
53
+ !babelTypes.isIdentifier(callee.property)) {
54
+ return;
55
+ }
56
+ if (WRAPPED_MEMBER_EXPRESSIONS.some(
57
+ ([object, property]) => callee.object.name === object &&
58
+ callee.property.name === property)) {
59
+ path.replaceWith(wrapTryCatchInFunction(path.node));
60
+ path.skip();
61
+ }
62
+}
63
+
64
+function replaceAndSkip(path) {
65
+ if (!babelTypes.isLabeledStatement(path.parent) ||
66
+ !babelTypes.isLoop(path.node)) {
67
+ // Don't wrap loops with labels as it makes continue
68
+ // statements syntactically invalid. We wrap the label
69
+ // instead below.
70
+ path.replaceWith(wrapTryCatch(path.node));
71
+ }
72
+ // Prevent infinite looping.
73
+ path.skip();
74
+}
75
+
76
+class AddTryCatchMutator extends mutator.Mutator {
77
+ callWithProb(path, fun) {
78
+ const probability = random.random();
79
+ if (probability < this.skipProb * this.loc) {
80
+ // Entirely skip try-catch wrapper.
81
+ path.skip();
82
+ } else if (probability < (this.skipProb + this.toplevelProb) * this.loc) {
83
+ // Only wrap on top-level.
84
+ fun(path);
85
+ }
86
+ }
87
+
88
+ get visitor() {
89
+ const thisMutator = this;
90
+ const accessStatement = {
91
+ enter(path) {
92
+ thisMutator.callWithProb(path, replaceAndSkip);
93
+ },
94
+ exit(path) {
95
+ // Apply nested wrapping (is only executed if not skipped above).
96
+ replaceAndSkip(path);
97
+ }
98
+ };
99
+ return {
100
+ Program: {
101
+ enter(path) {
102
+ // Track original source location fraction in [0, 1).
103
+ thisMutator.loc = 0;
104
+ // Target probability for skipping try-catch.
105
+ thisMutator.skipProb = DEFAULT_SKIP_PROB;
106
+ // Target probability for not nesting try-catch.
107
+ thisMutator.toplevelProb = DEFAULT_TOPLEVEL_PROB;
108
+ // Maybe deviate from target probability for the entire test.
109
+ if (random.choose(IGNORE_DEFAULT_PROB)) {
110
+ thisMutator.skipProb = random.uniform(0, 1);
111
+ thisMutator.toplevelProb = random.uniform(0, 1);
112
+ thisMutator.annotate(
113
+ path.node,
114
+ 'Target skip probability ' + thisMutator.skipProb +
115
+ ' and toplevel probability ' + thisMutator.toplevelProb);
116
+ }
117
+ }
118
+ },
119
+ Noop: {
120
+ enter(path) {
121
+ if (common.getSourceLoc(path.node)) {
122
+ thisMutator.loc = common.getSourceLoc(path.node);
123
+ }
124
+ },
125
+ },
126
+ ExpressionStatement: accessStatement,
127
+ IfStatement: accessStatement,
128
+ LabeledStatement: {
129
+ enter(path) {
130
+ // Apply an extra try-catch around the label of a loop, since we
131
+ // ignore the loop itself if it has a label.
132
+ if (babelTypes.isLoop(path.node.body)) {
133
+ thisMutator.callWithProb(path, replaceAndSkip);
134
+ }
135
+ },
136
+ exit(path) {
137
+ // Apply nested wrapping (is only executed if not skipped above).
138
+ if (babelTypes.isLoop(path.node.body)) {
139
+ replaceAndSkip(path);
140
+ }
141
+ },
142
+ },
143
+ // This covers {While|DoWhile|ForIn|ForOf|For}Statement.
144
+ Loop: accessStatement,
145
+ NewExpression: {
146
+ enter(path) {
147
+ thisMutator.callWithProb(path, replaceNewExpression);
148
+ },
149
+ exit(path) {
150
+ // Apply nested wrapping (is only executed if not skipped above).
151
+ replaceNewExpression(path);
152
+ }
153
+ },
154
+ SwitchStatement: accessStatement,
155
+ VariableDeclaration: {
156
+ enter(path) {
157
+ if (path.node.kind !== 'var' || babelTypes.isLoop(path.parent))
158
+ return;
159
+ thisMutator.callWithProb(path, replaceAndSkip);
160
+ },
161
+ exit(path) {
162
+ if (path.node.kind !== 'var' || babelTypes.isLoop(path.parent))
163
+ return;
164
+ // Apply nested wrapping (is only executed if not skipped above).
165
+ replaceAndSkip(path);
166
+ }
167
+ },
168
+ WithStatement: accessStatement,
169
+ };
170
+ }
171
+}
172
+
173
+module.exports = {
174
+ AddTryCatchMutator: AddTryCatchMutator,
175
+}
compiler/forget/packages/js-fuzzer/mutators/variable_mutator.js
new
+73
@@ -0,0 +1,73 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Variables mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTypes = require('@babel/types');
12
+
13
+const common = require('./common.js');
14
+const random = require('../random.js');
15
+const mutator = require('./mutator.js');
16
+
17
+function _isInFunctionParam(path) {
18
+ const child = path.find(p => p.parent && babelTypes.isFunction(p.parent));
19
+ return child && child.parentKey === 'params';
20
+}
21
+
22
+class VariableMutator extends mutator.Mutator {
23
+ constructor(settings) {
24
+ super();
25
+ this.settings = settings;
26
+ }
27
+
28
+ get visitor() {
29
+ const thisMutator = this;
30
+
31
+ return {
32
+ Identifier(path) {
33
+ if (!random.choose(thisMutator.settings.MUTATE_VARIABLES)) {
34
+ return;
35
+ }
36
+
37
+ if (!common.isVariableIdentifier(path.node.name)) {
38
+ return;
39
+ }
40
+
41
+ // Don't mutate variables that are being declared.
42
+ if (babelTypes.isVariableDeclarator(path.parent)) {
43
+ return;
44
+ }
45
+
46
+ // Don't mutate function params.
47
+ if (_isInFunctionParam(path)) {
48
+ return;
49
+ }
50
+
51
+ if (common.isInForLoopCondition(path) ||
52
+ common.isInWhileLoop(path)) {
53
+ return;
54
+ }
55
+
56
+ const randVar = common.randomVariable(path);
57
+ if (!randVar) {
58
+ return;
59
+ }
60
+
61
+ const newName = randVar.name;
62
+ thisMutator.annotate(
63
+ path.node,
64
+ `Replaced ${path.node.name} with ${newName}`);
65
+ path.node.name = newName;
66
+ }
67
+ };
68
+ }
69
+}
70
+
71
+module.exports = {
72
+ VariableMutator: VariableMutator,
73
+};
compiler/forget/packages/js-fuzzer/mutators/variable_or_object_mutation.js
new
+154
@@ -0,0 +1,154 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Variables mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTemplate = require('@babel/template').default;
12
+const babelTypes = require('@babel/types');
13
+
14
+const common = require('./common.js');
15
+const random = require('../random.js');
16
+const mutator = require('./mutator.js');
17
+
18
+const MAX_MUTATION_RECURSION_DEPTH = 5;
19
+
20
+class VariableOrObjectMutator extends mutator.Mutator {
21
+ constructor(settings) {
22
+ super();
23
+ this.settings = settings;
24
+ }
25
+
26
+ _randomVariableOrObject(path) {
27
+ const randomVar = common.randomVariable(path);
28
+ if (random.choose(0.05) || !randomVar) {
29
+ return common.randomObject();
30
+ }
31
+
32
+ return randomVar;
33
+ }
34
+
35
+ _randomVariableOrObjectMutations(path, recurseDepth=0) {
36
+ if (recurseDepth >= MAX_MUTATION_RECURSION_DEPTH) {
37
+ return new Array();
38
+ }
39
+
40
+ const probability = random.random();
41
+
42
+ if (probability < 0.3) {
43
+ const first = this._randomVariableOrObjectMutations(path, recurseDepth + 1);
44
+ const second = this._randomVariableOrObjectMutations(
45
+ path, recurseDepth + 1);
46
+ return first.concat(second);
47
+ }
48
+
49
+ const randVarOrObject = this._randomVariableOrObject(path);
50
+ const randProperty = common.randomProperty(randVarOrObject);
51
+ let newRandVarOrObject = randVarOrObject;
52
+ if (random.choose(0.2)) {
53
+ newRandVarOrObject = this._randomVariableOrObject(path);
54
+ }
55
+
56
+ const mutations = new Array();
57
+
58
+ if (probability < 0.4) {
59
+ const template = babelTemplate(
60
+ 'delete IDENTIFIER[PROPERTY], __callGC()')
61
+ mutations.push(template({
62
+ IDENTIFIER: randVarOrObject,
63
+ PROPERTY: randProperty
64
+ }));
65
+ } else if (probability < 0.5) {
66
+ const template = babelTemplate(
67
+ 'IDENTIFIER[PROPERTY], __callGC()')
68
+ mutations.push(template({
69
+ IDENTIFIER: randVarOrObject,
70
+ PROPERTY: randProperty
71
+ }));
72
+ } else if (probability < 0.6) {
73
+ const template = babelTemplate(
74
+ 'IDENTIFIER[PROPERTY] = RANDOM, __callGC()')
75
+ mutations.push(template({
76
+ IDENTIFIER: randVarOrObject,
77
+ PROPERTY: randProperty,
78
+ RANDOM: common.randomValue(path),
79
+ }));
80
+ } else if (probability < 0.7) {
81
+ mutations.push(
82
+ babelTypes.expressionStatement(
83
+ common.callRandomFunction(path, randVarOrObject)));
84
+ } else if (probability < 0.8) {
85
+ const template = babelTemplate(
86
+ 'VAR = IDENTIFIER, __callGC()')
87
+ var randomVar = common.randomVariable(path);
88
+ if (!randomVar) {
89
+ return mutations;
90
+ }
91
+
92
+ mutations.push(template({
93
+ VAR: randomVar,
94
+ IDENTIFIER: randVarOrObject,
95
+ }));
96
+ } else if (probability < 0.9) {
97
+ const template = babelTemplate(
98
+ 'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' +
99
+ 'Object.defineProperty(IDENTIFIER, PROPERTY, {value: VALUE})')
100
+ mutations.push(template({
101
+ IDENTIFIER: newRandVarOrObject,
102
+ PROPERTY: randProperty,
103
+ VALUE: common.randomValue(path),
104
+ }));
105
+ } else {
106
+ const template = babelTemplate(
107
+ 'if (IDENTIFIER != null && typeof(IDENTIFIER) == "object") ' +
108
+ 'Object.defineProperty(IDENTIFIER, PROPERTY, {' +
109
+ 'get: function() { GETTER_MUTATION ; return VALUE; },' +
110
+ 'set: function(value) { SETTER_MUTATION; }' +
111
+ '})');
112
+ mutations.push(template({
113
+ IDENTIFIER: newRandVarOrObject,
114
+ PROPERTY: randProperty,
115
+ GETTER_MUTATION: this._randomVariableOrObjectMutations(
116
+ path, recurseDepth + 1),
117
+ SETTER_MUTATION: this._randomVariableOrObjectMutations(
118
+ path, recurseDepth + 1),
119
+ VALUE: common.randomValue(path),
120
+ }));
121
+ }
122
+
123
+ return mutations;
124
+ }
125
+
126
+
127
+ get visitor() {
128
+ const settings = this.settings;
129
+ const thisMutator = this;
130
+
131
+ return {
132
+ ExpressionStatement(path) {
133
+ if (!random.choose(settings.ADD_VAR_OR_OBJ_MUTATIONS)) {
134
+ return;
135
+ }
136
+
137
+ const mutations = thisMutator._randomVariableOrObjectMutations(path);
138
+ thisMutator.annotate(mutations[0], 'Random mutation');
139
+
140
+ if (random.choose(0.5)) {
141
+ thisMutator.insertBeforeSkip(path, mutations);
142
+ } else {
143
+ thisMutator.insertAfterSkip(path, mutations);
144
+ }
145
+
146
+ path.skip();
147
+ }
148
+ };
149
+ }
150
+}
151
+
152
+module.exports = {
153
+ VariableOrObjectMutator: VariableOrObjectMutator,
154
+};
compiler/forget/packages/js-fuzzer/package.json
new
+34
@@ -0,0 +1,34 @@
1
+{
2
+ "name": "ochang_js_fuzzer",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "run.js",
6
+ "scripts": {
7
+ "test": "echo 'no test'",
8
+ "build": "echo 'no build'"
9
+ },
10
+ "bin": "run.js",
11
+ "author": "ochang@google.com",
12
+ "license": "ISC",
13
+ "dependencies": {
14
+ "@babel/generator": "^7.1.3",
15
+ "@babel/template": "^7.1.2",
16
+ "@babel/traverse": "^7.1.4",
17
+ "@babel/types": "^7.1.3",
18
+ "@babel/parser": "^7.1.3",
19
+ "commander": "^2.11.0",
20
+ "globals": "^10.1.0",
21
+ "tempfile": "^3.0.0",
22
+ "tempy": "^0.5.0"
23
+ },
24
+ "devDependencies": {
25
+ "eslint": "^6.8.0",
26
+ "mocha": "^3.5.3",
27
+ "pkg": "^4.3.4",
28
+ "prettier": "2.0.5",
29
+ "sinon": "^4.0.0"
30
+ },
31
+ "pkg": {
32
+ "assets": "resources/**/*"
33
+ }
34
+}
compiler/forget/packages/js-fuzzer/package.sh
new
+32
@@ -0,0 +1,32 @@
1
+#!/bin/bash
2
+# Copyright 2020 the V8 project authors. All rights reserved.
3
+# Use of this source code is governed by a BSD-style license that can be
4
+# found in the LICENSE file.
5
+
6
+DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" >/dev/null 2>&1 && pwd )"
7
+
8
+OS="linux"
9
+OS_LABEL="Linux"
10
+SUFFIX=""
11
+if [[ -n "$1" && $1 == "win" ]]; then
12
+ OS="win"
13
+ OS_LABEL="Windows"
14
+ SUFFIX=".exe"
15
+elif [[ -n "$1" && $1 == "macos" ]]; then
16
+ OS="macos"
17
+ OS_LABEL="MacOS"
18
+fi
19
+
20
+echo "Building and packaging for $OS_LABEL..."
21
+(set -x; $DIR/node_modules/.bin/pkg -t node10-$OS-x64 $DIR)
22
+
23
+rm -rf $DIR/output > /dev/null 2>&1 || true
24
+rm $DIR/output.zip > /dev/null 2>&1 || true
25
+
26
+mkdir $DIR/output
27
+cd $DIR/output
28
+ln -s ../db db
29
+ln -s ../ochang_js_fuzzer$SUFFIX run$SUFFIX
30
+ln -s ../foozzie_launcher.py foozzie_launcher.py
31
+echo "Creating $DIR/output.zip"
32
+(set -x; zip -r $DIR/output.zip * > /dev/null)
compiler/forget/packages/js-fuzzer/random.js
new
+113
@@ -0,0 +1,113 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Random helpers.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+
13
+function randInt(min, max) {
14
+ return Math.floor(Math.random() * (max - min + 1)) + min;
15
+}
16
+
17
+function choose(probability) {
18
+ return Math.random() < probability;
19
+}
20
+
21
+function random() {
22
+ return Math.random();
23
+}
24
+
25
+function uniform(min, max) {
26
+ return Math.random() * (max - min) + min;
27
+}
28
+
29
+function sample(iterable, count) {
30
+ const result = new Array(count);
31
+ let index = 0;
32
+
33
+ for (const item of iterable) {
34
+ if (index < count) {
35
+ result[index] = item;
36
+ } else {
37
+ const randIndex = randInt(0, index);
38
+ if (randIndex < count) {
39
+ result[randIndex] = item;
40
+ }
41
+ }
42
+
43
+ index++;
44
+ }
45
+
46
+ if (index < count) {
47
+ // Not enough items.
48
+ result.length = index;
49
+ }
50
+
51
+ return result;
52
+}
53
+
54
+function swap(array, p1, p2) {
55
+ [array[p1], array[p2]] = [array[p2], array[p1]];
56
+}
57
+
58
+/**
59
+ * Returns "count" elements, randomly selected from "highProbArray" and
60
+ * "lowProbArray". Elements from highProbArray have a "factor" times
61
+ * higher chance to be chosen. As a side effect, this swaps the chosen
62
+ * elements to the end of the respective input arrays. The complexity is
63
+ * O(count).
64
+ */
65
+function twoBucketSample(lowProbArray, highProbArray, factor, count) {
66
+ // Track number of available elements for choosing.
67
+ let low = lowProbArray.length;
68
+ let high = highProbArray.length;
69
+ assert(low + high >= count);
70
+ const result = [];
71
+ for (let i = 0; i < count; i++) {
72
+ // Map a random number to the summarized indices of both arrays. Give
73
+ // highProbArray elements a "factor" times higher probability.
74
+ const p = random();
75
+ const index = Math.floor(p * (high * factor + low));
76
+ if (index < low) {
77
+ // If the index is in the low part, draw the element and discard it.
78
+ result.push(lowProbArray[index]);
79
+ swap(lowProbArray, index, --low);
80
+ } else {
81
+ // Same as above but for a highProbArray element. The index is first
82
+ // mapped back to the array's range.
83
+ const highIndex = Math.floor((index - low) / factor);
84
+ result.push(highProbArray[highIndex]);
85
+ swap(highProbArray, highIndex, --high);
86
+ }
87
+ }
88
+ return result;
89
+}
90
+
91
+function single(array) {
92
+ return array[randInt(0, array.length - 1)];
93
+}
94
+
95
+function shuffle(array) {
96
+ for (let i = 0; i < array.length - 1; i++) {
97
+ const j = randInt(i, array.length - 1);
98
+ swap(array, i, j);
99
+ }
100
+
101
+ return array;
102
+}
103
+
104
+module.exports = {
105
+ choose: choose,
106
+ randInt: randInt,
107
+ random: random,
108
+ sample: sample,
109
+ shuffle: shuffle,
110
+ single: single,
111
+ twoBucketSample: twoBucketSample,
112
+ uniform: uniform,
113
+}
compiler/forget/packages/js-fuzzer/resources/differential_fuzz_chakra.js
new
+17
@@ -0,0 +1,17 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+// Adjust chakra behavior for differential fuzzing.
7
+
8
+this.WScript = new Proxy({}, {
9
+ get(target, name) {
10
+ switch (name) {
11
+ case 'Echo':
12
+ return __prettyPrintExtra;
13
+ default:
14
+ return {};
15
+ }
16
+ }
17
+});
compiler/forget/packages/js-fuzzer/resources/differential_fuzz_jstest.js
new
+11
@@ -0,0 +1,11 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+function debug(msg) {
6
+ __prettyPrintExtra(msg);
7
+}
8
+
9
+function shouldBe(_a) {
10
+ __prettyPrintExtra((typeof _a == "function" ? _a() : eval(_a)));
11
+}
compiler/forget/packages/js-fuzzer/resources/differential_fuzz_library.js
new
+122
@@ -0,0 +1,122 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+// Helpers for printing in correctness fuzzing.
7
+
8
+// Global helper functions for printing.
9
+var __prettyPrint;
10
+var __prettyPrintExtra;
11
+
12
+// Track caught exceptions.
13
+var __caught = 0;
14
+
15
+// Track a hash of all printed values - printing is cut off after a
16
+// certain size.
17
+var __hash = 0;
18
+
19
+(function() {
20
+ const charCodeAt = String.prototype.charCodeAt;
21
+ const join = Array.prototype.join;
22
+ const map = Array.prototype.map;
23
+ const substring = String.prototype.substring;
24
+ const toString = Object.prototype.toString;
25
+
26
+ // Same as in mjsunit.js.
27
+ const classOf = function(object) {
28
+ // Argument must not be null or undefined.
29
+ const string = toString.call(object);
30
+ // String has format [object <ClassName>].
31
+ return substring.call(string, 8, string.length - 1);
32
+ };
33
+
34
+ // For standard cases use original prettyPrinted from mjsunit.
35
+ const origPrettyPrinted = prettyPrinted;
36
+
37
+ // Override prettyPrinted with a version that also recusively prints objects
38
+ // and arrays with a depth of 4. We don't track circles, but we'd cut off
39
+ // after a depth of 4 if there are any.
40
+ prettyPrinted = function prettyPrinted(value, depth=4) {
41
+ if (depth <= 0) {
42
+ return "...";
43
+ }
44
+ switch (typeof value) {
45
+ case "object":
46
+ if (value === null) return "null";
47
+ switch (classOf(value)) {
48
+ case "Array":
49
+ return prettyPrintedArray(value, depth);
50
+ case "Object":
51
+ return prettyPrintedObject(value, depth);
52
+ }
53
+ }
54
+ // Fall through to original version for all other types.
55
+ return origPrettyPrinted(value);
56
+ }
57
+
58
+ // Helper for pretty array with depth.
59
+ function prettyPrintedArray(array, depth) {
60
+ const result = map.call(array, (value, index, array) => {
61
+ if (value === undefined && !(index in array)) return "";
62
+ return prettyPrinted(value, depth - 1);
63
+ });
64
+ return `[${join.call(result, ", ")}]`;
65
+ }
66
+
67
+ // Helper for pretty objects with depth.
68
+ function prettyPrintedObject(object, depth) {
69
+ const keys = Object.keys(object);
70
+ const prettyValues = map.call(keys, (key) => {
71
+ return `${key}: ${prettyPrinted(object[key], depth - 1)}`;
72
+ });
73
+ const content = join.call(prettyValues, ", ");
74
+ return `${object.constructor.name || "Object"}{${content}}`;
75
+ }
76
+
77
+ // Helper for calculating a hash code of a string.
78
+ function hashCode(str) {
79
+ let hash = 0;
80
+ if (str.length == 0) {
81
+ return hash;
82
+ }
83
+ for (let i = 0; i < str.length; i++) {
84
+ const char = charCodeAt.call(str, i);
85
+ hash = ((hash << 5) - hash) + char;
86
+ hash = hash & hash;
87
+ }
88
+ return hash;
89
+ }
90
+
91
+ // Upper limit for calling extra printing. When reached, hashes of
92
+ // strings are tracked and printed instead.
93
+ let maxExtraPrinting = 100;
94
+
95
+ // Helper for pretty printing.
96
+ __prettyPrint = function(value, extra=false) {
97
+ let str = prettyPrinted(value);
98
+
99
+ // Change __hash with the contents of the full string to
100
+ // keep track of differences also when we don't print.
101
+ const hash = hashCode(str);
102
+ __hash = hashCode(hash + __hash.toString());
103
+
104
+ if (extra && maxExtraPrinting-- <= 0) {
105
+ return;
106
+ }
107
+
108
+ // Cut off long strings to prevent overloading I/O. We still track
109
+ // the hash of the full string.
110
+ if (str.length > 64) {
111
+ const head = substring.call(str, 0, 54);
112
+ const tail = substring.call(str, str.length - 10, str.length - 1);
113
+ str = `${head}[...]${tail}`;
114
+ }
115
+
116
+ print(str);
117
+ };
118
+
119
+ __prettyPrintExtra = function (value) {
120
+ __prettyPrint(value, true);
121
+ }
122
+})();
compiler/forget/packages/js-fuzzer/resources/differential_fuzz_mjsunit.js
new
+8
@@ -0,0 +1,8 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+// Substitute for mjsunit. We reuse prettyPrinted from mjsunit, but only if
7
+// it is loaded. If not, we use this substitute instead.
8
+let prettyPrinted = value => value;
compiler/forget/packages/js-fuzzer/resources/differential_fuzz_suppressions.js
new
+12
@@ -0,0 +1,12 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+// Don't breach stack limit in differential fuzzing as it leads to
7
+// early bailout.
8
+runNearStackLimit = function(f) {
9
+ try {
10
+ f();
11
+ } catch (e) {}
12
+};
compiler/forget/packages/js-fuzzer/resources/differential_fuzz_v8.js
new
+29
@@ -0,0 +1,29 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+// Adjust mjsunit behavior for differential fuzzing.
7
+
8
+// We're not interested in stack traces.
9
+MjsUnitAssertionError = () => {};
10
+
11
+// Do more printing in assertions for more correctness coverage.
12
+failWithMessage = message => { __prettyPrint(message); };
13
+assertSame = (expected, found, name_opt) => { __prettyPrint(found); };
14
+assertNotSame = (expected, found, name_opt) => { __prettyPrint(found); };
15
+assertEquals = (expected, found, name_opt) => { __prettyPrint(found); };
16
+assertNotEquals = (expected, found, name_opt) => { __prettyPrint(found); };
17
+assertNull = (value, name_opt) => { __prettyPrint(value); };
18
+assertNotNull = (value, name_opt) => { __prettyPrint(value); };
19
+
20
+// Suppress optimization status as it leads to false positives.
21
+assertUnoptimized = () => {};
22
+assertOptimized = () => {};
23
+isNeverOptimize = () => {};
24
+isAlwaysOptimize = () => {};
25
+isInterpreted = () => {};
26
+isBaseline = () => {};
27
+isUnoptimized = () => {};
28
+isOptimized = () => {};
29
+isTurboFanned = () => {};
compiler/forget/packages/js-fuzzer/resources/fuzz_library.js
new
+116
@@ -0,0 +1,116 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Slightly modified variants from http://code.fitness/post/2016/01/javascript-enumerate-methods.html.
6
+function __isPropertyOfType(obj, name, type) {
7
+ let desc;
8
+ try {
9
+ desc = Object.getOwnPropertyDescriptor(obj, name);
10
+ } catch(e) {
11
+ return false;
12
+ }
13
+
14
+ if (!desc)
15
+ return false;
16
+
17
+ return typeof type === 'undefined' || typeof desc.value === type;
18
+}
19
+
20
+function __getProperties(obj, type) {
21
+ if (typeof obj === "undefined" || obj === null)
22
+ return [];
23
+
24
+ let properties = [];
25
+ for (let name of Object.getOwnPropertyNames(obj)) {
26
+ if (__isPropertyOfType(obj, name, type))
27
+ properties.push(name);
28
+ }
29
+
30
+ let proto = Object.getPrototypeOf(obj);
31
+ while (proto && proto != Object.prototype) {
32
+ Object.getOwnPropertyNames(proto)
33
+ .forEach (name => {
34
+ if (name !== 'constructor') {
35
+ if (__isPropertyOfType(proto, name, type))
36
+ properties.push(name);
37
+ }
38
+ });
39
+ proto = Object.getPrototypeOf(proto);
40
+ }
41
+ return properties;
42
+}
43
+
44
+function* __getObjects(root = this, level = 0) {
45
+ if (level > 4)
46
+ return;
47
+
48
+ let obj_names = __getProperties(root, 'object');
49
+ for (let obj_name of obj_names) {
50
+ let obj = root[obj_name];
51
+ if (obj === root)
52
+ continue;
53
+
54
+ yield obj;
55
+ yield* __getObjects(obj, level + 1);
56
+ }
57
+}
58
+
59
+function __getRandomObject(seed) {
60
+ let objects = [];
61
+ for (let obj of __getObjects()) {
62
+ objects.push(obj);
63
+ }
64
+
65
+ return objects[seed % objects.length];
66
+}
67
+
68
+function __getRandomProperty(obj, seed) {
69
+ let properties = __getProperties(obj);
70
+ if (!properties.length)
71
+ return undefined;
72
+
73
+ return properties[seed % properties.length];
74
+}
75
+
76
+function __callRandomFunction(obj, seed, ...args)
77
+{
78
+ let functions = __getProperties(obj, 'function');
79
+ if (!functions.length)
80
+ return;
81
+
82
+ let random_function = functions[seed % functions.length];
83
+ try {
84
+ obj[random_function](...args);
85
+ } catch(e) { }
86
+}
87
+
88
+function runNearStackLimit(f) {
89
+ function t() {
90
+ try {
91
+ return t();
92
+ } catch (e) {
93
+ return f();
94
+ }
95
+ };
96
+ try {
97
+ return t();
98
+ } catch (e) {}
99
+}
100
+
101
+// Limit number of times we cause major GCs in tests to reduce hangs
102
+// when called within larger loops.
103
+let __callGC;
104
+(function() {
105
+ let countGC = 0;
106
+ __callGC = function() {
107
+ if (countGC++ < 50) {
108
+ gc();
109
+ }
110
+ };
111
+})();
112
+
113
+// Neuter common test functions.
114
+try { this.failWithMessage = nop; } catch(e) { }
115
+try { this.triggerAssertFalse = nop; } catch(e) { }
116
+try { this.quit = nop; } catch(e) { }
compiler/forget/packages/js-fuzzer/resources/jstest_stubs.js
new
+41
@@ -0,0 +1,41 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Minimally stub out methods from JSTest's standalone-pre.js.
6
+function description(msg) {}
7
+function debug(msg) {}
8
+
9
+function shouldBe(_a) {
10
+ print((typeof _a == "function" ? _a() : eval(_a)));
11
+}
12
+
13
+function shouldBeTrue(_a) { shouldBe(_a); }
14
+function shouldBeFalse(_a) { shouldBe(_a); }
15
+function shouldBeNaN(_a) { shouldBe(_a); }
16
+function shouldBeNull(_a) { shouldBe(_a); }
17
+function shouldNotThrow(_a) { shouldBe(_a); }
18
+function shouldThrow(_a) { shouldBe(_a); }
19
+
20
+function noInline() {}
21
+function finishJSTest() {}
22
+
23
+// Stub out $vm.
24
+try {
25
+ $vm;
26
+} catch(e) {
27
+ const handler = {
28
+ get: function(x, prop) {
29
+ if (prop == Symbol.toPrimitive) {
30
+ return function() { return undefined; };
31
+ }
32
+ return dummy;
33
+ },
34
+ };
35
+ const dummy = new Proxy(function() { return dummy; }, handler);
36
+ this.$vm = dummy;
37
+}
38
+
39
+// Other functions.
40
+function ensureArrayStorage() {}
41
+function transferArrayBuffer() {}
compiler/forget/packages/js-fuzzer/resources/stubs.js
new
+36
@@ -0,0 +1,36 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Helper neuter function.
6
+function nop() { return false; }
7
+
8
+// Stubs for non-standard functions.
9
+try { gc; } catch(e) {
10
+ this.gc = function () {
11
+ for (let i = 0; i < 10000; i++) {
12
+ let s = new String("AAAA" + Math.random());
13
+ }
14
+ }
15
+}
16
+try { uneval; } catch(e) { this.uneval = this.nop; }
17
+
18
+try {
19
+ // For Chakra tests.
20
+ WScript;
21
+} catch(e) {
22
+ this.WScript = new Proxy({}, {
23
+ get(target, name) {
24
+ switch (name) {
25
+ case 'Echo':
26
+ return print;
27
+ default:
28
+ return {};
29
+ }
30
+
31
+ }
32
+ });
33
+}
34
+
35
+try { this.alert = console.log; } catch(e) { }
36
+try { this.print = console.log; } catch(e) { }
compiler/forget/packages/js-fuzzer/run.js
new
+241
@@ -0,0 +1,241 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Description of this file.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const fs = require('fs');
13
+const path = require('path');
14
+
15
+const program = require('commander');
16
+
17
+const corpus = require('./corpus.js');
18
+const differentialScriptMutator = require('./differential_script_mutator.js');
19
+const random = require('./random.js');
20
+const scriptMutator = require('./script_mutator.js');
21
+const sourceHelpers = require('./source_helpers.js');
22
+
23
+// Maximum number of test inputs to use for one fuzz test.
24
+const MAX_TEST_INPUTS_PER_TEST = 10;
25
+
26
+// Base implementations for default or differential fuzzing.
27
+const SCRIPT_MUTATORS = {
28
+ default: scriptMutator.ScriptMutator,
29
+ foozzie: differentialScriptMutator.DifferentialScriptMutator,
30
+};
31
+
32
+function getRandomInputs(primaryCorpus, secondaryCorpora, count) {
33
+ count = random.randInt(2, count);
34
+
35
+ // Choose 40%-80% of inputs from primary corpus.
36
+ const primaryCount = Math.floor(random.uniform(0.4, 0.8) * count);
37
+ count -= primaryCount;
38
+
39
+ let inputs = primaryCorpus.getRandomTestcases(primaryCount);
40
+
41
+ // Split remainder equally between the secondary corpora.
42
+ const secondaryCount = Math.floor(count / secondaryCorpora.length);
43
+
44
+ for (let i = 0; i < secondaryCorpora.length; i++) {
45
+ let currentCount = secondaryCount;
46
+ if (i == secondaryCorpora.length - 1) {
47
+ // Last one takes the remainder.
48
+ currentCount = count;
49
+ }
50
+
51
+ count -= currentCount;
52
+ if (currentCount) {
53
+ inputs = inputs.concat(
54
+ secondaryCorpora[i].getRandomTestcases(currentCount));
55
+ }
56
+ }
57
+
58
+ return random.shuffle(inputs);
59
+}
60
+
61
+function collect(value, total) {
62
+ total.push(value);
63
+ return total;
64
+}
65
+
66
+function overrideSettings(settings, settingOverrides) {
67
+ for (const setting of settingOverrides) {
68
+ const parts = setting.split('=');
69
+ settings[parts[0]] = parseFloat(parts[1]);
70
+ }
71
+}
72
+
73
+function* randomInputGen(engine) {
74
+ const inputDir = path.resolve(program.input_dir);
75
+
76
+ const v8Corpus = new corpus.Corpus(inputDir, 'v8');
77
+ const chakraCorpus = new corpus.Corpus(inputDir, 'chakra');
78
+ const spiderMonkeyCorpus = new corpus.Corpus(inputDir, 'spidermonkey');
79
+ const jscCorpus = new corpus.Corpus(inputDir, 'WebKit/JSTests');
80
+ const crashTestsCorpus = new corpus.Corpus(inputDir, 'CrashTests');
81
+
82
+ for (let i = 0; i < program.no_of_files; i++) {
83
+ let inputs;
84
+ if (engine === 'V8') {
85
+ inputs = getRandomInputs(
86
+ v8Corpus,
87
+ random.shuffle([chakraCorpus, spiderMonkeyCorpus, jscCorpus,
88
+ crashTestsCorpus, v8Corpus]),
89
+ MAX_TEST_INPUTS_PER_TEST);
90
+ } else if (engine == 'chakra') {
91
+ inputs = getRandomInputs(
92
+ chakraCorpus,
93
+ random.shuffle([v8Corpus, spiderMonkeyCorpus, jscCorpus,
94
+ crashTestsCorpus]),
95
+ MAX_TEST_INPUTS_PER_TEST);
96
+ } else if (engine == 'spidermonkey') {
97
+ inputs = getRandomInputs(
98
+ spiderMonkeyCorpus,
99
+ random.shuffle([v8Corpus, chakraCorpus, jscCorpus,
100
+ crashTestsCorpus]),
101
+ MAX_TEST_INPUTS_PER_TEST);
102
+ } else {
103
+ inputs = getRandomInputs(
104
+ jscCorpus,
105
+ random.shuffle([chakraCorpus, spiderMonkeyCorpus, v8Corpus,
106
+ crashTestsCorpus]),
107
+ MAX_TEST_INPUTS_PER_TEST);
108
+ }
109
+
110
+ if (inputs.length > 0) {
111
+ yield inputs;
112
+ }
113
+ }
114
+}
115
+
116
+function* corpusInputGen() {
117
+ const inputCorpus = new corpus.Corpus(
118
+ path.resolve(program.input_dir),
119
+ program.mutate_corpus,
120
+ program.extra_strict);
121
+ for (const input of inputCorpus.getAllTestcases()) {
122
+ yield [input];
123
+ }
124
+}
125
+
126
+function* enumerate(iterable) {
127
+ let i = 0;
128
+ for (const value of iterable) {
129
+ yield [i, value];
130
+ i++;
131
+ }
132
+}
133
+
134
+function main() {
135
+ Error.stackTraceLimit = Infinity;
136
+
137
+ program
138
+ .version('0.0.1')
139
+ .option('-i, --input_dir <path>', 'Input directory.')
140
+ .option('-o, --output_dir <path>', 'Output directory.')
141
+ .option('-n, --no_of_files <n>', 'Output directory.', parseInt)
142
+ .option('-c, --mutate_corpus <name>', 'Mutate single files in a corpus.')
143
+ .option('-e, --extra_strict', 'Additionally parse files in strict mode.')
144
+ .option('-m, --mutate <path>', 'Mutate a file and output results.')
145
+ .option('-s, --setting [setting]', 'Settings overrides.', collect, [])
146
+ .option('-v, --verbose', 'More verbose printing.')
147
+ .option('-z, --zero_settings', 'Zero all settings.')
148
+ .parse(process.argv);
149
+
150
+ const settings = scriptMutator.defaultSettings();
151
+ if (program.zero_settings) {
152
+ for (const key of Object.keys(settings)) {
153
+ settings[key] = 0.0;
154
+ }
155
+ }
156
+
157
+ if (program.setting.length > 0) {
158
+ overrideSettings(settings, program.setting);
159
+ }
160
+
161
+ let app_name = process.env.APP_NAME;
162
+ if (app_name && app_name.endsWith('.exe')) {
163
+ app_name = app_name.substr(0, app_name.length - 4);
164
+ }
165
+
166
+ if (app_name === 'd8' ||
167
+ app_name === 'v8_simple_inspector_fuzzer' ||
168
+ app_name === 'v8_foozzie.py') {
169
+ // V8 supports running the raw d8 executable, the inspector fuzzer or
170
+ // the differential fuzzing harness 'foozzie'.
171
+ settings.engine = 'V8';
172
+ } else if (app_name === 'ch') {
173
+ settings.engine = 'chakra';
174
+ } else if (app_name === 'js') {
175
+ settings.engine = 'spidermonkey';
176
+ } else if (app_name === 'jsc') {
177
+ settings.engine = 'jsc';
178
+ } else {
179
+ console.log('ERROR: Invalid APP_NAME');
180
+ process.exit(1);
181
+ }
182
+
183
+ const mode = process.env.FUZZ_MODE || 'default';
184
+ assert(mode in SCRIPT_MUTATORS, `Unknown mode ${mode}`);
185
+ const mutator = new SCRIPT_MUTATORS[mode](settings);
186
+
187
+ if (program.mutate) {
188
+ const absPath = path.resolve(program.mutate);
189
+ const baseDir = path.dirname(absPath);
190
+ const fileName = path.basename(absPath);
191
+ const input = sourceHelpers.loadSource(
192
+ baseDir, fileName, program.extra_strict);
193
+ const mutated = mutator.mutateMultiple([input]);
194
+ console.log(mutated.code);
195
+ return;
196
+ }
197
+
198
+ let inputGen;
199
+
200
+ if (program.mutate_corpus) {
201
+ inputGen = corpusInputGen();
202
+ } else {
203
+ inputGen = randomInputGen(settings.engine);
204
+ }
205
+
206
+ for (const [i, inputs] of enumerate(inputGen)) {
207
+ const outputPath = path.join(program.output_dir, 'fuzz-' + i + '.js');
208
+
209
+ const start = Date.now();
210
+ const paths = inputs.map(input => input.relPath);
211
+
212
+ try {
213
+ const mutated = mutator.mutateMultiple(inputs);
214
+ fs.writeFileSync(outputPath, mutated.code);
215
+
216
+ if (settings.engine === 'V8' && mutated.flags && mutated.flags.length > 0) {
217
+ const flagsPath = path.join(program.output_dir, 'flags-' + i + '.js');
218
+ fs.writeFileSync(flagsPath, mutated.flags.join(' '));
219
+ }
220
+ } catch (e) {
221
+ if (e.message.startsWith('ENOSPC')) {
222
+ console.log('ERROR: No space left. Bailing out...');
223
+ console.log(e);
224
+ return;
225
+ }
226
+ console.log(`ERROR: Exception during mutate: ${paths}`);
227
+ console.log(e);
228
+ continue;
229
+ } finally {
230
+ if (program.verbose) {
231
+ const duration = Date.now() - start;
232
+ console.log(`Mutating ${paths} took ${duration} ms.`);
233
+ }
234
+ }
235
+ if ((i + 1) % 10 == 0) {
236
+ console.log('Up to ', i + 1);
237
+ }
238
+ }
239
+}
240
+
241
+main();
compiler/forget/packages/js-fuzzer/script_mutator.js
new
+253
@@ -0,0 +1,253 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Script mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const fs = require('fs');
12
+const path = require('path');
13
+
14
+const common = require('./mutators/common.js');
15
+const db = require('./db.js');
16
+const random = require('./random.js');
17
+const sourceHelpers = require('./source_helpers.js');
18
+
19
+const { AddTryCatchMutator } = require('./mutators/try_catch.js');
20
+const { ArrayMutator } = require('./mutators/array_mutator.js');
21
+const { CrossOverMutator } = require('./mutators/crossover_mutator.js');
22
+const { ExpressionMutator } = require('./mutators/expression_mutator.js');
23
+const { FunctionCallMutator } = require('./mutators/function_call_mutator.js');
24
+const { IdentifierNormalizer } = require('./mutators/normalizer.js');
25
+const { NumberMutator } = require('./mutators/number_mutator.js');
26
+const { ObjectMutator } = require('./mutators/object_mutator.js');
27
+const { VariableMutator } = require('./mutators/variable_mutator.js');
28
+const { VariableOrObjectMutator } = require('./mutators/variable_or_object_mutation.js');
29
+
30
+const MAX_EXTRA_MUTATIONS = 5;
31
+
32
+function defaultSettings() {
33
+ return {
34
+ ADD_VAR_OR_OBJ_MUTATIONS: 0.1,
35
+ DIFF_FUZZ_EXTRA_PRINT: 0.1,
36
+ DIFF_FUZZ_TRACK_CAUGHT: 0.4,
37
+ MUTATE_ARRAYS: 0.1,
38
+ MUTATE_CROSSOVER_INSERT: 0.05,
39
+ MUTATE_EXPRESSIONS: 0.1,
40
+ MUTATE_FUNCTION_CALLS: 0.1,
41
+ MUTATE_NUMBERS: 0.05,
42
+ MUTATE_OBJECTS: 0.1,
43
+ MUTATE_VARIABLES: 0.075,
44
+ SCRIPT_MUTATOR_EXTRA_MUTATIONS: 0.2,
45
+ SCRIPT_MUTATOR_SHUFFLE: 0.2,
46
+ };
47
+}
48
+
49
+class Result {
50
+ constructor(code, flags) {
51
+ this.code = code;
52
+ this.flags = flags;
53
+ }
54
+}
55
+
56
+class ScriptMutator {
57
+ constructor(settings, db_path=undefined) {
58
+ // Use process.cwd() to bypass pkg's snapshot filesystem.
59
+ this.mutateDb = new db.MutateDb(db_path || path.join(process.cwd(), 'db'));
60
+ this.mutators = [
61
+ new ArrayMutator(settings),
62
+ new ObjectMutator(settings),
63
+ new VariableMutator(settings),
64
+ new NumberMutator(settings),
65
+ new CrossOverMutator(settings, this.mutateDb),
66
+ new ExpressionMutator(settings),
67
+ new FunctionCallMutator(settings),
68
+ new VariableOrObjectMutator(settings),
69
+ ];
70
+ this.trycatch = new AddTryCatchMutator(settings);
71
+ this.settings = settings;
72
+ }
73
+
74
+ _addMjsunitIfNeeded(dependencies, input) {
75
+ if (dependencies.has('mjsunit')) {
76
+ return;
77
+ }
78
+
79
+ if (!input.absPath.includes('mjsunit')) {
80
+ return;
81
+ }
82
+
83
+ // Find mjsunit.js
84
+ let mjsunitPath = input.absPath;
85
+ while (path.dirname(mjsunitPath) != mjsunitPath &&
86
+ path.basename(mjsunitPath) != 'mjsunit') {
87
+ mjsunitPath = path.dirname(mjsunitPath);
88
+ }
89
+
90
+ if (path.basename(mjsunitPath) == 'mjsunit') {
91
+ mjsunitPath = path.join(mjsunitPath, 'mjsunit.js');
92
+ dependencies.set('mjsunit', sourceHelpers.loadDependencyAbs(
93
+ input.baseDir, mjsunitPath));
94
+ return;
95
+ }
96
+
97
+ console.log('ERROR: Failed to find mjsunit.js');
98
+ }
99
+
100
+ _addSpiderMonkeyShellIfNeeded(dependencies, input) {
101
+ // Find shell.js files
102
+ const shellJsPaths = new Array();
103
+ let currentDir = path.dirname(input.absPath);
104
+
105
+ while (path.dirname(currentDir) != currentDir) {
106
+ const shellJsPath = path.join(currentDir, 'shell.js');
107
+ if (fs.existsSync(shellJsPath)) {
108
+ shellJsPaths.push(shellJsPath);
109
+ }
110
+
111
+ if (currentDir == 'spidermonkey') {
112
+ break;
113
+ }
114
+ currentDir = path.dirname(currentDir);
115
+ }
116
+
117
+ // Add shell.js dependencies in reverse to add ones that are higher up in
118
+ // the directory tree first.
119
+ for (let i = shellJsPaths.length - 1; i >= 0; i--) {
120
+ if (!dependencies.has(shellJsPaths[i])) {
121
+ const dependency = sourceHelpers.loadDependencyAbs(
122
+ input.baseDir, shellJsPaths[i]);
123
+ dependencies.set(shellJsPaths[i], dependency);
124
+ }
125
+ }
126
+ }
127
+
128
+ _addJSTestStubsIfNeeded(dependencies, input) {
129
+ if (dependencies.has('jstest_stubs') ||
130
+ !input.absPath.includes('JSTests')) {
131
+ return;
132
+ }
133
+ dependencies.set(
134
+ 'jstest_stubs', sourceHelpers.loadResource('jstest_stubs.js'));
135
+ }
136
+
137
+ mutate(source) {
138
+ let mutators = this.mutators.slice();
139
+ let annotations = [];
140
+ if (random.choose(this.settings.SCRIPT_MUTATOR_SHUFFLE)){
141
+ annotations.push(' Script mutator: using shuffled mutators');
142
+ random.shuffle(mutators);
143
+ }
144
+
145
+ if (random.choose(this.settings.SCRIPT_MUTATOR_EXTRA_MUTATIONS)){
146
+ for (let i = random.randInt(1, MAX_EXTRA_MUTATIONS); i > 0; i--) {
147
+ let mutator = random.single(this.mutators);
148
+ mutators.push(mutator);
149
+ annotations.push(` Script mutator: extra ${mutator.constructor.name}`);
150
+ }
151
+ }
152
+
153
+ // Try-catch wrapping should always be the last mutation.
154
+ mutators.push(this.trycatch);
155
+
156
+ for (const mutator of mutators) {
157
+ mutator.mutate(source);
158
+ }
159
+
160
+ for (const annotation of annotations.reverse()) {
161
+ sourceHelpers.annotateWithComment(source.ast, annotation);
162
+ }
163
+ }
164
+
165
+ // Returns parsed dependencies for inputs.
166
+ resolveInputDependencies(inputs) {
167
+ const dependencies = new Map();
168
+
169
+ // Resolve test harness files.
170
+ inputs.forEach(input => {
171
+ try {
172
+ // TODO(machenbach): Some harness files contain load expressions
173
+ // that are not recursively resolved. We already remove them, but we
174
+ // also need to load the dependencies they point to.
175
+ this._addJSTestStubsIfNeeded(dependencies, input);
176
+ this._addMjsunitIfNeeded(dependencies, input)
177
+ this._addSpiderMonkeyShellIfNeeded(dependencies, input);
178
+ } catch (e) {
179
+ console.log(
180
+ 'ERROR: Failed to resolve test harness for', input.relPath);
181
+ throw e;
182
+ }
183
+ });
184
+
185
+ // Resolve dependencies loaded within the input files.
186
+ inputs.forEach(input => {
187
+ try {
188
+ input.loadDependencies(dependencies);
189
+ } catch (e) {
190
+ console.log(
191
+ 'ERROR: Failed to resolve dependencies for', input.relPath);
192
+ throw e;
193
+ }
194
+ });
195
+
196
+ // Map.values() returns values in insertion order.
197
+ return Array.from(dependencies.values());
198
+ }
199
+
200
+ // Combines input dependencies with fuzzer resources.
201
+ resolveDependencies(inputs) {
202
+ const dependencies = this.resolveInputDependencies(inputs);
203
+
204
+ // Add stubs for non-standard functions in the beginning.
205
+ dependencies.unshift(sourceHelpers.loadResource('stubs.js'));
206
+
207
+ // Add our fuzzing support helpers. This also overrides some common test
208
+ // functions from earlier dependencies that cause early bailouts.
209
+ dependencies.push(sourceHelpers.loadResource('fuzz_library.js'));
210
+
211
+ return dependencies;
212
+ }
213
+
214
+ // Normalizes, combines and mutates multiple inputs.
215
+ mutateInputs(inputs) {
216
+ const normalizerMutator = new IdentifierNormalizer();
217
+
218
+ for (const [index, input] of inputs.entries()) {
219
+ try {
220
+ normalizerMutator.mutate(input);
221
+ } catch (e) {
222
+ console.log('ERROR: Failed to normalize ', input.relPath);
223
+ throw e;
224
+ }
225
+
226
+ common.setSourceLoc(input, index, inputs.length);
227
+ }
228
+
229
+ // Combine ASTs into one. This is so that mutations have more context to
230
+ // cross over content between ASTs (e.g. variables).
231
+ const combinedSource = common.concatPrograms(inputs);
232
+ this.mutate(combinedSource);
233
+
234
+ return combinedSource;
235
+ }
236
+
237
+ mutateMultiple(inputs) {
238
+ // High level operation:
239
+ // 1) Compute dependencies from inputs.
240
+ // 2) Normalize, combine and mutate inputs.
241
+ // 3) Generate code with dependency code prepended.
242
+ const dependencies = this.resolveDependencies(inputs);
243
+ const combinedSource = this.mutateInputs(inputs);
244
+ const code = sourceHelpers.generateCode(combinedSource, dependencies);
245
+ const flags = common.concatFlags(dependencies.concat([combinedSource]));
246
+ return new Result(code, flags);
247
+ }
248
+}
249
+
250
+module.exports = {
251
+ defaultSettings: defaultSettings,
252
+ ScriptMutator: ScriptMutator,
253
+};
compiler/forget/packages/js-fuzzer/source_helpers.js
new
+466
@@ -0,0 +1,466 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Source loader.
7
+ */
8
+
9
+const fs = require('fs');
10
+const fsPath = require('path');
11
+
12
+const { EOL } = require('os');
13
+
14
+const babelGenerator = require('@babel/generator').default;
15
+const babelTraverse = require('@babel/traverse').default;
16
+const babelTypes = require('@babel/types');
17
+const babylon = require('@babel/parser');
18
+
19
+const exceptions = require('./exceptions.js');
20
+
21
+const SCRIPT = Symbol('SCRIPT');
22
+const MODULE = Symbol('MODULE');
23
+
24
+const V8_BUILTIN_PREFIX = '__V8Builtin';
25
+const V8_REPLACE_BUILTIN_REGEXP = new RegExp(
26
+ V8_BUILTIN_PREFIX + '(\\w+)\\(', 'g');
27
+
28
+const BABYLON_OPTIONS = {
29
+ sourceType: 'script',
30
+ allowReturnOutsideFunction: true,
31
+ tokens: false,
32
+ ranges: false,
33
+ plugins: [
34
+ 'asyncGenerators',
35
+ 'bigInt',
36
+ 'classPrivateMethods',
37
+ 'classPrivateProperties',
38
+ 'classProperties',
39
+ 'doExpressions',
40
+ 'exportDefaultFrom',
41
+ 'nullishCoalescingOperator',
42
+ 'numericSeparator',
43
+ 'objectRestSpread',
44
+ 'optionalCatchBinding',
45
+ 'optionalChaining',
46
+ ],
47
+}
48
+
49
+const BABYLON_REPLACE_VAR_OPTIONS = Object.assign({}, BABYLON_OPTIONS);
50
+BABYLON_REPLACE_VAR_OPTIONS['placeholderPattern'] = /^VAR_[0-9]+$/;
51
+
52
+function _isV8OrSpiderMonkeyLoad(path) {
53
+ // 'load' and 'loadRelativeToScript' used by V8 and SpiderMonkey.
54
+ return (babelTypes.isIdentifier(path.node.callee) &&
55
+ (path.node.callee.name == 'load' ||
56
+ path.node.callee.name == 'loadRelativeToScript') &&
57
+ path.node.arguments.length == 1 &&
58
+ babelTypes.isStringLiteral(path.node.arguments[0]));
59
+}
60
+
61
+function _isChakraLoad(path) {
62
+ // 'WScript.LoadScriptFile' used by Chakra.
63
+ // TODO(ochang): The optional second argument can change semantics ("self",
64
+ // "samethread", "crossthread" etc).
65
+ // Investigate whether if it still makes sense to include them.
66
+ return (babelTypes.isMemberExpression(path.node.callee) &&
67
+ babelTypes.isIdentifier(path.node.callee.property) &&
68
+ path.node.callee.property.name == 'LoadScriptFile' &&
69
+ path.node.arguments.length >= 1 &&
70
+ babelTypes.isStringLiteral(path.node.arguments[0]));
71
+}
72
+
73
+function _findPath(path, caseSensitive=true) {
74
+ // If the path exists, return the path. Otherwise return null. Used to handle
75
+ // case insensitive matches for Chakra tests.
76
+ if (caseSensitive) {
77
+ return fs.existsSync(path) ? path : null;
78
+ }
79
+
80
+ path = fsPath.normalize(fsPath.resolve(path));
81
+ const pathComponents = path.split(fsPath.sep);
82
+ let realPath = fsPath.resolve(fsPath.sep);
83
+
84
+ for (let i = 1; i < pathComponents.length; i++) {
85
+ // For each path component, do a directory listing to see if there is a case
86
+ // insensitive match.
87
+ const curListing = fs.readdirSync(realPath);
88
+ let realComponent = null;
89
+ for (const component of curListing) {
90
+ if (i < pathComponents.length - 1 &&
91
+ !fs.statSync(fsPath.join(realPath, component)).isDirectory()) {
92
+ continue;
93
+ }
94
+
95
+ if (component.toLowerCase() == pathComponents[i].toLowerCase()) {
96
+ realComponent = component;
97
+ break;
98
+ }
99
+ }
100
+
101
+ if (!realComponent) {
102
+ return null;
103
+ }
104
+
105
+ realPath = fsPath.join(realPath, realComponent);
106
+ }
107
+
108
+ return realPath;
109
+}
110
+
111
+function _findDependentCodePath(filePath, baseDirectory, caseSensitive=true) {
112
+ const fullPath = fsPath.join(baseDirectory, filePath);
113
+
114
+ const realPath = _findPath(fullPath, caseSensitive)
115
+ if (realPath) {
116
+ // Check base directory of current file.
117
+ return realPath;
118
+ }
119
+
120
+ while (fsPath.dirname(baseDirectory) != baseDirectory) {
121
+ // Walk up the directory tree.
122
+ const testPath = fsPath.join(baseDirectory, filePath);
123
+ const realPath = _findPath(testPath, caseSensitive)
124
+ if (realPath) {
125
+ return realPath;
126
+ }
127
+
128
+ baseDirectory = fsPath.dirname(baseDirectory);
129
+ }
130
+
131
+ return null;
132
+}
133
+
134
+/**
135
+ * Removes V8/Spidermonkey/Chakra load expressions in a source AST and returns
136
+ * their string values in an array.
137
+ *
138
+ * @param {string} originalFilePath Absolute path to file.
139
+ * @param {AST} ast Babel AST of the sources.
140
+ */
141
+function resolveLoads(originalFilePath, ast) {
142
+ const dependencies = [];
143
+
144
+ babelTraverse(ast, {
145
+ CallExpression(path) {
146
+ const isV8OrSpiderMonkeyLoad = _isV8OrSpiderMonkeyLoad(path);
147
+ const isChakraLoad = _isChakraLoad(path);
148
+ if (!isV8OrSpiderMonkeyLoad && !isChakraLoad) {
149
+ return;
150
+ }
151
+
152
+ let loadValue = path.node.arguments[0].extra.rawValue;
153
+ // Normalize Windows path separators.
154
+ loadValue = loadValue.replace(/\\/g, fsPath.sep);
155
+
156
+ // Remove load call.
157
+ path.remove();
158
+
159
+ const resolvedPath = _findDependentCodePath(
160
+ loadValue, fsPath.dirname(originalFilePath), !isChakraLoad);
161
+ if (!resolvedPath) {
162
+ console.log('ERROR: Could not find dependent path for', loadValue);
163
+ return;
164
+ }
165
+
166
+ if (exceptions.isTestSkippedAbs(resolvedPath)) {
167
+ // Dependency is skipped.
168
+ return;
169
+ }
170
+
171
+ // Add the dependency path.
172
+ dependencies.push(resolvedPath);
173
+ }
174
+ });
175
+ return dependencies;
176
+}
177
+
178
+function isStrictDirective(directive) {
179
+ return (directive.value &&
180
+ babelTypes.isDirectiveLiteral(directive.value) &&
181
+ directive.value.value === 'use strict');
182
+}
183
+
184
+function replaceV8Builtins(code) {
185
+ return code.replace(/%(\w+)\(/g, V8_BUILTIN_PREFIX + '$1(');
186
+}
187
+
188
+function restoreV8Builtins(code) {
189
+ return code.replace(V8_REPLACE_BUILTIN_REGEXP, '%$1(');
190
+}
191
+
192
+function maybeUseStict(code, useStrict) {
193
+ if (useStrict) {
194
+ return `'use strict';${EOL}${EOL}${code}`;
195
+ }
196
+ return code;
197
+}
198
+
199
+class Source {
200
+ constructor(baseDir, relPath, flags, dependentPaths) {
201
+ this.baseDir = baseDir;
202
+ this.relPath = relPath;
203
+ this.flags = flags;
204
+ this.dependentPaths = dependentPaths;
205
+ this.sloppy = exceptions.isTestSloppyRel(relPath);
206
+ }
207
+
208
+ get absPath() {
209
+ return fsPath.join(this.baseDir, this.relPath);
210
+ }
211
+
212
+ /**
213
+ * Specifies if the source isn't compatible with strict mode.
214
+ */
215
+ isSloppy() {
216
+ return this.sloppy;
217
+ }
218
+
219
+ /**
220
+ * Specifies if the source has a top-level 'use strict' directive.
221
+ */
222
+ isStrict() {
223
+ throw Error('Not implemented');
224
+ }
225
+
226
+ /**
227
+ * Generates the code as a string without any top-level 'use strict'
228
+ * directives. V8 natives that were replaced before parsing are restored.
229
+ */
230
+ generateNoStrict() {
231
+ throw Error('Not implemented');
232
+ }
233
+
234
+ /**
235
+ * Recursively adds dependencies of a this source file.
236
+ *
237
+ * @param {Map} dependencies Dependency map to which to add new, parsed
238
+ * dependencies unless they are already in the map.
239
+ * @param {Map} visitedDependencies A set for avoiding loops.
240
+ */
241
+ loadDependencies(dependencies, visitedDependencies) {
242
+ visitedDependencies = visitedDependencies || new Set();
243
+
244
+ for (const absPath of this.dependentPaths) {
245
+ if (dependencies.has(absPath) ||
246
+ visitedDependencies.has(absPath)) {
247
+ // Already added.
248
+ continue;
249
+ }
250
+
251
+ // Prevent infinite loops.
252
+ visitedDependencies.add(absPath);
253
+
254
+ // Recursively load dependencies.
255
+ const dependency = loadDependencyAbs(this.baseDir, absPath);
256
+ dependency.loadDependencies(dependencies, visitedDependencies);
257
+
258
+ // Add the dependency.
259
+ dependencies.set(absPath, dependency);
260
+ }
261
+ }
262
+}
263
+
264
+/**
265
+ * Represents sources whose AST can be manipulated.
266
+ */
267
+class ParsedSource extends Source {
268
+ constructor(ast, baseDir, relPath, flags, dependentPaths) {
269
+ super(baseDir, relPath, flags, dependentPaths);
270
+ this.ast = ast;
271
+ }
272
+
273
+ isStrict() {
274
+ return !!this.ast.program.directives.filter(isStrictDirective).length;
275
+ }
276
+
277
+ generateNoStrict() {
278
+ const allDirectives = this.ast.program.directives;
279
+ this.ast.program.directives = this.ast.program.directives.filter(
280
+ directive => !isStrictDirective(directive));
281
+ try {
282
+ const code = babelGenerator(this.ast.program, {comments: true}).code;
283
+ return restoreV8Builtins(code);
284
+ } finally {
285
+ this.ast.program.directives = allDirectives;
286
+ }
287
+ }
288
+}
289
+
290
+/**
291
+ * Represents sources with cached code.
292
+ */
293
+class CachedSource extends Source {
294
+ constructor(source) {
295
+ super(source.baseDir, source.relPath, source.flags, source.dependentPaths);
296
+ this.use_strict = source.isStrict();
297
+ this.code = source.generateNoStrict();
298
+ }
299
+
300
+ isStrict() {
301
+ return this.use_strict;
302
+ }
303
+
304
+ generateNoStrict() {
305
+ return this.code;
306
+ }
307
+}
308
+
309
+/**
310
+ * Read file path into an AST.
311
+ *
312
+ * Post-processes the AST by replacing V8 natives and removing disallowed
313
+ * natives, as well as removing load expressions and adding the paths-to-load
314
+ * as meta data.
315
+ */
316
+function loadSource(baseDir, relPath, parseStrict=false) {
317
+ const absPath = fsPath.resolve(fsPath.join(baseDir, relPath));
318
+ const data = fs.readFileSync(absPath, 'utf-8');
319
+
320
+ if (guessType(data) !== SCRIPT) {
321
+ return null;
322
+ }
323
+
324
+ const preprocessed = maybeUseStict(replaceV8Builtins(data), parseStrict);
325
+ const ast = babylon.parse(preprocessed, BABYLON_OPTIONS);
326
+
327
+ removeComments(ast);
328
+ cleanAsserts(ast);
329
+ annotateWithOriginalPath(ast, relPath);
330
+
331
+ const flags = loadFlags(data);
332
+ const dependentPaths = resolveLoads(absPath, ast);
333
+
334
+ return new ParsedSource(ast, baseDir, relPath, flags, dependentPaths);
335
+}
336
+
337
+function guessType(data) {
338
+ if (data.includes('// MODULE')) {
339
+ return MODULE;
340
+ }
341
+
342
+ return SCRIPT;
343
+}
344
+
345
+/**
346
+ * Remove existing comments.
347
+ */
348
+function removeComments(ast) {
349
+ babelTraverse(ast, {
350
+ enter(path) {
351
+ babelTypes.removeComments(path.node);
352
+ }
353
+ });
354
+}
355
+
356
+/**
357
+ * Removes "Assert" from strings in spidermonkey shells or from older
358
+ * crash tests: https://crbug.com/1068268
359
+ */
360
+function cleanAsserts(ast) {
361
+ function replace(string) {
362
+ return string == null ? null : string.replace(/[Aa]ssert/g, '*****t');
363
+ }
364
+ babelTraverse(ast, {
365
+ StringLiteral(path) {
366
+ path.node.value = replace(path.node.value);
367
+ path.node.extra.raw = replace(path.node.extra.raw);
368
+ path.node.extra.rawValue = replace(path.node.extra.rawValue);
369
+ },
370
+ TemplateElement(path) {
371
+ path.node.value.cooked = replace(path.node.value.cooked);
372
+ path.node.value.raw = replace(path.node.value.raw);
373
+ },
374
+ });
375
+}
376
+
377
+/**
378
+ * Annotate code with top-level comment.
379
+ */
380
+function annotateWithComment(ast, comment) {
381
+ if (ast.program && ast.program.body && ast.program.body.length > 0) {
382
+ babelTypes.addComment(
383
+ ast.program.body[0], 'leading', comment, true);
384
+ }
385
+}
386
+
387
+/**
388
+ * Annotate code with original file path.
389
+ */
390
+function annotateWithOriginalPath(ast, relPath) {
391
+ annotateWithComment(ast, ' Original: ' + relPath);
392
+}
393
+
394
+// TODO(machenbach): Move this into the V8 corpus. Other test suites don't
395
+// use this flag logic.
396
+function loadFlags(data) {
397
+ const result = [];
398
+ let count = 0;
399
+ for (const line of data.split('\n')) {
400
+ if (count++ > 40) {
401
+ // No need to process the whole file. Flags are always added after the
402
+ // copyright header.
403
+ break;
404
+ }
405
+ const match = line.match(/\/\/ Flags:\s*(.*)\s*/);
406
+ if (!match) {
407
+ continue;
408
+ }
409
+ for (const flag of exceptions.filterFlags(match[1].split(/\s+/))) {
410
+ result.push(flag);
411
+ }
412
+ }
413
+ return result;
414
+}
415
+
416
+// Convenience helper to load sources with absolute paths.
417
+function loadSourceAbs(baseDir, absPath) {
418
+ return loadSource(baseDir, fsPath.relative(baseDir, absPath));
419
+}
420
+
421
+const dependencyCache = new Map();
422
+
423
+function loadDependency(baseDir, relPath) {
424
+ const absPath = fsPath.join(baseDir, relPath);
425
+ let dependency = dependencyCache.get(absPath);
426
+ if (!dependency) {
427
+ const source = loadSource(baseDir, relPath);
428
+ dependency = new CachedSource(source);
429
+ dependencyCache.set(absPath, dependency);
430
+ }
431
+ return dependency;
432
+}
433
+
434
+function loadDependencyAbs(baseDir, absPath) {
435
+ return loadDependency(baseDir, fsPath.relative(baseDir, absPath));
436
+}
437
+
438
+// Convenience helper to load a file from the resources directory.
439
+function loadResource(fileName) {
440
+ return loadDependency(__dirname, fsPath.join('resources', fileName));
441
+}
442
+
443
+function generateCode(source, dependencies=[]) {
444
+ const allSources = dependencies.concat([source]);
445
+ const codePieces = allSources.map(
446
+ source => source.generateNoStrict());
447
+
448
+ if (allSources.some(source => source.isStrict()) &&
449
+ !allSources.some(source => source.isSloppy())) {
450
+ codePieces.unshift('\'use strict\';');
451
+ }
452
+
453
+ return codePieces.join(EOL + EOL);
454
+}
455
+
456
+module.exports = {
457
+ BABYLON_OPTIONS: BABYLON_OPTIONS,
458
+ BABYLON_REPLACE_VAR_OPTIONS: BABYLON_REPLACE_VAR_OPTIONS,
459
+ annotateWithComment: annotateWithComment,
460
+ generateCode: generateCode,
461
+ loadDependencyAbs: loadDependencyAbs,
462
+ loadResource: loadResource,
463
+ loadSource: loadSource,
464
+ loadSourceAbs: loadSourceAbs,
465
+ ParsedSource: ParsedSource,
466
+}
compiler/forget/packages/js-fuzzer/test/helpers.js
new
+75
@@ -0,0 +1,75 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test helpers.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const path = require('path');
13
+const fs = require('fs');
14
+
15
+const sourceHelpers = require('../source_helpers.js');
16
+
17
+const BASE_DIR = path.join(path.dirname(__dirname), 'test_data');
18
+const DB_DIR = path.join(BASE_DIR, 'fake_db');
19
+
20
+const HEADER = `// Copyright 2020 the V8 project authors. All rights reserved.
21
+// Use of this source code is governed by a BSD-style license that can be
22
+// found in the LICENSE file.
23
+
24
+`;
25
+
26
+/**
27
+ * Create a function that returns one of `probs` when called. It rotates
28
+ * through the values. Useful to replace `random.random()` in tests using
29
+ * the probabilities that trigger different interesting cases.
30
+ */
31
+function cycleProbabilitiesFun(probs) {
32
+ let index = 0;
33
+ return () => {
34
+ index = index % probs.length;
35
+ return probs[index++];
36
+ };
37
+}
38
+
39
+/**
40
+ * Replace Math.random with a deterministic pseudo-random function.
41
+ */
42
+function deterministicRandom(sandbox) {
43
+ let seed = 1;
44
+ function random() {
45
+ const x = Math.sin(seed++) * 10000;
46
+ return x - Math.floor(x);
47
+ }
48
+ sandbox.stub(Math, 'random').callsFake(() => { return random(); });
49
+}
50
+
51
+function loadTestData(relPath) {
52
+ return sourceHelpers.loadSource(BASE_DIR, relPath);
53
+}
54
+
55
+function assertExpectedResult(expectedPath, result) {
56
+ const absPath = path.join(BASE_DIR, expectedPath);
57
+ if (process.env.GENERATE) {
58
+ fs.writeFileSync(absPath, HEADER + result.trim() + '\n');
59
+ return;
60
+ }
61
+
62
+ // Omit copyright header when comparing files.
63
+ const expected = fs.readFileSync(absPath, 'utf-8').trim().split('\n');
64
+ expected.splice(0, 4);
65
+ assert.strictEqual(expected.join('\n'), result.trim());
66
+}
67
+
68
+module.exports = {
69
+ BASE_DIR: BASE_DIR,
70
+ DB_DIR: DB_DIR,
71
+ assertExpectedResult: assertExpectedResult,
72
+ cycleProbabilitiesFun: cycleProbabilitiesFun,
73
+ deterministicRandom: deterministicRandom,
74
+ loadTestData: loadTestData,
75
+}
compiler/forget/packages/js-fuzzer/test/test_available_variables.js
new
+34
@@ -0,0 +1,34 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for mutating variables
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTraverse = require('@babel/traverse').default;
12
+
13
+const common = require('../mutators/common.js');
14
+const helpers = require('./helpers.js');
15
+
16
+describe('Available variables and functions', () => {
17
+ it('test', () => {
18
+ const source = helpers.loadTestData('available_variables.js');
19
+ const result = new Array();
20
+
21
+ babelTraverse(source.ast, {
22
+ CallExpression(path) {
23
+ result.push({
24
+ variables: common.availableVariables(path),
25
+ functions: common.availableFunctions(path),
26
+ });
27
+ }
28
+ });
29
+
30
+ helpers.assertExpectedResult(
31
+ 'available_variables_expected.js',
32
+ JSON.stringify(result, null, 2));
33
+ });
34
+});
compiler/forget/packages/js-fuzzer/test/test_corpus.js
new
+113
@@ -0,0 +1,113 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Corpus loading.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const sinon = require('sinon');
13
+
14
+const exceptions = require('../exceptions.js');
15
+const corpus = require('../corpus.js');
16
+
17
+const sandbox = sinon.createSandbox();
18
+
19
+function testSoftSkipped(count, softSkipped, paths) {
20
+ sandbox.stub(exceptions, 'getSoftSkipped').callsFake(() => {
21
+ return softSkipped;
22
+ });
23
+ const mjsunit = new corpus.Corpus('test_data', 'mjsunit_softskipped');
24
+ const cases = mjsunit.getRandomTestcasePaths(count);
25
+ assert.deepEqual(paths, cases);
26
+}
27
+
28
+describe('Loading corpus', () => {
29
+ afterEach(() => {
30
+ sandbox.restore();
31
+ });
32
+
33
+ it('keeps all tests with no soft-skipped tests', () => {
34
+ sandbox.stub(Math, 'random').callsFake(() => 0.9);
35
+ testSoftSkipped(
36
+ 3,
37
+ [],
38
+ ['mjsunit_softskipped/permitted.js',
39
+ 'mjsunit_softskipped/object-literal.js',
40
+ 'mjsunit_softskipped/regress/binaryen-123.js']);
41
+ });
42
+
43
+ it('choose one test with no soft-skipped tests', () => {
44
+ sandbox.stub(Math, 'random').callsFake(() => 0.9);
45
+ testSoftSkipped(
46
+ 1,
47
+ [],
48
+ ['mjsunit_softskipped/permitted.js']);
49
+ });
50
+
51
+ it('keeps soft-skipped tests', () => {
52
+ sandbox.stub(Math, 'random').callsFake(() => 0.9);
53
+ testSoftSkipped(
54
+ 1,
55
+ [/^binaryen.*\.js/, 'object-literal.js'],
56
+ ['mjsunit_softskipped/permitted.js']);
57
+ });
58
+
59
+ it('keeps no generated soft-skipped tests', () => {
60
+ sandbox.stub(Math, 'random').callsFake(() => 0.9);
61
+ const softSkipped = [
62
+ // Correctly listed full relative path of test case.
63
+ 'mjsunit_softskipped/regress/binaryen-123.js',
64
+ // Only basename doesn't match.
65
+ 'object-literal.js',
66
+ // Only pieces of the path don't match.
67
+ 'mjsunit_softskipped',
68
+ ];
69
+ sandbox.stub(exceptions, 'getGeneratedSoftSkipped').callsFake(
70
+ () => { return new Set(softSkipped); });
71
+ testSoftSkipped(
72
+ 2,
73
+ // None soft-skipped for basenames and regexps.
74
+ [],
75
+ // Only binaryen-123.js gets filtered out.
76
+ ['mjsunit_softskipped/object-literal.js',
77
+ 'mjsunit_softskipped/permitted.js']);
78
+ });
79
+
80
+ it('keeps soft-skipped tests by chance', () => {
81
+ sandbox.stub(Math, 'random').callsFake(() => 0);
82
+ testSoftSkipped(
83
+ 3,
84
+ [/^binaryen.*\.js/, 'object-literal.js'],
85
+ ['mjsunit_softskipped/object-literal.js',
86
+ 'mjsunit_softskipped/regress/binaryen-123.js',
87
+ 'mjsunit_softskipped/permitted.js']);
88
+ });
89
+
90
+ it('caches relative paths', () => {
91
+ sandbox.stub(Math, 'random').callsFake(() => 0);
92
+ sandbox.stub(exceptions, 'getSoftSkipped').callsFake(
93
+ () => { return ['object-literal.js']; });
94
+ const generatedSoftSkipped = [
95
+ 'mjsunit_softskipped/regress/binaryen-123.js',
96
+ ];
97
+ sandbox.stub(exceptions, 'getGeneratedSoftSkipped').callsFake(
98
+ () => { return new Set(generatedSoftSkipped); });
99
+ const mjsunit = new corpus.Corpus('test_data' , 'mjsunit_softskipped');
100
+ assert.deepEqual(
101
+ ['mjsunit_softskipped/object-literal.js',
102
+ 'mjsunit_softskipped/regress/binaryen-123.js'],
103
+ mjsunit.softSkippedFiles);
104
+ assert.deepEqual(
105
+ ['mjsunit_softskipped/permitted.js'],
106
+ mjsunit.permittedFiles);
107
+ assert.deepEqual(
108
+ ['mjsunit_softskipped/permitted.js',
109
+ 'mjsunit_softskipped/object-literal.js',
110
+ 'mjsunit_softskipped/regress/binaryen-123.js'],
111
+ Array.from(mjsunit.relFiles()));
112
+ });
113
+});
compiler/forget/packages/js-fuzzer/test/test_db.js
new
+33
@@ -0,0 +1,33 @@
1
+// Copyright 2021 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test the script building the DB.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const { execSync } = require("child_process");
13
+const fs = require('fs');
14
+const path = require('path');
15
+const tempy = require('tempy');
16
+
17
+function buildDb(inputDir, corpusName, outputDir) {
18
+ execSync(
19
+ `node build_db.js -i ${inputDir} -o ${outputDir} ${corpusName}`,
20
+ {stdio: ['pipe']});
21
+}
22
+
23
+describe('DB tests', () => {
24
+ // Test feeds an expression that does not apply.
25
+ it('omits erroneous expressions', () => {
26
+ const outPath = tempy.directory();
27
+ buildDb('test_data/db', 'this', outPath);
28
+ const indexFile = path.join(outPath, 'index.json');
29
+ const indexJSON = JSON.parse(fs.readFileSync(indexFile), 'utf-8');
30
+ assert.deepEqual(
31
+ indexJSON, {"statements": [], "superStatements": [], "all": []});
32
+ });
33
+});
compiler/forget/packages/js-fuzzer/test/test_differential_fuzz.js
new
+141
@@ -0,0 +1,141 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for differential fuzzing.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const program = require('commander');
13
+const sinon = require('sinon');
14
+
15
+const helpers = require('./helpers.js');
16
+const scriptMutator = require('../script_mutator.js');
17
+const sourceHelpers = require('../source_helpers.js');
18
+const random = require('../random.js');
19
+
20
+const { DifferentialFuzzMutator, DifferentialFuzzSuppressions } = require(
21
+ '../mutators/differential_fuzz_mutator.js');
22
+const { DifferentialScriptMutator } = require(
23
+ '../differential_script_mutator.js');
24
+
25
+const sandbox = sinon.createSandbox();
26
+
27
+function testMutators(settings, mutatorClass, inputFile, expectedFile) {
28
+ const source = helpers.loadTestData('differential_fuzz/' + inputFile);
29
+
30
+ const mutator = new mutatorClass(settings);
31
+ mutator.mutate(source);
32
+
33
+ const mutated = sourceHelpers.generateCode(source);
34
+ helpers.assertExpectedResult(
35
+ 'differential_fuzz/' + expectedFile, mutated);
36
+}
37
+
38
+describe('Differential fuzzing', () => {
39
+ beforeEach(() => {
40
+ // Zero settings for all mutators.
41
+ this.settings = scriptMutator.defaultSettings();
42
+ for (const key of Object.keys(this.settings)) {
43
+ this.settings[key] = 0.0;
44
+ }
45
+ // By default, deterministically use all mutations of differential
46
+ // fuzzing.
47
+ this.settings['DIFF_FUZZ_EXTRA_PRINT'] = 1.0;
48
+ this.settings['DIFF_FUZZ_TRACK_CAUGHT'] = 1.0;
49
+
50
+ // Fake fuzzer being called with --input_dir flag.
51
+ this.oldInputDir = program.input_dir;
52
+ program.input_dir = helpers.BASE_DIR;
53
+ });
54
+
55
+ afterEach(() => {
56
+ sandbox.restore();
57
+ program.input_dir = this.oldInputDir;
58
+ });
59
+
60
+ it('applies suppressions', () => {
61
+ // This selects the first random variable when replacing .arguments.
62
+ sandbox.stub(random, 'single').callsFake(a => a[0]);
63
+ testMutators(
64
+ this.settings,
65
+ DifferentialFuzzSuppressions,
66
+ 'suppressions.js',
67
+ 'suppressions_expected.js');
68
+ });
69
+
70
+ it('adds extra printing', () => {
71
+ testMutators(
72
+ this.settings,
73
+ DifferentialFuzzMutator,
74
+ 'mutations.js',
75
+ 'mutations_expected.js');
76
+ });
77
+
78
+ it('does no extra printing', () => {
79
+ this.settings['DIFF_FUZZ_EXTRA_PRINT'] = 0.0;
80
+ testMutators(
81
+ this.settings,
82
+ DifferentialFuzzMutator,
83
+ 'exceptions.js',
84
+ 'exceptions_expected.js');
85
+ });
86
+
87
+ it('runs end to end', () => {
88
+ // Don't choose any zeroed settings or IGNORE_DEFAULT_PROB in try-catch
89
+ // mutator. Choose using original flags with >= 2%.
90
+ const chooseOrigFlagsProb = 0.2;
91
+ sandbox.stub(random, 'choose').callsFake((p) => p >= chooseOrigFlagsProb);
92
+
93
+ // Fake build directory from which two json configurations for flags are
94
+ // loaded.
95
+ const env = {
96
+ APP_DIR: 'test_data/differential_fuzz',
97
+ GENERATE: process.env.GENERATE,
98
+ };
99
+ sandbox.stub(process, 'env').value(env);
100
+
101
+ // Fake loading resources and instead load one fixed fake file for each.
102
+ sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => {
103
+ return helpers.loadTestData('differential_fuzz/fake_resource.js');
104
+ });
105
+
106
+ // Load input files.
107
+ const files = [
108
+ 'differential_fuzz/input1.js',
109
+ 'differential_fuzz/input2.js',
110
+ ];
111
+ const sources = files.map(helpers.loadTestData);
112
+
113
+ // Apply top-level fuzzing, with all probabilistic configs switched off.
114
+ this.settings['DIFF_FUZZ_EXTRA_PRINT'] = 0.0;
115
+ this.settings['DIFF_FUZZ_TRACK_CAUGHT'] = 0.0;
116
+ const mutator = new DifferentialScriptMutator(
117
+ this.settings, helpers.DB_DIR);
118
+ const mutated = mutator.mutateMultiple(sources);
119
+ helpers.assertExpectedResult(
120
+ 'differential_fuzz/combined_expected.js', mutated.code);
121
+
122
+ // Flags for v8_foozzie.py are calculated from v8_fuzz_experiments.json and
123
+ // v8_fuzz_flags.json in test_data/differential_fuzz.
124
+ const expectedFlags = [
125
+ '--first-config=ignition',
126
+ '--second-config=ignition_turbo',
127
+ '--second-d8=d8',
128
+ '--second-config-extra-flags=--foo1',
129
+ '--second-config-extra-flags=--foo2',
130
+ '--first-config-extra-flags=--flag1',
131
+ '--second-config-extra-flags=--flag1',
132
+ '--first-config-extra-flags=--flag2',
133
+ '--second-config-extra-flags=--flag2',
134
+ '--first-config-extra-flags=--flag3',
135
+ '--second-config-extra-flags=--flag3',
136
+ '--first-config-extra-flags=--flag4',
137
+ '--second-config-extra-flags=--flag4'
138
+ ];
139
+ assert.deepEqual(expectedFlags, mutated.flags);
140
+ });
141
+});
compiler/forget/packages/js-fuzzer/test/test_differential_fuzz_library.js
new
+113
@@ -0,0 +1,113 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for the differential-fuzzing library files.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const fs = require('fs');
13
+const path = require('path');
14
+
15
+const libPath = path.resolve(
16
+ path.join(__dirname, '..', 'resources', 'differential_fuzz_library.js'));
17
+const code = fs.readFileSync(libPath, 'utf-8');
18
+
19
+// We wire the print function to write to this result variable.
20
+const resultDummy = 'let result; const print = text => { result = text; };';
21
+
22
+// The prettyPrinted function from mjsunit is reused in the library.
23
+const prettyPrint = 'let prettyPrinted = value => value;';
24
+
25
+const hookedUpCode = resultDummy + prettyPrint + code;
26
+
27
+// Runs the library, adds test code and verifies the result.
28
+function testLibrary(testCode, expected) {
29
+ // The code isn't structured as a module. The test code is expected to
30
+ // evaluate to a result which we store in actual.
31
+ const actual = eval(hookedUpCode + testCode);
32
+ assert.deepEqual(expected, actual);
33
+}
34
+
35
+describe('Differential fuzzing library', () => {
36
+ it('prints objects', () => {
37
+ testLibrary(
38
+ '__prettyPrint([0, 1, 2, 3]); result;',
39
+ '[0, 1, 2, 3]');
40
+ testLibrary(
41
+ '__prettyPrint({0: 1, 2: 3}); result;',
42
+ 'Object{0: 1, 2: 3}');
43
+ testLibrary(
44
+ 'const o = {}; o.k = 42;__prettyPrint(o); result;',
45
+ 'Object{k: 42}');
46
+ });
47
+
48
+ it('cuts off deep nesting', () => {
49
+ // We print only until a nesting depth of 4.
50
+ testLibrary(
51
+ '__prettyPrint({0: [1, 2, [3, {4: []}]]}); result;',
52
+ 'Object{0: [1, 2, [3, Object{4: ...}]]}');
53
+ });
54
+
55
+ it('cuts off long strings', () => {
56
+ const long = new Array(66).join('a');
57
+ const head = new Array(55).join('a');
58
+ const tail = new Array(10).join('a');
59
+ testLibrary(
60
+ `__prettyPrint("${long}"); result;`,
61
+ `${head}[...]${tail}`);
62
+ // If the string gets longer, the cut-off version is still the same.
63
+ const veryLong = new Array(100).join('a');
64
+ testLibrary(
65
+ `__prettyPrint("${veryLong}"); result;`,
66
+ `${head}[...]${tail}`);
67
+ });
68
+
69
+ it('tracks hash difference', () => {
70
+ // Test that we track a hash value for each string we print.
71
+ const long = new Array(66).join('a');
72
+ testLibrary(
73
+ `__prettyPrint("${long}"); __hash;`,
74
+ 2097980794);
75
+ // Test that the hash value differs, also when the cut-off result doesn't.
76
+ const veryLong = new Array(100).join('a');
77
+ testLibrary(
78
+ `__prettyPrint("${veryLong}"); __hash;`,
79
+ -428472866);
80
+ // Test that repeated calls update the hash.
81
+ testLibrary(
82
+ `__prettyPrint("${long}");__prettyPrint("${long}"); __hash;`,
83
+ -909224493);
84
+ });
85
+
86
+ it('limits extra printing', () => {
87
+ // Test that after exceeding the limit for calling extra printing, there
88
+ // is no new string printed (in the test case no new result added).
89
+ testLibrary(
90
+ 'for (let i = 0; i < 20; i++) __prettyPrintExtra(i); result;',
91
+ '19');
92
+ testLibrary(
93
+ 'for (let i = 0; i < 101; i++) __prettyPrintExtra(i); result;',
94
+ '99');
95
+ testLibrary(
96
+ 'for (let i = 0; i < 102; i++) __prettyPrintExtra(i); result;',
97
+ '99');
98
+ });
99
+
100
+ it('tracks hash after limit', () => {
101
+ // Test that after exceeding the limit for calling extra printing, the
102
+ // hash is still updated.
103
+ testLibrary(
104
+ 'for (let i = 0; i < 20; i++) __prettyPrintExtra(i); __hash;',
105
+ -945753644);
106
+ testLibrary(
107
+ 'for (let i = 0; i < 101; i++) __prettyPrintExtra(i); __hash;',
108
+ 1907055979);
109
+ testLibrary(
110
+ 'for (let i = 0; i < 102; i++) __prettyPrintExtra(i); __hash;',
111
+ -590842070);
112
+ });
113
+});
compiler/forget/packages/js-fuzzer/test/test_load.js
new
+69
@@ -0,0 +1,69 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test normalization.
7
+ */
8
+
9
+'use strict';
10
+
11
+const sinon = require('sinon');
12
+
13
+const helpers = require('./helpers.js');
14
+const sourceHelpers = require('../source_helpers.js');
15
+
16
+const { ScriptMutator } = require('../script_mutator.js');
17
+
18
+const sandbox = sinon.createSandbox();
19
+
20
+function testLoad(testPath, expectedPath) {
21
+ const mutator = new ScriptMutator({}, helpers.DB_DIR);
22
+ const source = helpers.loadTestData(testPath);
23
+ const dependencies = mutator.resolveInputDependencies([source]);
24
+ const code = sourceHelpers.generateCode(source, dependencies);
25
+ helpers.assertExpectedResult(expectedPath, code);
26
+}
27
+
28
+describe('V8 dependencies', () => {
29
+ it('test', () => {
30
+ testLoad(
31
+ 'mjsunit/test_load.js',
32
+ 'mjsunit/test_load_expected.js');
33
+
34
+ });
35
+ it('does not loop indefinitely', () => {
36
+ testLoad(
37
+ 'mjsunit/test_load_self.js',
38
+ 'mjsunit/test_load_self_expected.js');
39
+ });
40
+});
41
+
42
+describe('Chakra dependencies', () => {
43
+ it('test', () => {
44
+ testLoad(
45
+ 'chakra/load.js',
46
+ 'chakra/load_expected.js');
47
+ });
48
+});
49
+
50
+describe('JSTest dependencies', () => {
51
+ afterEach(() => {
52
+ sandbox.restore();
53
+ });
54
+
55
+ it('test', () => {
56
+ const fakeStubs = sourceHelpers.loadSource(
57
+ helpers.BASE_DIR, 'JSTests/fake_stub.js');
58
+ sandbox.stub(sourceHelpers, 'loadResource').callsFake(() => fakeStubs);
59
+ testLoad('JSTests/load.js', 'JSTests/load_expected.js');
60
+ });
61
+});
62
+
63
+describe('SpiderMonkey dependencies', () => {
64
+ it('test', () => {
65
+ testLoad(
66
+ 'spidermonkey/test/load.js',
67
+ 'spidermonkey/test/load_expected.js');
68
+ });
69
+});
compiler/forget/packages/js-fuzzer/test/test_mutate_arrays.js
new
+47
@@ -0,0 +1,47 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for mutating arrays
7
+ */
8
+
9
+'use strict';
10
+
11
+const sinon = require('sinon');
12
+
13
+const babylon = require('@babel/parser');
14
+
15
+const common = require('../mutators/common.js');
16
+const helpers = require('./helpers.js');
17
+const scriptMutator = require('../script_mutator.js');
18
+const sourceHelpers = require('../source_helpers.js');
19
+
20
+const {ArrayMutator} = require('../mutators/array_mutator.js');
21
+
22
+const sandbox = sinon.createSandbox();
23
+
24
+describe('Mutate arrays', () => {
25
+ afterEach(() => {
26
+ sandbox.restore();
27
+ });
28
+
29
+ it('performs all mutations', () => {
30
+ // Make random operations deterministic.
31
+ sandbox.stub(common, 'randomValue').callsFake(
32
+ () => babylon.parseExpression('""'));
33
+ helpers.deterministicRandom(sandbox);
34
+
35
+ const source = helpers.loadTestData('mutate_arrays.js');
36
+
37
+ const settings = scriptMutator.defaultSettings();
38
+ settings['MUTATE_ARRAYS'] = 1.0;
39
+
40
+ const mutator = new ArrayMutator(settings);
41
+ mutator.mutate(source);
42
+
43
+ const mutated = sourceHelpers.generateCode(source);
44
+ helpers.assertExpectedResult(
45
+ 'mutate_arrays_expected.js', mutated);
46
+ });
47
+});
compiler/forget/packages/js-fuzzer/test/test_mutate_expressions.js
new
+79
@@ -0,0 +1,79 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for mutating expressions
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+
13
+const babelTypes = require('@babel/types');
14
+const sinon = require('sinon');
15
+
16
+const common = require('../mutators/common.js');
17
+const expressionMutator = require('../mutators/expression_mutator.js');
18
+const helpers = require('./helpers.js');
19
+const scriptMutator = require('../script_mutator.js');
20
+const sourceHelpers = require('../source_helpers.js');
21
+const random = require('../random.js');
22
+
23
+const sandbox = sinon.createSandbox();
24
+
25
+function testCloneSiblings(expected_file) {
26
+ const source = helpers.loadTestData('mutate_expressions.js');
27
+
28
+ const settings = scriptMutator.defaultSettings();
29
+ settings['MUTATE_EXPRESSIONS'] = 1.0;
30
+
31
+ const mutator = new expressionMutator.ExpressionMutator(settings);
32
+ mutator.mutate(source);
33
+
34
+ const mutated = sourceHelpers.generateCode(source);
35
+ helpers.assertExpectedResult(expected_file, mutated);
36
+}
37
+
38
+describe('Mutate expressions', () => {
39
+ beforeEach(() => {
40
+ // Select the previous sibling.
41
+ sandbox.stub(random, 'randInt').callsFake((a, b) => b);
42
+ // This chooses cloning siblings.
43
+ sandbox.stub(random, 'random').callsFake(() => 0.8);
44
+ });
45
+
46
+ afterEach(() => {
47
+ sandbox.restore();
48
+ });
49
+
50
+ it('clones previous to current', () => {
51
+ // Keep the order of [previous, current], select previous.
52
+ sandbox.stub(random, 'shuffle').callsFake(a => a);
53
+ // Insert after. Keep returning true for the MUTATE_EXPRESSIONS check.
54
+ sandbox.stub(random, 'choose').callsFake(a => a === 1);
55
+
56
+ testCloneSiblings('mutate_expressions_previous_expected.js');
57
+ });
58
+
59
+ it('clones current to previous', () => {
60
+ // Switch the order of [previous, current], select current.
61
+ sandbox.stub(random, 'shuffle').callsFake(a => [a[1], a[0]]);
62
+ // Insert before.
63
+ sandbox.stub(random, 'choose').callsFake(() => true);
64
+
65
+ testCloneSiblings('mutate_expressions_current_expected.js');
66
+ });
67
+});
68
+
69
+describe('Cloning', () => {
70
+ // Ensure that the source location we add are not cloned.
71
+ it('is not copying added state', () => {
72
+ const source = helpers.loadTestData('mutate_expressions.js');
73
+ common.setSourceLoc(source, 5, 10);
74
+ const noopNode = source.ast.program.body[0];
75
+ assert.equal(0.5, common.getSourceLoc(noopNode));
76
+ const cloned = babelTypes.cloneDeep(noopNode);
77
+ assert.equal(undefined, common.getSourceLoc(cloned));
78
+ });
79
+});
compiler/forget/packages/js-fuzzer/test/test_mutate_function_calls.js
new
+84
@@ -0,0 +1,84 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for mutating funciton calls.
7
+ */
8
+
9
+'use strict';
10
+
11
+const sinon = require('sinon');
12
+
13
+const helpers = require('./helpers.js');
14
+const random = require('../random.js');
15
+const scriptMutator = require('../script_mutator.js');
16
+const sourceHelpers = require('../source_helpers.js');
17
+const functionCallMutator = require('../mutators/function_call_mutator.js');
18
+
19
+const sandbox = sinon.createSandbox();
20
+
21
+function loadAndMutate(input_file) {
22
+ const source = helpers.loadTestData(input_file);
23
+
24
+ const settings = scriptMutator.defaultSettings();
25
+ settings['engine'] = 'V8';
26
+ settings['MUTATE_FUNCTION_CALLS'] = 1.0;
27
+
28
+ const mutator = new functionCallMutator.FunctionCallMutator(settings);
29
+ mutator.mutate(source);
30
+ return source;
31
+}
32
+
33
+describe('Mutate functions', () => {
34
+ afterEach(() => {
35
+ sandbox.restore();
36
+ });
37
+
38
+ it('is robust without available functions', () => {
39
+ sandbox.stub(random, 'random').callsFake(() => { return 0.2; });
40
+
41
+ // We just ensure here that mutating this file doesn't throw.
42
+ loadAndMutate('mutate_function_call.js');
43
+ });
44
+
45
+ it('optimizes functions with turbofan in V8', () => {
46
+ sandbox.stub(random, 'random').callsFake(() => { return 0.5; });
47
+ sandbox.stub(random, 'choose').callsFake(p => true);
48
+
49
+ const source = loadAndMutate('mutate_function_call.js');
50
+ const mutated = sourceHelpers.generateCode(source);
51
+ helpers.assertExpectedResult(
52
+ 'mutate_function_call_expected.js', mutated);
53
+ });
54
+
55
+ it('optimizes functions with maglev in V8', () => {
56
+ sandbox.stub(random, 'random').callsFake(() => { return 0.5; });
57
+ // False-path takes 'Maglev'. Other calls to choose should return
58
+ // true. It's also used to determine if a mutator should be chosen.
59
+ sandbox.stub(random, 'choose').callsFake(p => p == 0.7 ? false : true);
60
+
61
+ const source = loadAndMutate('mutate_function_call.js');
62
+ const mutated = sourceHelpers.generateCode(source);
63
+ helpers.assertExpectedResult(
64
+ 'mutate_function_call_maglev_expected.js', mutated);
65
+ });
66
+
67
+ it('compiles functions in V8 to baseline', () => {
68
+ sandbox.stub(random, 'random').callsFake(() => { return 0.7; });
69
+
70
+ const source = loadAndMutate('mutate_function_call.js');
71
+ const mutated = sourceHelpers.generateCode(source);
72
+ helpers.assertExpectedResult(
73
+ 'mutate_function_call_baseline_expected.js', mutated);
74
+ });
75
+
76
+ it('deoptimizes functions in V8', () => {
77
+ sandbox.stub(random, 'random').callsFake(() => { return 0.8; });
78
+
79
+ const source = loadAndMutate('mutate_function_call.js');
80
+ const mutated = sourceHelpers.generateCode(source);
81
+ helpers.assertExpectedResult(
82
+ 'mutate_function_call_deopt_expected.js', mutated);
83
+ });
84
+});
compiler/forget/packages/js-fuzzer/test/test_mutate_numbers.js
new
+54
@@ -0,0 +1,54 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for mutating variables
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTypes = require('@babel/types');
12
+const sinon = require('sinon');
13
+
14
+const common = require('../mutators/common.js');
15
+const helpers = require('./helpers.js');
16
+const scriptMutator = require('../script_mutator.js');
17
+const sourceHelpers = require('../source_helpers.js');
18
+const numberMutator = require('../mutators/number_mutator.js');
19
+const random = require('../random.js');
20
+
21
+const sandbox = sinon.createSandbox();
22
+
23
+describe('Mutate numbers', () => {
24
+ beforeEach(() => {
25
+ sandbox.stub(common, 'nearbyRandomNumber').callsFake(
26
+ () => { return babelTypes.numericLiteral(-3) });
27
+ sandbox.stub(common, 'randomInterestingNumber').callsFake(
28
+ () => { return babelTypes.numericLiteral(-4) });
29
+ sandbox.stub(random, 'randInt').callsFake(() => { return -5 });
30
+
31
+ // Interesting cases from number mutator.
32
+ const interestingProbs = [0.009, 0.05, 0.5];
33
+ sandbox.stub(random, 'random').callsFake(
34
+ helpers.cycleProbabilitiesFun(interestingProbs));
35
+ });
36
+
37
+ afterEach(() => {
38
+ sandbox.restore();
39
+ });
40
+
41
+ it('test', () => {
42
+ const source = helpers.loadTestData('mutate_numbers.js');
43
+
44
+ const settings = scriptMutator.defaultSettings();
45
+ settings['MUTATE_NUMBERS'] = 1.0;
46
+
47
+ const mutator = new numberMutator.NumberMutator(settings);
48
+ mutator.mutate(source);
49
+
50
+ const mutated = sourceHelpers.generateCode(source);
51
+ helpers.assertExpectedResult(
52
+ 'mutate_numbers_expected.js', mutated);
53
+ });
54
+});
compiler/forget/packages/js-fuzzer/test/test_mutate_objects.js
new
+47
@@ -0,0 +1,47 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for mutating object expressions
7
+ */
8
+
9
+'use strict';
10
+
11
+const sinon = require('sinon');
12
+
13
+const babylon = require('@babel/parser');
14
+
15
+const common = require('../mutators/common.js');
16
+const helpers = require('./helpers.js');
17
+const scriptMutator = require('../script_mutator.js');
18
+const sourceHelpers = require('../source_helpers.js');
19
+
20
+const {ObjectMutator} = require('../mutators/object_mutator.js');
21
+
22
+const sandbox = sinon.createSandbox();
23
+
24
+describe('Mutate objects', () => {
25
+ afterEach(() => {
26
+ sandbox.restore();
27
+ });
28
+
29
+ it('performs all mutations', () => {
30
+ // Make random operations deterministic.
31
+ sandbox.stub(common, 'randomValue').callsFake(
32
+ () => babylon.parseExpression('""'));
33
+ helpers.deterministicRandom(sandbox);
34
+
35
+ const source = helpers.loadTestData('mutate_objects.js');
36
+
37
+ const settings = scriptMutator.defaultSettings();
38
+ settings['MUTATE_OBJECTS'] = 1.0;
39
+
40
+ const mutator = new ObjectMutator(settings);
41
+ mutator.mutate(source);
42
+
43
+ const mutated = sourceHelpers.generateCode(source);
44
+ helpers.assertExpectedResult(
45
+ 'mutate_objects_expected.js', mutated);
46
+ });
47
+});
compiler/forget/packages/js-fuzzer/test/test_mutate_variable_or_object.js
new
+72
@@ -0,0 +1,72 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test variable-or-object mutator.
7
+ */
8
+
9
+'use strict';
10
+
11
+const babylon = require('@babel/parser');
12
+const sinon = require('sinon');
13
+
14
+const common = require('../mutators/common.js');
15
+const helpers = require('./helpers.js');
16
+const variableOrObject = require('../mutators/variable_or_object_mutation.js');
17
+const random = require('../random.js');
18
+const sourceHelpers = require('../source_helpers.js');
19
+
20
+const sandbox = sinon.createSandbox();
21
+
22
+function testMutations(testPath, expectedPath) {
23
+ const source = helpers.loadTestData(testPath);
24
+
25
+ const mutator = new variableOrObject.VariableOrObjectMutator(
26
+ { ADD_VAR_OR_OBJ_MUTATIONS: 1.0 });
27
+
28
+ mutator.mutate(source);
29
+
30
+ const mutated = sourceHelpers.generateCode(source);
31
+ helpers.assertExpectedResult(expectedPath, mutated);
32
+}
33
+
34
+describe('Variable or object mutator', () => {
35
+ beforeEach(() => {
36
+ // Make before/after insertion deterministic. This also chooses
37
+ // random objects.
38
+ sandbox.stub(random, 'choose').callsFake(() => { return true; });
39
+ // This stubs out the random seed.
40
+ sandbox.stub(random, 'randInt').callsFake(() => { return 123; });
41
+ // Random value is itself dependent on too much randomization.
42
+ sandbox.stub(common, 'randomValue').callsFake(
43
+ () => { return babylon.parseExpression('0'); });
44
+ });
45
+
46
+ afterEach(() => {
47
+ sandbox.restore();
48
+ });
49
+
50
+ it('test', () => {
51
+ let index = 0;
52
+ // Test different cases of _randomVariableOrObjectMutations in
53
+ // variable_or_object_mutation.js.
54
+ const choices = [
55
+ 0.2, // Trigger recursive case.
56
+ 0.3, // Recursion 1: Delete.
57
+ 0.4, // Recursion 2: Property access.
58
+ 0.5, // Random assignment.
59
+ // 0.6 case for randomFunction omitted as it has too much randomization.
60
+ 0.7, // Variable assignment.
61
+ 0.8, // Object.defineProperty.
62
+ 0.9, // Object.defineProperty recursive.
63
+ 0.3, // Recursion 1: Delete.
64
+ 0.4, // Recursion 2: Property access.
65
+ ];
66
+ sandbox.stub(random, 'random').callsFake(
67
+ () => { return choices[index++]; });
68
+ testMutations(
69
+ 'mutate_var_or_obj.js',
70
+ 'mutate_var_or_obj_expected.js');
71
+ });
72
+});
compiler/forget/packages/js-fuzzer/test/test_mutate_variables.js
new
+47
@@ -0,0 +1,47 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Tests for mutating variables
7
+ */
8
+
9
+'use strict';
10
+
11
+const babelTypes = require('@babel/types');
12
+const sinon = require('sinon');
13
+
14
+const common = require('../mutators/common.js');
15
+const helpers = require('./helpers.js');
16
+const scriptMutator = require('../script_mutator.js');
17
+const sourceHelpers = require('../source_helpers.js');
18
+const variableMutator = require('../mutators/variable_mutator.js');
19
+
20
+const sandbox = sinon.createSandbox();
21
+
22
+describe('Mutate variables', () => {
23
+ beforeEach(() => {
24
+ sandbox.stub(
25
+ common, 'randomVariable').callsFake(
26
+ () => { return babelTypes.identifier('REPLACED') });
27
+ });
28
+
29
+ afterEach(() => {
30
+ sandbox.restore();
31
+ });
32
+
33
+ it('test', () => {
34
+
35
+ const source = helpers.loadTestData('mutate_variables.js');
36
+
37
+ const settings = scriptMutator.defaultSettings();
38
+ settings['MUTATE_VARIABLES'] = 1.0;
39
+
40
+ const mutator = new variableMutator.VariableMutator(settings);
41
+ mutator.mutate(source);
42
+
43
+ const mutated = sourceHelpers.generateCode(source);
44
+ helpers.assertExpectedResult(
45
+ 'mutate_variables_expected.js', mutated);
46
+ });
47
+});
compiler/forget/packages/js-fuzzer/test/test_mutation_order.js
new
+56
@@ -0,0 +1,56 @@
1
+// Copyright 2022 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test shuffling mutators and extra mutations.
7
+ *
8
+ * Use minimal probability settings to demonstrate order changes of top-level
9
+ * mutators. Which mutations are used exactly is not relevant to the test and
10
+ * handled pseudo-randomly.
11
+ */
12
+
13
+'use strict';
14
+
15
+const sinon = require('sinon');
16
+
17
+const helpers = require('./helpers.js');
18
+const scriptMutator = require('../script_mutator.js');
19
+const sourceHelpers = require('../source_helpers.js');
20
+const random = require('../random.js');
21
+
22
+const sandbox = sinon.createSandbox();
23
+
24
+describe('Toplevel mutations', () => {
25
+ afterEach(() => {
26
+ sandbox.restore();
27
+ });
28
+
29
+ it('shuffle their order', () => {
30
+ // Make random operations deterministic.
31
+ helpers.deterministicRandom(sandbox);
32
+
33
+ this.settings = {
34
+ ADD_VAR_OR_OBJ_MUTATIONS: 0.0,
35
+ MUTATE_CROSSOVER_INSERT: 0.0,
36
+ MUTATE_EXPRESSIONS: 0.0,
37
+ MUTATE_FUNCTION_CALLS: 1.0,
38
+ MUTATE_NUMBERS: 1.0,
39
+ MUTATE_VARIABLES: 0.0,
40
+ SCRIPT_MUTATOR_SHUFFLE: 1.0,
41
+ SCRIPT_MUTATOR_EXTRA_MUTATIONS: 1.0,
42
+ engine: 'V8',
43
+ testing: true,
44
+ };
45
+
46
+ const source = helpers.loadTestData('mutation_order/input.js');
47
+ const mutator = new scriptMutator.ScriptMutator(this.settings, helpers.DB_DIR);
48
+ const mutated = mutator.mutateInputs([source]);
49
+ const code = sourceHelpers.generateCode(mutated);
50
+
51
+ // The test data should be rich enough to produce a pattern from the
52
+ // FunctionCallMutator that afterwards gets mutated by the NumberMutator.
53
+ helpers.assertExpectedResult(
54
+ 'mutation_order/output_expected.js', code);
55
+ });
56
+});
compiler/forget/packages/js-fuzzer/test/test_normalize.js
new
+42
@@ -0,0 +1,42 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test normalization.
7
+ */
8
+
9
+'use strict';
10
+
11
+const helpers = require('./helpers.js');
12
+const normalizer = require('../mutators/normalizer.js');
13
+const sourceHelpers = require('../source_helpers.js');
14
+
15
+describe('Normalize', () => {
16
+ it('test basic', () => {
17
+ const source = helpers.loadTestData('normalize.js');
18
+
19
+ const mutator = new normalizer.IdentifierNormalizer();
20
+ mutator.mutate(source);
21
+
22
+ const normalized_0 = sourceHelpers.generateCode(source);
23
+ helpers.assertExpectedResult(
24
+ 'normalize_expected_0.js', normalized_0);
25
+
26
+ mutator.mutate(source);
27
+ const normalized_1 = sourceHelpers.generateCode(source);
28
+ helpers.assertExpectedResult(
29
+ 'normalize_expected_1.js', normalized_1);
30
+ });
31
+
32
+ it('test simple_test.js', () => {
33
+ const source = helpers.loadTestData('simple_test.js');
34
+
35
+ const mutator = new normalizer.IdentifierNormalizer();
36
+ mutator.mutate(source);
37
+
38
+ const normalized = sourceHelpers.generateCode(source);
39
+ helpers.assertExpectedResult(
40
+ 'simple_test_expected.js', normalized);
41
+ });
42
+});
compiler/forget/packages/js-fuzzer/test/test_random.js
new
+51
@@ -0,0 +1,51 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test random utilities.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const sinon = require('sinon');
13
+
14
+const { twoBucketSample } = require('../random.js');
15
+
16
+const sandbox = sinon.createSandbox();
17
+
18
+
19
+describe('Two-bucket choosing', () => {
20
+ afterEach(() => {
21
+ sandbox.restore();
22
+ });
23
+
24
+ it('with one empty', () => {
25
+ sandbox.stub(Math, 'random').callsFake(() => 0.5);
26
+ assert.deepEqual([1, 2], twoBucketSample([0, 1, 2], [], 1, 2));
27
+ assert.deepEqual([1, 2], twoBucketSample([], [0, 1, 2], 1, 2));
28
+ assert.deepEqual([0], twoBucketSample([0], [], 1, 1));
29
+ assert.deepEqual([0], twoBucketSample([], [0], 1, 1));
30
+ });
31
+
32
+ it('chooses with 0.3', () => {
33
+ sandbox.stub(Math, 'random').callsFake(() => 0.3);
34
+ assert.deepEqual([1, 2], twoBucketSample([0, 1, 2], [3, 4, 5], 1, 2));
35
+ // Higher factor.
36
+ assert.deepEqual([3, 5], twoBucketSample([0, 1, 2], [3, 4, 5], 4, 2));
37
+ });
38
+
39
+ it('chooses with 0.7', () => {
40
+ sandbox.stub(Math, 'random').callsFake(() => 0.7);
41
+ assert.deepEqual([4, 3], twoBucketSample([0, 1, 2], [3, 4, 5], 1, 2));
42
+ });
43
+
44
+ it('chooses with 0.5', () => {
45
+ sandbox.stub(Math, 'random').callsFake(() => 0.5);
46
+ assert.deepEqual([3], twoBucketSample([0, 1], [2, 3, 4, 5], 1, 1));
47
+ assert.deepEqual([3], twoBucketSample([0, 1, 2, 3], [4, 5], 1, 1));
48
+ // Higher factor.
49
+ assert.deepEqual([4], twoBucketSample([0, 1, 2, 3], [4, 5], 2, 1));
50
+ });
51
+});
compiler/forget/packages/js-fuzzer/test/test_regressions.js
new
+113
@@ -0,0 +1,113 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Regression tests.
7
+ */
8
+
9
+'use strict';
10
+
11
+const assert = require('assert');
12
+const { execSync } = require("child_process");
13
+const fs = require('fs');
14
+const sinon = require('sinon');
15
+const tempfile = require('tempfile');
16
+const tempy = require('tempy');
17
+
18
+const exceptions = require('../exceptions.js');
19
+const helpers = require('./helpers.js');
20
+const scriptMutator = require('../script_mutator.js');
21
+
22
+const sandbox = sinon.createSandbox();
23
+
24
+const SYNTAX_ERROR_RE = /.*SyntaxError.*/
25
+
26
+function createFuzzTest(fake_db, settings, inputFiles) {
27
+ const sources = inputFiles.map(input => helpers.loadTestData(input));
28
+
29
+ const mutator = new scriptMutator.ScriptMutator(settings, fake_db);
30
+ const result = mutator.mutateMultiple(sources);
31
+
32
+ const output_file = tempfile('.js');
33
+ fs.writeFileSync(output_file, result.code);
34
+ return output_file;
35
+}
36
+
37
+function execFile(jsFile) {
38
+ execSync("node " + jsFile, {stdio: ['pipe']});
39
+}
40
+
41
+describe('Regression tests', () => {
42
+ beforeEach(() => {
43
+ helpers.deterministicRandom(sandbox);
44
+
45
+ this.settings = {
46
+ ADD_VAR_OR_OBJ_MUTATIONS: 0.0,
47
+ MUTATE_CROSSOVER_INSERT: 0.0,
48
+ MUTATE_EXPRESSIONS: 0.0,
49
+ MUTATE_FUNCTION_CALLS: 0.0,
50
+ MUTATE_NUMBERS: 0.0,
51
+ MUTATE_VARIABLES: 0.0,
52
+ engine: 'V8',
53
+ testing: true,
54
+ }
55
+ });
56
+
57
+ afterEach(() => {
58
+ sandbox.restore();
59
+ });
60
+
61
+ it('combine strict and with', () => {
62
+ // Test that when a file with "use strict" is used in the inputs,
63
+ // the result is only strict if no other file contains anything
64
+ // prohibited in strict mode (here a with statement).
65
+ // It is assumed that such input files are marked as sloppy in the
66
+ // auto generated exceptions.
67
+ sandbox.stub(exceptions, 'getGeneratedSloppy').callsFake(
68
+ () => { return new Set(['regress/strict/input_with.js']); });
69
+ const file = createFuzzTest(
70
+ 'test_data/regress/strict/db',
71
+ this.settings,
72
+ ['regress/strict/input_strict.js', 'regress/strict/input_with.js']);
73
+ execFile(file);
74
+ });
75
+
76
+ it('combine strict and delete', () => {
77
+ // As above with unqualified delete.
78
+ sandbox.stub(exceptions, 'getGeneratedSloppy').callsFake(
79
+ () => { return new Set(['regress/strict/input_delete.js']); });
80
+ const file = createFuzzTest(
81
+ 'test_data/regress/strict/db',
82
+ this.settings,
83
+ ['regress/strict/input_strict.js', 'regress/strict/input_delete.js']);
84
+ execFile(file);
85
+ });
86
+
87
+ it('mutates negative value', () => {
88
+ // This tests that the combination of number, function call and expression
89
+ // mutator does't produce an update expression.
90
+ // Previously the 1 in -1 was replaced with another negative number leading
91
+ // to e.g. -/*comment/*-2. Then cloning the expression removed the
92
+ // comment and produced --2 in the end.
93
+ this.settings['MUTATE_NUMBERS'] = 1.0;
94
+ this.settings['MUTATE_FUNCTION_CALLS'] = 1.0;
95
+ this.settings['MUTATE_EXPRESSIONS'] = 1.0;
96
+ const file = createFuzzTest(
97
+ 'test_data/regress/numbers/db',
98
+ this.settings,
99
+ ['regress/numbers/input_negative.js']);
100
+ execFile(file);
101
+ });
102
+
103
+ it('mutates indices', () => {
104
+ // Test that indices are not replaced with a negative number causing a
105
+ // syntax error (e.g. {-1: ""}).
106
+ this.settings['MUTATE_NUMBERS'] = 1.0;
107
+ const file = createFuzzTest(
108
+ 'test_data/regress/numbers/db',
109
+ this.settings,
110
+ ['regress/numbers/input_indices.js']);
111
+ execFile(file);
112
+ });
113
+});
compiler/forget/packages/js-fuzzer/test/test_try_catch.js
new
+85
@@ -0,0 +1,85 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test normalization.
7
+ */
8
+
9
+'use strict';
10
+
11
+const sinon = require('sinon');
12
+
13
+const common = require('../mutators/common.js');
14
+const helpers = require('./helpers.js');
15
+const random = require('../random.js');
16
+const sourceHelpers = require('../source_helpers.js');
17
+const tryCatch = require('../mutators/try_catch.js');
18
+
19
+const sandbox = sinon.createSandbox();
20
+
21
+function loadSource() {
22
+ return helpers.loadTestData('try_catch.js');
23
+}
24
+
25
+function testTryCatch(source, expected) {
26
+ const mutator = new tryCatch.AddTryCatchMutator();
27
+ mutator.mutate(source);
28
+
29
+ const mutated = sourceHelpers.generateCode(source);
30
+ helpers.assertExpectedResult(expected, mutated);
31
+}
32
+
33
+describe('Try catch', () => {
34
+ afterEach(() => {
35
+ sandbox.restore();
36
+ });
37
+
38
+ // Wrap on exit, hence wrap everything nested.
39
+ it('wraps all', () => {
40
+ sandbox.stub(random, 'choose').callsFake(() => { return false; });
41
+ sandbox.stub(random, 'random').callsFake(() => { return 0.7; });
42
+ testTryCatch(loadSource(), 'try_catch_expected.js');
43
+ });
44
+
45
+ // Wrap on enter and skip.
46
+ it('wraps toplevel', () => {
47
+ sandbox.stub(random, 'choose').callsFake(() => { return false; });
48
+ sandbox.stub(random, 'random').callsFake(() => { return 0.04; });
49
+ const source = loadSource();
50
+
51
+ // Fake source fraction 0.1 (i.e. the second of 10 files).
52
+ // Probability for toplevel try-catch is 0.05.
53
+ common.setSourceLoc(source, 1, 10);
54
+
55
+ testTryCatch(source, 'try_catch_toplevel_expected.js');
56
+ });
57
+
58
+ // Choose the rare case of skipping try-catch.
59
+ it('wraps nothing', () => {
60
+ sandbox.stub(random, 'choose').callsFake(() => { return false; });
61
+ sandbox.stub(random, 'random').callsFake(() => { return 0.01; });
62
+ const source = loadSource();
63
+
64
+ // Fake source fraction 0.1 (i.e. the second of 10 files).
65
+ // Probability for skipping is 0.02.
66
+ common.setSourceLoc(source, 1, 10);
67
+
68
+ testTryCatch(source, 'try_catch_nothing_expected.js');
69
+ });
70
+
71
+ // Choose to alter the target probability to 0.9 resulting in skipping
72
+ // all try-catch.
73
+ it('wraps nothing with high target probability', () => {
74
+ sandbox.stub(random, 'choose').callsFake(() => { return true; });
75
+ sandbox.stub(random, 'uniform').callsFake(() => { return 0.9; });
76
+ sandbox.stub(random, 'random').callsFake(() => { return 0.8; });
77
+ const source = loadSource();
78
+
79
+ // Fake source fraction 0.9 (i.e. the last of 10 files).
80
+ // Probability for skipping is 0.81 (0.9 * 0.9).
81
+ common.setSourceLoc(source, 9, 10);
82
+
83
+ testTryCatch(source, 'try_catch_alternate_expected.js');
84
+ });
85
+});
compiler/forget/packages/js-fuzzer/test_data/JSTests/fake_stub.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+print("Fake stub");
compiler/forget/packages/js-fuzzer/test_data/JSTests/load.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+print("JSTest");
compiler/forget/packages/js-fuzzer/test_data/JSTests/load_expected.js
new
+9
@@ -0,0 +1,9 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: JSTests/fake_stub.js
6
+print("Fake stub");
7
+
8
+// Original: JSTests/load.js
9
+print("JSTest");
compiler/forget/packages/js-fuzzer/test_data/available_variables.js
new
+33
@@ -0,0 +1,33 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+let __v_0 = 0;
6
+let __v_1 = 0;
7
+
8
+console.log(__v_0, __v_1, __f_0, __f_1);
9
+
10
+function __f_0() {
11
+ let __v_2 = 0;
12
+ console.log(__v_0, __v_1, __v_2, __f_0, __f_1);
13
+}
14
+
15
+let __v_3 = 0;
16
+
17
+console.log(__v_0, __v_1, __v_3, __f_0, __f_1);
18
+
19
+function __f_1(__v_7) {
20
+ let __v_4 = 0;
21
+
22
+ console.log(__v_0, __v_1, __v_3, __v_4, __v_7, __f_0, __f_1);
23
+ {
24
+ let __v_5 = 0;
25
+ var __v_6 = 0;
26
+ console.log(__v_0, __v_1, __v_3, __v_4, __v_5, __v_6, __v_7, __f_0, __f_1, __f_2);
27
+ function __f_2 () {};
28
+ console.log(__v_0, __v_1, __v_3, __v_4, __v_5, __v_6, __v_7, __f_0, __f_1, __f_2);
29
+ }
30
+ // TODO(machenbach): __f_2 is missing as available identifier.
31
+ console.log(__v_0, __v_1, __v_3, __v_4, __v_6, __v_7, __f_0, __f_1, __f_2);
32
+}
33
+console.log(__v_0, __v_1, __v_3, __f_0, __f_1);
compiler/forget/packages/js-fuzzer/test_data/available_variables_expected.js
new
+270
@@ -0,0 +1,270 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+[
6
+ {
7
+ "variables": [
8
+ {
9
+ "type": "Identifier",
10
+ "name": "__v_0"
11
+ },
12
+ {
13
+ "type": "Identifier",
14
+ "name": "__v_1"
15
+ }
16
+ ],
17
+ "functions": [
18
+ {
19
+ "type": "Identifier",
20
+ "name": "__f_0"
21
+ },
22
+ {
23
+ "type": "Identifier",
24
+ "name": "__f_1"
25
+ }
26
+ ]
27
+ },
28
+ {
29
+ "variables": [
30
+ {
31
+ "type": "Identifier",
32
+ "name": "__v_2"
33
+ },
34
+ {
35
+ "type": "Identifier",
36
+ "name": "__v_0"
37
+ },
38
+ {
39
+ "type": "Identifier",
40
+ "name": "__v_1"
41
+ }
42
+ ],
43
+ "functions": [
44
+ {
45
+ "type": "Identifier",
46
+ "name": "__f_0"
47
+ },
48
+ {
49
+ "type": "Identifier",
50
+ "name": "__f_1"
51
+ }
52
+ ]
53
+ },
54
+ {
55
+ "variables": [
56
+ {
57
+ "type": "Identifier",
58
+ "name": "__v_0"
59
+ },
60
+ {
61
+ "type": "Identifier",
62
+ "name": "__v_1"
63
+ },
64
+ {
65
+ "type": "Identifier",
66
+ "name": "__v_3"
67
+ }
68
+ ],
69
+ "functions": [
70
+ {
71
+ "type": "Identifier",
72
+ "name": "__f_0"
73
+ },
74
+ {
75
+ "type": "Identifier",
76
+ "name": "__f_1"
77
+ }
78
+ ]
79
+ },
80
+ {
81
+ "variables": [
82
+ {
83
+ "type": "Identifier",
84
+ "name": "__v_7"
85
+ },
86
+ {
87
+ "type": "Identifier",
88
+ "name": "__v_4"
89
+ },
90
+ {
91
+ "type": "Identifier",
92
+ "name": "__v_0"
93
+ },
94
+ {
95
+ "type": "Identifier",
96
+ "name": "__v_1"
97
+ },
98
+ {
99
+ "type": "Identifier",
100
+ "name": "__v_3"
101
+ }
102
+ ],
103
+ "functions": [
104
+ {
105
+ "type": "Identifier",
106
+ "name": "__f_0"
107
+ },
108
+ {
109
+ "type": "Identifier",
110
+ "name": "__f_1"
111
+ }
112
+ ]
113
+ },
114
+ {
115
+ "variables": [
116
+ {
117
+ "type": "Identifier",
118
+ "name": "__v_5"
119
+ },
120
+ {
121
+ "type": "Identifier",
122
+ "name": "__v_7"
123
+ },
124
+ {
125
+ "type": "Identifier",
126
+ "name": "__v_4"
127
+ },
128
+ {
129
+ "type": "Identifier",
130
+ "name": "__v_6"
131
+ },
132
+ {
133
+ "type": "Identifier",
134
+ "name": "__v_0"
135
+ },
136
+ {
137
+ "type": "Identifier",
138
+ "name": "__v_1"
139
+ },
140
+ {
141
+ "type": "Identifier",
142
+ "name": "__v_3"
143
+ }
144
+ ],
145
+ "functions": [
146
+ {
147
+ "type": "Identifier",
148
+ "name": "__f_2"
149
+ },
150
+ {
151
+ "type": "Identifier",
152
+ "name": "__f_0"
153
+ },
154
+ {
155
+ "type": "Identifier",
156
+ "name": "__f_1"
157
+ }
158
+ ]
159
+ },
160
+ {
161
+ "variables": [
162
+ {
163
+ "type": "Identifier",
164
+ "name": "__v_5"
165
+ },
166
+ {
167
+ "type": "Identifier",
168
+ "name": "__v_7"
169
+ },
170
+ {
171
+ "type": "Identifier",
172
+ "name": "__v_4"
173
+ },
174
+ {
175
+ "type": "Identifier",
176
+ "name": "__v_6"
177
+ },
178
+ {
179
+ "type": "Identifier",
180
+ "name": "__v_0"
181
+ },
182
+ {
183
+ "type": "Identifier",
184
+ "name": "__v_1"
185
+ },
186
+ {
187
+ "type": "Identifier",
188
+ "name": "__v_3"
189
+ }
190
+ ],
191
+ "functions": [
192
+ {
193
+ "type": "Identifier",
194
+ "name": "__f_2"
195
+ },
196
+ {
197
+ "type": "Identifier",
198
+ "name": "__f_0"
199
+ },
200
+ {
201
+ "type": "Identifier",
202
+ "name": "__f_1"
203
+ }
204
+ ]
205
+ },
206
+ {
207
+ "variables": [
208
+ {
209
+ "type": "Identifier",
210
+ "name": "__v_7"
211
+ },
212
+ {
213
+ "type": "Identifier",
214
+ "name": "__v_4"
215
+ },
216
+ {
217
+ "type": "Identifier",
218
+ "name": "__v_6"
219
+ },
220
+ {
221
+ "type": "Identifier",
222
+ "name": "__v_0"
223
+ },
224
+ {
225
+ "type": "Identifier",
226
+ "name": "__v_1"
227
+ },
228
+ {
229
+ "type": "Identifier",
230
+ "name": "__v_3"
231
+ }
232
+ ],
233
+ "functions": [
234
+ {
235
+ "type": "Identifier",
236
+ "name": "__f_0"
237
+ },
238
+ {
239
+ "type": "Identifier",
240
+ "name": "__f_1"
241
+ }
242
+ ]
243
+ },
244
+ {
245
+ "variables": [
246
+ {
247
+ "type": "Identifier",
248
+ "name": "__v_0"
249
+ },
250
+ {
251
+ "type": "Identifier",
252
+ "name": "__v_1"
253
+ },
254
+ {
255
+ "type": "Identifier",
256
+ "name": "__v_3"
257
+ }
258
+ ],
259
+ "functions": [
260
+ {
261
+ "type": "Identifier",
262
+ "name": "__f_0"
263
+ },
264
+ {
265
+ "type": "Identifier",
266
+ "name": "__f_1"
267
+ }
268
+ ]
269
+ }
270
+]
compiler/forget/packages/js-fuzzer/test_data/chakra/dir/load3.js
new
+6
@@ -0,0 +1,6 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+WScript.LoadScriptFile("..\\load2.js", "self");
6
+console.log('load3');
compiler/forget/packages/js-fuzzer/test_data/chakra/load.js
new
+9
@@ -0,0 +1,9 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+if (this.WScript && this.WScript.LoadScriptFile) {
6
+ WScript.LoadScriptFile("load1.js");
7
+}
8
+
9
+console.log('load.js');
compiler/forget/packages/js-fuzzer/test_data/chakra/load1.js
new
+8
@@ -0,0 +1,8 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Test case insensitivity.
6
+WScript.LoadScriptFile("DIR\\LoAd3.js");
7
+
8
+console.log('load1.js');
compiler/forget/packages/js-fuzzer/test_data/chakra/load2.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+console.log('load2.js');
compiler/forget/packages/js-fuzzer/test_data/chakra/load_expected.js
new
+17
@@ -0,0 +1,17 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: chakra/load2.js
6
+console.log('load2.js');
7
+
8
+// Original: chakra/dir/load3.js
9
+console.log('load3');
10
+
11
+// Original: chakra/load1.js
12
+console.log('load1.js');
13
+
14
+// Original: chakra/load.js
15
+if (this.WScript && this.WScript.LoadScriptFile) {}
16
+
17
+console.log('load.js');
compiler/forget/packages/js-fuzzer/test_data/cross_over_mutator_class_input.js
new
+11
@@ -0,0 +1,11 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+class __C {
6
+ foo() {
7
+ let __v_0 = 2;
8
+ let __v_1 = 2;
9
+ Math.pow(__v_0, __v_1);
10
+ }
11
+}
compiler/forget/packages/js-fuzzer/test_data/db/this/file.js
new
+9
@@ -0,0 +1,9 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+function C() {
6
+ this.c = "c";
7
+}
8
+
9
+var c = new C();
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/combined_expected.js
new
+59
@@ -0,0 +1,59 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: differential_fuzz/fake_resource.js
6
+print("I'm a resource.");
7
+
8
+// Original: differential_fuzz/fake_resource.js
9
+print("I'm a resource.");
10
+
11
+// Original: differential_fuzz/fake_resource.js
12
+print("I'm a resource.");
13
+
14
+// Original: differential_fuzz/fake_resource.js
15
+print("I'm a resource.");
16
+
17
+// Original: differential_fuzz/fake_resource.js
18
+print("I'm a resource.");
19
+
20
+/* DifferentialFuzzMutator: Print variables and exceptions from section */
21
+try {
22
+ print("Hash: " + __hash);
23
+ print("Caught: " + __caught);
24
+} catch (e) {}
25
+
26
+print("v8-foozzie source: differential_fuzz/input1.js");
27
+
28
+// Original: differential_fuzz/input1.js
29
+try {
30
+ var __v_0 = 0;
31
+} catch (e) {}
32
+
33
+try {
34
+ /* DifferentialFuzzMutator: Pretty printing */
35
+ __prettyPrintExtra(__v_0);
36
+} catch (e) {}
37
+
38
+/* DifferentialFuzzMutator: Print variables and exceptions from section */
39
+try {
40
+ print("Hash: " + __hash);
41
+ print("Caught: " + __caught);
42
+
43
+ __prettyPrint(__v_0);
44
+} catch (e) {}
45
+
46
+print("v8-foozzie source: differential_fuzz/input2.js");
47
+
48
+// Original: differential_fuzz/input2.js
49
+let __v_1 = 1;
50
+
51
+/* DifferentialFuzzMutator: Print variables and exceptions from section */
52
+try {
53
+ print("Hash: " + __hash);
54
+ print("Caught: " + __caught);
55
+
56
+ __prettyPrint(__v_0);
57
+
58
+ __prettyPrint(__v_1);
59
+} catch (e) {}
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/exceptions.js
new
+8
@@ -0,0 +1,8 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+try {
7
+ let __v_0 = boom;
8
+} catch (e) {}
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/exceptions_expected.js
new
+16
@@ -0,0 +1,16 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: differential_fuzz/exceptions.js
6
+try {
7
+ let __v_0 = boom;
8
+} catch (e) {
9
+ __caught++;
10
+}
11
+
12
+/* DifferentialFuzzMutator: Print variables and exceptions from section */
13
+try {
14
+ print("Hash: " + __hash);
15
+ print("Caught: " + __caught);
16
+} catch (e) {}
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/fake_resource.js
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+// This file represents anything loaded from the resources directory.
7
+print("I'm a resource.");
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/input1.js
new
+9
@@ -0,0 +1,9 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Flags: --flag1 --flag2
6
+// Flags: --flag3
7
+
8
+var a = 0;
9
+print(a);
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/input2.js
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Flags: --flag4
6
+
7
+let b = 1;
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/mutations.js
new
+26
@@ -0,0 +1,26 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+
6
+// Print after declaration.
7
+var __v_0 = [1, 2, 3];
8
+
9
+// Don't print after declarations or assigments in loops.
10
+for (let __v_1 = 0; __v_1 < 3; __v_1 += 1) {
11
+
12
+ // Print after multiple declarations.
13
+ let __v_2, __v_3 = 0;
14
+
15
+ // Print after assigning to member.
16
+ __v_0.foo = undefined;
17
+
18
+ // Replace with deep printing.
19
+ print(0);
20
+
21
+ // Print exception.
22
+ try {
23
+ // Print after assignment.
24
+ __v_1 += 1;
25
+ } catch(e) {}
26
+}
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/mutations_expected.js
new
+44
@@ -0,0 +1,44 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: differential_fuzz/mutations.js
6
+var __v_0 = [1, 2, 3];
7
+
8
+/* DifferentialFuzzMutator: Extra variable printing */
9
+__prettyPrintExtra(__v_0);
10
+
11
+for (let __v_1 = 0; __v_1 < 3; __v_1 += 1) {
12
+ let __v_2,
13
+ __v_3 = 0;
14
+
15
+ /* DifferentialFuzzMutator: Extra variable printing */
16
+ __prettyPrintExtra(__v_2);
17
+
18
+ __prettyPrintExtra(__v_3);
19
+
20
+ __v_0.foo = undefined;
21
+
22
+ /* DifferentialFuzzMutator: Extra variable printing */
23
+ __prettyPrintExtra(__v_0);
24
+
25
+ /* DifferentialFuzzMutator: Pretty printing */
26
+ __prettyPrintExtra(0);
27
+
28
+ try {
29
+ __v_1 += 1;
30
+
31
+ /* DifferentialFuzzMutator: Extra variable printing */
32
+ __prettyPrintExtra(__v_1);
33
+ } catch (e) {
34
+ __prettyPrintExtra(e);
35
+ }
36
+}
37
+
38
+/* DifferentialFuzzMutator: Print variables and exceptions from section */
39
+try {
40
+ print("Hash: " + __hash);
41
+ print("Caught: " + __caught);
42
+
43
+ __prettyPrint(__v_0);
44
+} catch (e) {}
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/suppressions.js
new
+15
@@ -0,0 +1,15 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// These statements might come from a CrashTest.
6
+print("v8-foozzie source: some/file/name");
7
+print('v8-foozzie source: some/file/name');
8
+
9
+function foo(__v_0) {
10
+ // This is an unsupported language feature.
11
+ return 1 in foo.arguments;
12
+}
13
+
14
+// This leads to precision differences in optimized code.
15
+print(192 ** -0.5);
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/suppressions_expected.js
new
+21
@@ -0,0 +1,21 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: differential_fuzz/suppressions.js
6
+print(
7
+/* DifferentialFuzzSuppressions: Replaced magic string */
8
+"v***************e: some/file/name");
9
+print(
10
+/* DifferentialFuzzSuppressions: Replaced magic string */
11
+"v***************e: some/file/name");
12
+
13
+function foo(__v_0) {
14
+ return 1 in
15
+ /* DifferentialFuzzSuppressions: Replaced .arguments */
16
+ __v_0;
17
+}
18
+
19
+print(
20
+/* DifferentialFuzzSuppressions: Replaced ** */
21
+192 + -0.5);
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/v8_fuzz_experiments.json
new
+3
@@ -0,0 +1,3 @@
1
+[
2
+ [100, "ignition", "ignition_turbo", "d8"]
3
+]
compiler/forget/packages/js-fuzzer/test_data/differential_fuzz/v8_fuzz_flags.json
new
+3
@@ -0,0 +1,3 @@
1
+[
2
+ [1.0, "--foo1 --foo2"]
3
+]
compiler/forget/packages/js-fuzzer/test_data/fake_db/index.json
new
+1
@@ -0,0 +1 @@
1
+{}
compiler/forget/packages/js-fuzzer/test_data/mjsunit/mjsunit.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+var fakeMjsunit = 'fake';
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load.js
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+var testLoad = 'test_load';
6
+load('test_data/mjsunit/test_load_1.js');
7
+load('test_load_0.js');
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load_0.js
new
+8
@@ -0,0 +1,8 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+load('test_data/mjsunit/test_load_1.js');
6
+load('test_load_2.js');
7
+load('test_load_3.js');
8
+var testLoad0 = 'test_load_0';
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load_1.js
new
+6
@@ -0,0 +1,6 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+load('test_load_2.js');
6
+var testLoad1 = 'test_load_1';
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load_2.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+var testLoad2 = 'test_load_2';
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load_3.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+var testLoad3 = 'test_load_3';
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load_expected.js
new
+21
@@ -0,0 +1,21 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mjsunit/mjsunit.js
6
+var fakeMjsunit = 'fake';
7
+
8
+// Original: mjsunit/test_load_2.js
9
+var testLoad2 = 'test_load_2';
10
+
11
+// Original: mjsunit/test_load_1.js
12
+var testLoad1 = 'test_load_1';
13
+
14
+// Original: mjsunit/test_load_3.js
15
+var testLoad3 = 'test_load_3';
16
+
17
+// Original: mjsunit/test_load_0.js
18
+var testLoad0 = 'test_load_0';
19
+
20
+// Original: mjsunit/test_load.js
21
+var testLoad = 'test_load';
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load_self.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+load("test_load_self.js");
compiler/forget/packages/js-fuzzer/test_data/mjsunit/test_load_self_expected.js
new
+6
@@ -0,0 +1,6 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mjsunit/mjsunit.js
6
+var fakeMjsunit = 'fake';
compiler/forget/packages/js-fuzzer/test_data/mjsunit_softskipped/object-literal.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Fake file
compiler/forget/packages/js-fuzzer/test_data/mjsunit_softskipped/permitted.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Fake file
compiler/forget/packages/js-fuzzer/test_data/mjsunit_softskipped/regress/binaryen-123.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Fake file
compiler/forget/packages/js-fuzzer/test_data/mutate_arrays.js
new
+34
@@ -0,0 +1,34 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+[];
6
+[];
7
+[];
8
+[];
9
+[];
10
+[];
11
+[];
12
+[];
13
+[];
14
+[];
15
+[1, 2, 3];
16
+[1, 2, 3];
17
+[1, 2, 3];
18
+[1, 2, 3];
19
+[1, 2, 3];
20
+[1, 2, 3];
21
+[1, 2, 3];
22
+[1, 2, 3];
23
+[1, 2, 3];
24
+[1, 2, 3];
25
+[1, 2, 3];
26
+[1, 2, 3];
27
+[1, 2, 3];
28
+[1, 2, 3];
29
+[1, 2, 3];
30
+[1, 2, 3];
31
+[1, 2, 3];
32
+[1, 2, 3];
33
+[1, 2, 3];
34
+[1, 2, 3];
compiler/forget/packages/js-fuzzer/test_data/mutate_arrays_expected.js
new
+109
@@ -0,0 +1,109 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mutate_arrays.js
6
+
7
+/* ArrayMutator: Remove elements */
8
+
9
+/* ArrayMutator: Insert a hole */
10
+[];
11
+[];
12
+
13
+/* ArrayMutator: Shuffle array */
14
+[];
15
+
16
+/* ArrayMutator: Insert a random value */
17
+[""];
18
+
19
+/* ArrayMutator: Insert a random value (replaced) */
20
+[""];
21
+
22
+/* ArrayMutator: Insert a hole (replaced) */
23
+[,];
24
+[];
25
+
26
+/* ArrayMutator: Insert a hole (replaced) */
27
+[,];
28
+
29
+/* ArrayMutator: Remove elements */
30
+[];
31
+
32
+/* ArrayMutator: Remove elements */
33
+[];
34
+
35
+/* ArrayMutator: Duplicate an element */
36
+[1, 1, 2, 3];
37
+
38
+/* ArrayMutator: Insert a random value (replaced) */
39
+[1, "", 3];
40
+
41
+/* ArrayMutator: Remove elements */
42
+[];
43
+
44
+/* ArrayMutator: Duplicate an element */
45
+[1, 2, 3, 2];
46
+
47
+/* ArrayMutator: Remove elements */
48
+[3];
49
+
50
+/* ArrayMutator: Duplicate an element (replaced) */
51
+[1, 2, 3];
52
+
53
+/* ArrayMutator: Insert a hole (replaced) */
54
+
55
+/* ArrayMutator: Duplicate an element (replaced) */
56
+[1, 2,,];
57
+
58
+/* ArrayMutator: Remove elements */
59
+[1, 2];
60
+
61
+/* ArrayMutator: Insert a hole (replaced) */
62
+
63
+/* ArrayMutator: Duplicate an element */
64
+[1, 1, 2,,];
65
+
66
+/* ArrayMutator: Shuffle array */
67
+[2, 1, 3];
68
+
69
+/* ArrayMutator: Remove elements */
70
+
71
+/* ArrayMutator: Remove elements */
72
+[3];
73
+
74
+/* ArrayMutator: Duplicate an element (replaced) */
75
+[1, 2, 1];
76
+
77
+/* ArrayMutator: Duplicate an element (replaced) */
78
+
79
+/* ArrayMutator: Duplicate an element (replaced) */
80
+[1, 2, 2];
81
+
82
+/* ArrayMutator: Insert a random value */
83
+[1, 2, 3, ""];
84
+
85
+/* ArrayMutator: Duplicate an element */
86
+[1, 2, 3, 3];
87
+
88
+/* ArrayMutator: Remove elements */
89
+
90
+/* ArrayMutator: Duplicate an element */
91
+[1, 2];
92
+
93
+/* ArrayMutator: Insert a random value (replaced) */
94
+
95
+/* ArrayMutator: Duplicate an element (replaced) */
96
+[1, 2, ""];
97
+
98
+/* ArrayMutator: Insert a random value (replaced) */
99
+
100
+/* ArrayMutator: Insert a random value (replaced) */
101
+["", 2, 3];
102
+
103
+/* ArrayMutator: Duplicate an element */
104
+
105
+/* ArrayMutator: Remove elements */
106
+[1, 1, 3];
107
+
108
+/* ArrayMutator: Remove elements */
109
+[1, 2];
compiler/forget/packages/js-fuzzer/test_data/mutate_expressions.js
new
+8
@@ -0,0 +1,8 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+1;
6
+let foo = undefined;
7
+2;
8
+3;
compiler/forget/packages/js-fuzzer/test_data/mutate_expressions_current_expected.js
new
+15
@@ -0,0 +1,15 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mutate_expressions.js
6
+1;
7
+
8
+/* ExpressionMutator: Cloned sibling */
9
+2;
10
+let foo = undefined;
11
+
12
+/* ExpressionMutator: Cloned sibling */
13
+3;
14
+2;
15
+3;
compiler/forget/packages/js-fuzzer/test_data/mutate_expressions_previous_expected.js
new
+12
@@ -0,0 +1,12 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mutate_expressions.js
6
+1;
7
+let foo = undefined;
8
+2;
9
+3;
10
+
11
+/* ExpressionMutator: Cloned sibling */
12
+2;
compiler/forget/packages/js-fuzzer/test_data/mutate_function_call.js
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+__f_0(1);
6
+a = __f_0(1);
7
+foo(1, __f_0());
compiler/forget/packages/js-fuzzer/test_data/mutate_function_call_baseline_expected.js
new
+16
@@ -0,0 +1,16 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/* FunctionCallMutator: Compiling baseline __f_0 */
6
+%CompileBaseline(__f_0);
7
+
8
+// Original: mutate_function_call.js
9
+__f_0(1);
10
+
11
+a = (
12
+/* FunctionCallMutator: Compiling baseline __f_0 */
13
+%CompileBaseline(__f_0), __f_0(1));
14
+foo(1, (
15
+/* FunctionCallMutator: Compiling baseline __f_0 */
16
+%CompileBaseline(__f_0), __f_0()));
compiler/forget/packages/js-fuzzer/test_data/mutate_function_call_deopt_expected.js
new
+19
@@ -0,0 +1,19 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+var _temp, _temp2;
6
+
7
+// Original: mutate_function_call.js
8
+
9
+/* FunctionCallMutator: Deoptimizing __f_0 */
10
+__f_0(1);
11
+
12
+%DeoptimizeFunction(__f_0);
13
+
14
+a = (
15
+/* FunctionCallMutator: Deoptimizing __f_0 */
16
+_temp = __f_0(1), %DeoptimizeFunction(__f_0), _temp);
17
+foo(1, (
18
+/* FunctionCallMutator: Deoptimizing __f_0 */
19
+_temp2 = __f_0(), %DeoptimizeFunction(__f_0), _temp2));
compiler/forget/packages/js-fuzzer/test_data/mutate_function_call_expected.js
new
+23
@@ -0,0 +1,23 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+%PrepareFunctionForOptimization(__f_0);
6
+
7
+__f_0(1);
8
+
9
+__f_0(1);
10
+
11
+%OptimizeFunctionOnNextCall(__f_0);
12
+
13
+// Original: mutate_function_call.js
14
+
15
+/* FunctionCallMutator: Optimizing __f_0 */
16
+__f_0(1);
17
+
18
+a = (
19
+/* FunctionCallMutator: Optimizing __f_0 */
20
+%PrepareFunctionForOptimization(__f_0), __f_0(1), __f_0(1), %OptimizeFunctionOnNextCall(__f_0), __f_0(1));
21
+foo(1, (
22
+/* FunctionCallMutator: Optimizing __f_0 */
23
+%PrepareFunctionForOptimization(__f_0), __f_0(), __f_0(), %OptimizeFunctionOnNextCall(__f_0), __f_0()));
compiler/forget/packages/js-fuzzer/test_data/mutate_function_call_maglev_expected.js
new
+23
@@ -0,0 +1,23 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+%PrepareFunctionForOptimization(__f_0);
6
+
7
+__f_0(1);
8
+
9
+__f_0(1);
10
+
11
+%OptimizeMaglevOnNextCall(__f_0);
12
+
13
+// Original: mutate_function_call.js
14
+
15
+/* FunctionCallMutator: Optimizing __f_0 */
16
+__f_0(1);
17
+
18
+a = (
19
+/* FunctionCallMutator: Optimizing __f_0 */
20
+%PrepareFunctionForOptimization(__f_0), __f_0(1), __f_0(1), %OptimizeMaglevOnNextCall(__f_0), __f_0(1));
21
+foo(1, (
22
+/* FunctionCallMutator: Optimizing __f_0 */
23
+%PrepareFunctionForOptimization(__f_0), __f_0(), __f_0(), %OptimizeMaglevOnNextCall(__f_0), __f_0()));
compiler/forget/packages/js-fuzzer/test_data/mutate_numbers.js
new
+22
@@ -0,0 +1,22 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+'use strict';
6
+
7
+function foo() {
8
+ let a = 123;
9
+ for (let i = 0; i < 456; i++) {
10
+ a += 1;
11
+ }
12
+
13
+ let b = 0;
14
+ while (b < 10) {
15
+ b += 2;
16
+ }
17
+
18
+ a += 1;
19
+}
20
+
21
+var a = {0: "", 1: "", get 1(){}};
22
+var b = -10;
compiler/forget/packages/js-fuzzer/test_data/mutate_numbers_expected.js
new
+46
@@ -0,0 +1,46 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+'use strict';
6
+
7
+// Original: mutate_numbers.js
8
+function foo() {
9
+ let a =
10
+ /* NumberMutator: Replaced 123 with -5 */
11
+ -5;
12
+
13
+ for (let i = 0; i < 456; i++) {
14
+ a +=
15
+ /* NumberMutator: Replaced 1 with -4 */
16
+ -4;
17
+ }
18
+
19
+ let b =
20
+ /* NumberMutator: Replaced 0 with -3 */
21
+ -3;
22
+
23
+ while (b < 10) {
24
+ b += 2;
25
+ }
26
+
27
+ a +=
28
+ /* NumberMutator: Replaced 1 with -5 */
29
+ -5;
30
+}
31
+
32
+var a = {
33
+ /* NumberMutator: Replaced 0 with 4 */
34
+ 4: "",
35
+
36
+ /* NumberMutator: Replaced 1 with 3 */
37
+ 3: "",
38
+
39
+ get
40
+ /* NumberMutator: Replaced 1 with 5 */
41
+ 5() {}
42
+
43
+};
44
+var b =
45
+/* NumberMutator: Replaced -10 with -4 */
46
+-4;
compiler/forget/packages/js-fuzzer/test_data/mutate_objects.js
new
+41
@@ -0,0 +1,41 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Empty objects are not manipulated.
6
+a = {};
7
+a = {};
8
+a = {};
9
+a = {};
10
+a = {};
11
+a = {};
12
+
13
+// Small objects only get some mutations.
14
+a = {1: 0};
15
+a = {a: 0};
16
+a = {"s": 0};
17
+a = {1: 0};
18
+a = {a: 0};
19
+a = {"s": 0};
20
+
21
+// Larger objects get all mutations.
22
+a = {1: "a", 2: "b", 3: "c"};
23
+a = {1: "a", 2: "b", 3: "c"};
24
+a = {1: "a", 2: "b", 3: "c"};
25
+a = {1: "a", 2: "b", 3: "c"};
26
+a = {1: "a", 2: "b", 3: "c"};
27
+a = {1: "a", 2: "b", 3: "c"};
28
+a = {1: "a", 2: "b", 3: "c"};
29
+a = {1: "a", 2: "b", 3: "c"};
30
+a = {1: "a", 2: "b", 3: "c"};
31
+a = {1: "a", 2: "b", 3: "c"};
32
+
33
+// Getters and setters are ignored.
34
+a = {get bar() { return 0 }, 1: 0, set bar(t) {}};
35
+a = {get bar() { return 0 }, 1: 0, set bar(t) {}};
36
+a = {get bar() { return 0 }, 1: 0, set bar(t) {}};
37
+
38
+// Recursive.
39
+a = {1: {4: "4", 5: "5", 6: "6"}, 2: {3: "3"}};
40
+a = {1: {4: "4", 5: "5", 6: "6"}, 2: {3: "3"}};
41
+a = {1: {4: "4", 5: "5", 6: "6"}, 2: {3: "3"}};
compiler/forget/packages/js-fuzzer/test_data/mutate_objects_expected.js
new
+182
@@ -0,0 +1,182 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mutate_objects.js
6
+a = {};
7
+a = {};
8
+a = {};
9
+a = {};
10
+a = {};
11
+a = {};
12
+a =
13
+/* ObjectMutator: Insert a random value */
14
+{
15
+ 1: ""
16
+};
17
+a = {
18
+ a: 0
19
+};
20
+a =
21
+/* ObjectMutator: Insert a random value */
22
+{
23
+ "s": ""
24
+};
25
+a =
26
+/* ObjectMutator: Stringify a property key */
27
+{
28
+ "1": 0
29
+};
30
+a =
31
+/* ObjectMutator: Remove a property */
32
+{};
33
+a = {
34
+ "s": 0
35
+};
36
+a =
37
+/* ObjectMutator: Swap properties */
38
+{
39
+ 1: "c",
40
+ 2: "b",
41
+ 3: "a"
42
+};
43
+a =
44
+/* ObjectMutator: Remove a property */
45
+{
46
+ 2: "b",
47
+ 3: "c"
48
+};
49
+a =
50
+/* ObjectMutator: Insert a random value */
51
+{
52
+ 1: "a",
53
+ 2: "",
54
+ 3: "c"
55
+};
56
+a =
57
+/* ObjectMutator: Swap properties */
58
+{
59
+ 1: "b",
60
+ 2: "a",
61
+ 3: "c"
62
+};
63
+a =
64
+/* ObjectMutator: Swap properties */
65
+{
66
+ 1: "c",
67
+ 2: "b",
68
+ 3: "a"
69
+};
70
+a =
71
+/* ObjectMutator: Stringify a property key */
72
+{
73
+ "1": "a",
74
+ 2: "b",
75
+ 3: "c"
76
+};
77
+a =
78
+/* ObjectMutator: Remove a property */
79
+{
80
+ 2: "b",
81
+ 3: "c"
82
+};
83
+a =
84
+/* ObjectMutator: Swap properties */
85
+{
86
+ 1: "b",
87
+ 2: "a",
88
+ 3: "c"
89
+};
90
+a =
91
+/* ObjectMutator: Duplicate a property value */
92
+{
93
+ 1: "c",
94
+ 2: "b",
95
+ 3: "c"
96
+};
97
+a =
98
+/* ObjectMutator: Duplicate a property value */
99
+{
100
+ 1: "a",
101
+ 2: "b",
102
+ 3: "b"
103
+};
104
+a = {
105
+ get bar() {
106
+ return 0;
107
+ },
108
+
109
+ 1: 0,
110
+
111
+ set bar(t) {}
112
+
113
+};
114
+a =
115
+/* ObjectMutator: Insert a random value */
116
+{
117
+ get bar() {
118
+ return 0;
119
+ },
120
+
121
+ 1: "",
122
+
123
+ set bar(t) {}
124
+
125
+};
126
+a =
127
+/* ObjectMutator: Remove a property */
128
+{
129
+ get bar() {
130
+ return 0;
131
+ },
132
+
133
+ set bar(t) {}
134
+
135
+};
136
+a =
137
+/* ObjectMutator: Duplicate a property value */
138
+{
139
+ 1:
140
+ /* ObjectMutator: Remove a property */
141
+ {},
142
+ 2:
143
+ /* ObjectMutator: Stringify a property key */
144
+ {
145
+ "3": "3"
146
+ }
147
+};
148
+a =
149
+/* ObjectMutator: Duplicate a property value */
150
+{
151
+ 1:
152
+ /* ObjectMutator: Swap properties */
153
+ {
154
+ 4: "4",
155
+ 5: "6",
156
+ 6: "5"
157
+ },
158
+ 2:
159
+ /* ObjectMutator: Remove a property */
160
+ {
161
+ 5: "5",
162
+ 6: "6"
163
+ }
164
+};
165
+a =
166
+/* ObjectMutator: Duplicate a property value */
167
+{
168
+ 1:
169
+ /* ObjectMutator: Swap properties */
170
+ {
171
+ 4: "6",
172
+ 5: "5",
173
+ 6: "4"
174
+ },
175
+ 2:
176
+ /* ObjectMutator: Stringify a property key */
177
+ {
178
+ 4: "4",
179
+ 5: "5",
180
+ "6": "6"
181
+ }
182
+};
compiler/forget/packages/js-fuzzer/test_data/mutate_var_or_obj.js
new
+10
@@ -0,0 +1,10 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+let __v_0 = {};
6
+Math.pow(1, 2);
7
+Math.pow(1, 2);
8
+Math.pow(1, 2);
9
+Math.pow(1, 2);
10
+Math.pow(1, 2);
compiler/forget/packages/js-fuzzer/test_data/mutate_var_or_obj_expected.js
new
+37
@@ -0,0 +1,37 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mutate_var_or_obj.js
6
+let __v_0 = {};
7
+
8
+/* VariableOrObjectMutator: Random mutation */
9
+delete __getRandomObject(123)[__getRandomProperty(__getRandomObject(123), 123)], __callGC();
10
+__getRandomObject(123)[__getRandomProperty(__getRandomObject(123), 123)], __callGC();
11
+Math.pow(1, 2);
12
+
13
+/* VariableOrObjectMutator: Random mutation */
14
+__getRandomObject(123)[__getRandomProperty(__getRandomObject(123), 123)] = 0, __callGC();
15
+Math.pow(1, 2);
16
+
17
+/* VariableOrObjectMutator: Random mutation */
18
+__v_0 = __getRandomObject(123), __callGC();
19
+Math.pow(1, 2);
20
+
21
+/* VariableOrObjectMutator: Random mutation */
22
+if (__getRandomObject(123) != null && typeof __getRandomObject(123) == "object") Object.defineProperty(__getRandomObject(123), __getRandomProperty(__getRandomObject(123), 123), {
23
+ value: 0
24
+});
25
+Math.pow(1, 2);
26
+
27
+/* VariableOrObjectMutator: Random mutation */
28
+if (__getRandomObject(123) != null && typeof __getRandomObject(123) == "object") Object.defineProperty(__getRandomObject(123), __getRandomProperty(__getRandomObject(123), 123), {
29
+ get: function () {
30
+ delete __getRandomObject(123)[__getRandomProperty(__getRandomObject(123), 123)], __callGC();
31
+ return 0;
32
+ },
33
+ set: function (value) {
34
+ __getRandomObject(123)[__getRandomProperty(__getRandomObject(123), 123)], __callGC();
35
+ }
36
+});
37
+Math.pow(1, 2);
compiler/forget/packages/js-fuzzer/test_data/mutate_variables.js
new
+30
@@ -0,0 +1,30 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+function __f_0(__v_10, __v_11) {
6
+ let __v_4 = 4;
7
+ let __v_5 = 5;
8
+ let __v_6 = 6;
9
+ let __v_7 = 7;
10
+ console.log(__v_4);
11
+ console.log(__v_5);
12
+ console.log(__v_6);
13
+ console.log(__v_7);
14
+ for (let __v_9 = 0; __v_9 < 10; __v_9++) {
15
+ console.log(__v_4);
16
+ }
17
+ let __v_8 = 0;
18
+ while (__v_8 < 10) {
19
+ __v_8++;
20
+ }
21
+}
22
+let __v_0 = 1;
23
+let __v_1 = 2;
24
+let __v_2 = 3;
25
+let __v_3 = 4;
26
+console.log(__v_0);
27
+console.log(__v_1);
28
+console.log(__v_2);
29
+console.log(__v_3);
30
+__f_0();
compiler/forget/packages/js-fuzzer/test_data/mutate_variables_expected.js
new
+54
@@ -0,0 +1,54 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: mutate_variables.js
6
+function __f_0(__v_10, __v_11) {
7
+ let __v_4 = 4;
8
+ let __v_5 = 5;
9
+ let __v_6 = 6;
10
+ let __v_7 = 7;
11
+ console.log(
12
+ /* VariableMutator: Replaced __v_4 with REPLACED */
13
+ REPLACED);
14
+ console.log(
15
+ /* VariableMutator: Replaced __v_5 with REPLACED */
16
+ REPLACED);
17
+ console.log(
18
+ /* VariableMutator: Replaced __v_6 with REPLACED */
19
+ REPLACED);
20
+ console.log(
21
+ /* VariableMutator: Replaced __v_7 with REPLACED */
22
+ REPLACED);
23
+
24
+ for (let __v_9 = 0; __v_9 < 10; __v_9++) {
25
+ console.log(
26
+ /* VariableMutator: Replaced __v_4 with REPLACED */
27
+ REPLACED);
28
+ }
29
+
30
+ let __v_8 = 0;
31
+
32
+ while (__v_8 < 10) {
33
+ __v_8++;
34
+ }
35
+}
36
+
37
+let __v_0 = 1;
38
+let __v_1 = 2;
39
+let __v_2 = 3;
40
+let __v_3 = 4;
41
+console.log(
42
+/* VariableMutator: Replaced __v_0 with REPLACED */
43
+REPLACED);
44
+console.log(
45
+/* VariableMutator: Replaced __v_1 with REPLACED */
46
+REPLACED);
47
+console.log(
48
+/* VariableMutator: Replaced __v_2 with REPLACED */
49
+REPLACED);
50
+console.log(
51
+/* VariableMutator: Replaced __v_3 with REPLACED */
52
+REPLACED);
53
+
54
+__f_0();
compiler/forget/packages/js-fuzzer/test_data/mutation_order/input.js
new
+23
@@ -0,0 +1,23 @@
1
+// Copyright 2022 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+var i = 1;
6
+var j = 'str';
7
+var k = undefined;
8
+var l = {0: 1};
9
+
10
+function foo(a, b) {
11
+ return a + b;
12
+}
13
+
14
+foo(i, 3);
15
+
16
+function bar(a) {
17
+ return foo(a, a);
18
+}
19
+
20
+foo('foo', j);
21
+bar(2, foo(i, j));
22
+foo(i, j);
23
+bar(j, 3);
compiler/forget/packages/js-fuzzer/test_data/mutation_order/output_expected.js
new
+119
@@ -0,0 +1,119 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Script mutator: using shuffled mutators
6
+// Script mutator: extra ArrayMutator
7
+// Script mutator: extra VariableMutator
8
+// Script mutator: extra ExpressionMutator
9
+// Script mutator: extra ArrayMutator
10
+
11
+// Original: mutation_order/input.js
12
+try {
13
+ var __v_0 =
14
+ /* NumberMutator: Replaced 1 with -10 */
15
+ -10;
16
+} catch (e) {}
17
+
18
+try {
19
+ var __v_1 = 'str';
20
+} catch (e) {}
21
+
22
+try {
23
+ var __v_2 = undefined;
24
+} catch (e) {}
25
+
26
+try {
27
+ var __v_3 = {
28
+ /* NumberMutator: Replaced 0 with 8 */
29
+ 8:
30
+ /* NumberMutator: Replaced 1 with 3 */
31
+ 3
32
+ };
33
+} catch (e) {}
34
+
35
+function __f_0(__v_4, __v_5) {
36
+ return __v_4 + __v_5;
37
+}
38
+
39
+try {
40
+ %PrepareFunctionForOptimization(__f_0);
41
+} catch (e) {}
42
+
43
+try {
44
+ __f_0(__v_0,
45
+ /* NumberMutator: Replaced 3 with 5 */
46
+ 5);
47
+} catch (e) {}
48
+
49
+try {
50
+ __f_0(__v_0,
51
+ /* NumberMutator: Replaced 3 with NaN */
52
+ NaN);
53
+} catch (e) {}
54
+
55
+try {
56
+ %OptimizeFunctionOnNextCall(__f_0);
57
+} catch (e) {}
58
+
59
+try {
60
+ /* FunctionCallMutator: Optimizing __f_0 */
61
+ __f_0(__v_0,
62
+ /* NumberMutator: Replaced 3 with 2 */
63
+ 2);
64
+} catch (e) {}
65
+
66
+function __f_1(__v_6) {
67
+ return (
68
+ /* FunctionCallMutator: Replaced __f_0 with __f_0 */
69
+ __f_0(__v_6, __v_6)
70
+ );
71
+}
72
+
73
+try {
74
+ %PrepareFunctionForOptimization(__f_0);
75
+} catch (e) {}
76
+
77
+try {
78
+ __f_0('foo', __v_1);
79
+} catch (e) {}
80
+
81
+try {
82
+ __f_0('foo', __v_1);
83
+} catch (e) {}
84
+
85
+try {
86
+ %OptimizeFunctionOnNextCall(__f_0);
87
+} catch (e) {}
88
+
89
+try {
90
+ /* FunctionCallMutator: Optimizing __f_0 */
91
+ __f_0('foo', __v_1);
92
+} catch (e) {}
93
+
94
+try {
95
+ /* FunctionCallMutator: Compiling baseline __f_1 */
96
+ %CompileBaseline(__f_1);
97
+} catch (e) {}
98
+
99
+try {
100
+ __f_1(
101
+ /* NumberMutator: Replaced 2 with -10 */
102
+ -10, __f_0(__v_0, __v_1));
103
+} catch (e) {}
104
+
105
+try {
106
+ /* FunctionCallMutator: Deoptimizing __f_0 */
107
+ __f_0(__v_0, __v_1);
108
+} catch (e) {}
109
+
110
+try {
111
+ %DeoptimizeFunction(__f_0);
112
+} catch (e) {}
113
+
114
+try {
115
+ /* FunctionCallMutator: Replaced __f_1 with __f_1 */
116
+ __f_1(__v_1,
117
+ /* NumberMutator: Replaced 3 with 16 */
118
+ 16);
119
+} catch (e) {}
compiler/forget/packages/js-fuzzer/test_data/normalize.js
new
+23
@@ -0,0 +1,23 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+'use strict';
6
+
7
+class Class {
8
+ constructor() {
9
+ this.abc = 789;
10
+ this.selfRef = Class;
11
+ }
12
+}
13
+
14
+function foo() {
15
+ let a = 123;
16
+ console.log(a);
17
+}
18
+
19
+foo();
20
+let a = 456;
21
+console.log(a);
22
+let b = new Class();
23
+console.log(b.abc);
compiler/forget/packages/js-fuzzer/test_data/normalize_expected_0.js
new
+28
@@ -0,0 +1,28 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+'use strict';
6
+
7
+// Original: normalize.js
8
+class __c_0 {
9
+ constructor() {
10
+ this.abc = 789;
11
+ this.selfRef = __c_0;
12
+ }
13
+
14
+}
15
+
16
+function __f_0() {
17
+ let __v_2 = 123;
18
+ console.log(__v_2);
19
+}
20
+
21
+__f_0();
22
+
23
+let __v_0 = 456;
24
+console.log(__v_0);
25
+
26
+let __v_1 = new __c_0();
27
+
28
+console.log(__v_1.abc);
compiler/forget/packages/js-fuzzer/test_data/normalize_expected_1.js
new
+28
@@ -0,0 +1,28 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+'use strict';
6
+
7
+// Original: normalize.js
8
+class __c_1 {
9
+ constructor() {
10
+ this.abc = 789;
11
+ this.selfRef = __c_1;
12
+ }
13
+
14
+}
15
+
16
+function __f_1() {
17
+ let __v_5 = 123;
18
+ console.log(__v_5);
19
+}
20
+
21
+__f_1();
22
+
23
+let __v_3 = 456;
24
+console.log(__v_3);
25
+
26
+let __v_4 = new __c_1();
27
+
28
+console.log(__v_4.abc);
compiler/forget/packages/js-fuzzer/test_data/regress/numbers/db/index.json
new
+1
@@ -0,0 +1 @@
1
+{}
compiler/forget/packages/js-fuzzer/test_data/regress/numbers/input_indices.js
new
+11
@@ -0,0 +1,11 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+let a = {
6
+ 0: "",
7
+ 1: "",
8
+ 2: "",
9
+ 3: "",
10
+ 4: "",
11
+};
compiler/forget/packages/js-fuzzer/test_data/regress/numbers/input_negative.js
new
+8
@@ -0,0 +1,8 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+foo(-1);
6
+foo(-1);
7
+foo(-1);
8
+foo(-1);
compiler/forget/packages/js-fuzzer/test_data/regress/spidermonkey/db/index.json
new
+1
@@ -0,0 +1 @@
1
+{}
compiler/forget/packages/js-fuzzer/test_data/regress/spidermonkey/input.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+print("Won't see this.");
compiler/forget/packages/js-fuzzer/test_data/regress/spidermonkey/shell.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+loadRelativeToScript('PatternAsserts.js');
compiler/forget/packages/js-fuzzer/test_data/regress/strict/db/index.json
new
+1
@@ -0,0 +1 @@
1
+{}
compiler/forget/packages/js-fuzzer/test_data/regress/strict/input_delete.js
new
+6
@@ -0,0 +1,6 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+var x;
6
+delete x;
compiler/forget/packages/js-fuzzer/test_data/regress/strict/input_strict.js
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+"use strict";
6
+
7
+print("Hello");
compiler/forget/packages/js-fuzzer/test_data/regress/strict/input_with.js
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+with (Math) {
6
+ print(PI);
7
+}
compiler/forget/packages/js-fuzzer/test_data/simple_test.js
new
+87
@@ -0,0 +1,87 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Test comment.
6
+// Flags: --gc-interval = 1
7
+var abs = Math.abs;
8
+var v1 = 5, v2; var v3;
9
+if (v1) {
10
+ var v4 = 3;
11
+ for (var v5 = 0; v5 < 4; v5++) {
12
+ console.log('Value of v5: ' +
13
+ v5);
14
+ }
15
+}
16
+let v6 = 3;
17
+const v7 = 5 + \u{0076}6;
18
+v1 = {['p' + v6]: ''};
19
+v1 = `test\`
20
+value is ${ v6 + v7 }` + '\0\400\377'
21
+v1 = (v8=2, {v9 = eval('v8')},) => { return v8 + v9 + 4; };
22
+v1 = () => 4 + 5;
23
+v1 = v10 => { return v10 + 4; }
24
+v1 = async v11 => v11 + 4;
25
+v12 = [0, 1, 2,];
26
+v13 = [3, 4, 5];
27
+v14 = [...v12, ...v13];
28
+v15 = ([v16, v17] = [1, 2], {v31: v18} = {v31: v16 + v17}) => v16 + v17 + v18;
29
+v16 = 170%16/16 + 2**32;
30
+v17 = 0o1 + 0O1 + 01 + 0b011 + 0B011;
31
+for (var v18 of [1, 2, 3]) console.log(v18);
32
+function f1(v19,) {}
33
+f1();
34
+%OptimizeFunctionOnNextCall(f1);
35
+function f2() {
36
+ var v20 = 5;
37
+ return v20 + 6;
38
+}
39
+(async function f3() {
40
+ var v21 = await 1;
41
+ console.log(v21);
42
+})();
43
+function* f4(v22=2, ...v23) {
44
+ yield* [1, 2, 3];
45
+}
46
+function* f5() { (yield 3) + (yield); }
47
+{ function f6() { } }
48
+v23 = { v6, [v6]: 3, f7() { }, get f8 () { }, *f9 () { }, async f10 () { } }
49
+var [v24, v25, ...v26] = [10, 20], {v27, v28} = {v27: 10, v28: 20};
50
+class c1 {
51
+ f11(v29) {
52
+ return v29 + 1;
53
+ }
54
+ static* f12() {
55
+ yield 'a' + super.f12();
56
+ }
57
+ constructor(v30) {
58
+ console.log(new.target.name);
59
+ }
60
+ [0]() { }
61
+}
62
+class c2 extends c1 { }
63
+do ; while(0);
64
+v16 **= 4;
65
+for (const v32 = 1; v32 < 1;);
66
+for (let v33 = 1; v33 < 5; v33++);
67
+for (var v34 = 1; v34 < 5; v34++);
68
+for (const {v35 = 0, v36 = 3} = {}; v36 < 1;);
69
+for (let {v37 = 0, v38 = 3} = {}; v38 != 0; v38--);
70
+for (var {v39 = 0, v40 = 3} = {}; v40 != 0; v40--);
71
+for (const v41 of [1, 2, 3]);
72
+for (let v42 of [1, 2, 3]);
73
+for (var v43 of [1, 2, 3]);
74
+for (const v44 in [1, 2, 3]);
75
+for (let v45 in [1, 2, 3]);
76
+for (var v46 in [1, 2, 3]);
77
+label: function f13() { }
78
+
79
+var a = function b() {
80
+ b();
81
+};
82
+
83
+var c = class C {
84
+ constructor() {
85
+ console.log(C.name);
86
+ }
87
+};
compiler/forget/packages/js-fuzzer/test_data/simple_test_expected.js
new
+177
@@ -0,0 +1,177 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: simple_test.js
6
+var __v_0 = Math.abs;
7
+
8
+var __v_1 = 5,
9
+ __v_2;
10
+
11
+var __v_3;
12
+
13
+if (__v_1) {
14
+ var __v_4 = 3;
15
+
16
+ for (var __v_5 = 0; __v_5 < 4; __v_5++) {
17
+ console.log('Value of v5: ' + __v_5);
18
+ }
19
+}
20
+
21
+let __v_6 = 3;
22
+
23
+const __v_7 = 5 + __v_6;
24
+
25
+__v_1 = {
26
+ ['p' + __v_6]: ''
27
+};
28
+__v_1 = `test\`
29
+value is ${__v_6 + __v_7}` + '\0\400\377';
30
+
31
+__v_1 = (__v_21 = 2, {
32
+ v9: __v_22 = eval('v8')
33
+}) => {
34
+ return __v_21 + __v_22 + 4;
35
+};
36
+
37
+__v_1 = () => 4 + 5;
38
+
39
+__v_1 = __v_23 => {
40
+ return __v_23 + 4;
41
+};
42
+
43
+__v_1 = async __v_24 => __v_24 + 4;
44
+
45
+__v_25 = [0, 1, 2];
46
+__v_26 = [3, 4, 5];
47
+__v_27 = [...__v_25, ...__v_26];
48
+
49
+__v_28 = ([__v_29, __v_30] = [1, 2], {
50
+ v31: __v_31
51
+} = {
52
+ v31: __v_29 + __v_30
53
+}) => __v_29 + __v_30 + __v_31;
54
+
55
+__v_42 = 170 % 16 / 16 + 2 ** 32;
56
+__v_33 = 0o1 + 0O1 + 01 + 0b011 + 0B011;
57
+
58
+for (var __v_8 of [1, 2, 3]) console.log(__v_8);
59
+
60
+function __f_0(__v_34) {}
61
+
62
+__f_0();
63
+
64
+%OptimizeFunctionOnNextCall(__f_0);
65
+
66
+function __f_1() {
67
+ var __v_35 = 5;
68
+ return __v_35 + 6;
69
+}
70
+
71
+(async function __f_5() {
72
+ var __v_36 = await 1;
73
+
74
+ console.log(__v_36);
75
+})();
76
+
77
+function* __f_2(__v_37 = 2, ...__v_38) {
78
+ yield* [1, 2, 3];
79
+}
80
+
81
+function* __f_3() {
82
+ (yield 3) + (yield);
83
+}
84
+
85
+{
86
+ function __f_6() {}
87
+}
88
+__v_39 = {
89
+ v6: __v_6,
90
+ [__v_6]: 3,
91
+
92
+ f7() {},
93
+
94
+ get f8() {},
95
+
96
+ *f9() {},
97
+
98
+ async f10() {}
99
+
100
+};
101
+var [__v_9, __v_10, ...__v_11] = [10, 20],
102
+ {
103
+ v27: __v_12,
104
+ v28: __v_13
105
+} = {
106
+ v27: 10,
107
+ v28: 20
108
+};
109
+
110
+class __c_0 {
111
+ f11(__v_40) {
112
+ return __v_40 + 1;
113
+ }
114
+
115
+ static *f12() {
116
+ yield 'a' + super.f12();
117
+ }
118
+
119
+ constructor(__v_41) {
120
+ console.log(new.target.name);
121
+ }
122
+
123
+ [0]() {}
124
+
125
+}
126
+
127
+class __c_1 extends __c_0 {}
128
+
129
+do ; while (0);
130
+
131
+__v_42 **= 4;
132
+
133
+for (const __v_43 = 1; __v_43 < 1;);
134
+
135
+for (let __v_44 = 1; __v_44 < 5; __v_44++);
136
+
137
+for (var __v_14 = 1; __v_14 < 5; __v_14++);
138
+
139
+for (const {
140
+ v35: __v_45 = 0,
141
+ v36: __v_46 = 3
142
+} = {}; __v_46 < 1;);
143
+
144
+for (let {
145
+ v37: __v_47 = 0,
146
+ v38: __v_48 = 3
147
+} = {}; __v_48 != 0; __v_48--);
148
+
149
+for (var {
150
+ v39: __v_15 = 0,
151
+ v40: __v_16 = 3
152
+} = {}; __v_16 != 0; __v_16--);
153
+
154
+for (const __v_49 of [1, 2, 3]);
155
+
156
+for (let __v_50 of [1, 2, 3]);
157
+
158
+for (var __v_17 of [1, 2, 3]);
159
+
160
+for (const __v_51 in [1, 2, 3]);
161
+
162
+for (let __v_52 in [1, 2, 3]);
163
+
164
+for (var __v_18 in [1, 2, 3]);
165
+
166
+label: function __f_4() {}
167
+
168
+var __v_19 = function __f_7() {
169
+ __f_7();
170
+};
171
+
172
+var __v_20 = class __c_2 {
173
+ constructor() {
174
+ console.log(__c_2.name);
175
+ }
176
+
177
+};
compiler/forget/packages/js-fuzzer/test_data/spidermonkey/load1.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+console.log('load1.js');
compiler/forget/packages/js-fuzzer/test_data/spidermonkey/shell.js
new
+7
@@ -0,0 +1,7 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+console.log('/shell.js');
6
+if (!ok)
7
+ throw new Error(`assertion failed: ${f} did not throw as expected`);
compiler/forget/packages/js-fuzzer/test_data/spidermonkey/test/load.js
new
+15
@@ -0,0 +1,15 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+load('load1.js');
6
+loadRelativeToScript('load2.js');
7
+console.log('load.js');
8
+
9
+if (!ok)
10
+ throw new Error(`Assertion failed: Some text`);
11
+
12
+print("Assertion failed: Some text");
13
+
14
+// Check that we can load template literals with null cooked value.
15
+check()`\01`;
compiler/forget/packages/js-fuzzer/test_data/spidermonkey/test/load2.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+console.log('load2.js');
compiler/forget/packages/js-fuzzer/test_data/spidermonkey/test/load_expected.js
new
+22
@@ -0,0 +1,22 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: spidermonkey/shell.js
6
+console.log('/shell.js');
7
+if (!ok) throw new Error(`*****tion failed: ${f} did not throw as expected`);
8
+
9
+// Original: spidermonkey/test/shell.js
10
+console.log('/test/shell.js');
11
+
12
+// Original: spidermonkey/load1.js
13
+console.log('load1.js');
14
+
15
+// Original: spidermonkey/test/load2.js
16
+console.log('load2.js');
17
+
18
+// Original: spidermonkey/test/load.js
19
+console.log('load.js');
20
+if (!ok) throw new Error(`*****tion failed: Some text`);
21
+print("*****tion failed: Some text");
22
+check()`\01`;
compiler/forget/packages/js-fuzzer/test_data/spidermonkey/test/shell.js
new
+5
@@ -0,0 +1,5 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+console.log('/test/shell.js');
compiler/forget/packages/js-fuzzer/test_data/try_catch.js
new
+41
@@ -0,0 +1,41 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+function blah() {
6
+ try {
7
+ var a = 10;
8
+ console.log(a);
9
+ } catch (e) {}
10
+
11
+ label: for (var i = 0; i < 100; i++) {
12
+ var b = 0;
13
+ while (b < 10) {
14
+ console.log(b);
15
+ b += 2;
16
+ continue label;
17
+ }
18
+ }
19
+}
20
+
21
+blah();
22
+blah();
23
+
24
+(function () {1;1;})();
25
+
26
+if (true) {
27
+ 2;2;
28
+} else {
29
+ 3;3;
30
+}
31
+
32
+let a = 0;
33
+switch (a) {
34
+ case 1: 1;
35
+}
36
+
37
+with (Math) {
38
+ cos(PI);
39
+}
40
+
41
+let module = new WebAssembly.Module(builder.toBuffer());
compiler/forget/packages/js-fuzzer/test_data/try_catch_alternate_expected.js
new
+51
@@ -0,0 +1,51 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/* AddTryCatchMutator: Target skip probability 0.9 and toplevel probability 0.9 */
6
+
7
+// Original: try_catch.js
8
+function blah() {
9
+ try {
10
+ var a = 10;
11
+ console.log(a);
12
+ } catch (e) {}
13
+
14
+ label: for (var i = 0; i < 100; i++) {
15
+ var b = 0;
16
+
17
+ while (b < 10) {
18
+ console.log(b);
19
+ b += 2;
20
+ continue label;
21
+ }
22
+ }
23
+}
24
+
25
+blah();
26
+blah();
27
+
28
+(function () {
29
+ 1;
30
+ 1;
31
+})();
32
+
33
+if (true) {
34
+ 2;
35
+ 2;
36
+} else {
37
+ 3;
38
+ 3;
39
+}
40
+
41
+let a = 0;
42
+
43
+switch (a) {
44
+ case 1:
45
+ 1;
46
+}
47
+
48
+with (Math) {
49
+ cos(PI);
50
+}
51
+let module = new WebAssembly.Module(builder.toBuffer());
compiler/forget/packages/js-fuzzer/test_data/try_catch_expected.js
new
+104
@@ -0,0 +1,104 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: try_catch.js
6
+function blah() {
7
+ try {
8
+ try {
9
+ var a = 10;
10
+ } catch (e) {}
11
+
12
+ try {
13
+ console.log(a);
14
+ } catch (e) {}
15
+ } catch (e) {}
16
+
17
+ try {
18
+ label: for (var i = 0; i < 100; i++) {
19
+ try {
20
+ var b = 0;
21
+ } catch (e) {}
22
+
23
+ try {
24
+ while (b < 10) {
25
+ try {
26
+ console.log(b);
27
+ } catch (e) {}
28
+
29
+ try {
30
+ b += 2;
31
+ } catch (e) {}
32
+
33
+ continue label;
34
+ }
35
+ } catch (e) {}
36
+ }
37
+ } catch (e) {}
38
+}
39
+
40
+try {
41
+ blah();
42
+} catch (e) {}
43
+
44
+try {
45
+ blah();
46
+} catch (e) {}
47
+
48
+try {
49
+ (function () {
50
+ try {
51
+ 1;
52
+ } catch (e) {}
53
+
54
+ try {
55
+ 1;
56
+ } catch (e) {}
57
+ })();
58
+} catch (e) {}
59
+
60
+try {
61
+ if (true) {
62
+ try {
63
+ 2;
64
+ } catch (e) {}
65
+
66
+ try {
67
+ 2;
68
+ } catch (e) {}
69
+ } else {
70
+ try {
71
+ 3;
72
+ } catch (e) {}
73
+
74
+ try {
75
+ 3;
76
+ } catch (e) {}
77
+ }
78
+} catch (e) {}
79
+
80
+let a = 0;
81
+
82
+try {
83
+ switch (a) {
84
+ case 1:
85
+ try {
86
+ 1;
87
+ } catch (e) {}
88
+
89
+ }
90
+} catch (e) {}
91
+
92
+try {
93
+ with (Math) {
94
+ try {
95
+ cos(PI);
96
+ } catch (e) {}
97
+ }
98
+} catch (e) {}
99
+
100
+let module = function () {
101
+ try {
102
+ return new WebAssembly.Module(builder.toBuffer());
103
+ } catch (e) {}
104
+}();
compiler/forget/packages/js-fuzzer/test_data/try_catch_nothing_expected.js
new
+49
@@ -0,0 +1,49 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: try_catch.js
6
+function blah() {
7
+ try {
8
+ var a = 10;
9
+ console.log(a);
10
+ } catch (e) {}
11
+
12
+ label: for (var i = 0; i < 100; i++) {
13
+ var b = 0;
14
+
15
+ while (b < 10) {
16
+ console.log(b);
17
+ b += 2;
18
+ continue label;
19
+ }
20
+ }
21
+}
22
+
23
+blah();
24
+blah();
25
+
26
+(function () {
27
+ 1;
28
+ 1;
29
+})();
30
+
31
+if (true) {
32
+ 2;
33
+ 2;
34
+} else {
35
+ 3;
36
+ 3;
37
+}
38
+
39
+let a = 0;
40
+
41
+switch (a) {
42
+ case 1:
43
+ 1;
44
+}
45
+
46
+with (Math) {
47
+ cos(PI);
48
+}
49
+let module = new WebAssembly.Module(builder.toBuffer());
compiler/forget/packages/js-fuzzer/test_data/try_catch_toplevel_expected.js
new
+74
@@ -0,0 +1,74 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+// Original: try_catch.js
6
+function blah() {
7
+ try {
8
+ try {
9
+ var a = 10;
10
+ } catch (e) {}
11
+
12
+ try {
13
+ console.log(a);
14
+ } catch (e) {}
15
+ } catch (e) {}
16
+
17
+ try {
18
+ label: for (var i = 0; i < 100; i++) {
19
+ var b = 0;
20
+
21
+ while (b < 10) {
22
+ console.log(b);
23
+ b += 2;
24
+ continue label;
25
+ }
26
+ }
27
+ } catch (e) {}
28
+}
29
+
30
+try {
31
+ blah();
32
+} catch (e) {}
33
+
34
+try {
35
+ blah();
36
+} catch (e) {}
37
+
38
+try {
39
+ (function () {
40
+ 1;
41
+ 1;
42
+ })();
43
+} catch (e) {}
44
+
45
+try {
46
+ if (true) {
47
+ 2;
48
+ 2;
49
+ } else {
50
+ 3;
51
+ 3;
52
+ }
53
+} catch (e) {}
54
+
55
+let a = 0;
56
+
57
+try {
58
+ switch (a) {
59
+ case 1:
60
+ 1;
61
+ }
62
+} catch (e) {}
63
+
64
+try {
65
+ with (Math) {
66
+ cos(PI);
67
+ }
68
+} catch (e) {}
69
+
70
+let module = function () {
71
+ try {
72
+ return new WebAssembly.Module(builder.toBuffer());
73
+ } catch (e) {}
74
+}();
compiler/forget/packages/js-fuzzer/test_db.js
new
+66
@@ -0,0 +1,66 @@
1
+// Copyright 2020 the V8 project authors. All rights reserved.
2
+// Use of this source code is governed by a BSD-style license that can be
3
+// found in the LICENSE file.
4
+
5
+/**
6
+ * @fileoverview Test all expressions in DB.
7
+ */
8
+
9
+const fs = require('fs');
10
+const fsPath = require('path');
11
+const program = require('commander');
12
+const sinon = require('sinon');
13
+
14
+const crossOverMutator = require('./mutators/crossover_mutator.js');
15
+const db = require('./db.js');
16
+const random = require('./random.js');
17
+const sourceHelpers = require('./source_helpers.js');
18
+
19
+const sandbox = sinon.createSandbox();
20
+
21
+function main() {
22
+ program
23
+ .version('0.0.1')
24
+ .option('-i, --input_dir <path>', 'DB directory.')
25
+ .parse(process.argv);
26
+
27
+ if (!program.input_dir) {
28
+ console.log('Need to specify DB dir.');
29
+ return;
30
+ }
31
+
32
+ const mutateDb = new db.MutateDb(program.input_dir);
33
+ const mutator = new crossOverMutator.CrossOverMutator(
34
+ { MUTATE_CROSSOVER_INSERT: 1.0, testing: true }, mutateDb);
35
+
36
+ let nPass = 0;
37
+ let nFail = 0;
38
+ // Iterate over all statements saved in the DB.
39
+ for (const statementPath of mutateDb.index.all) {
40
+ const expression = JSON.parse(fs.readFileSync(
41
+ fsPath.join(program.input_dir, statementPath)), 'utf-8');
42
+ // Stub out choosing random variables in cross-over mutator.
43
+ sandbox.stub(random, 'single').callsFake((a) => { return a[0]; });
44
+ // Ensure we are selecting the statement of the current iteration.
45
+ sandbox.stub(mutateDb, 'getRandomStatement').callsFake(
46
+ () => { return expression; });
47
+ // Use a source that will try to insert one statement, allowing
48
+ // super.
49
+ const source = sourceHelpers.loadSource(
50
+ __dirname,
51
+ 'test_data/cross_over_mutator_class_input.js');
52
+ try {
53
+ mutator.mutate(source);
54
+ nPass++;
55
+ } catch (e) {
56
+ console.log('******************************************************')
57
+ console.log(expression);
58
+ console.log(e.message);
59
+ nFail++;
60
+ }
61
+ sandbox.restore();
62
+ }
63
+ console.log(`Result: ${nPass} passed, ${nFail} failed.`)
64
+}
65
+
66
+main();
compiler/forget/packages/js-fuzzer/tools/fuzz_one.py
new
+43
@@ -0,0 +1,43 @@
1
+#!/usr/bin/env python3
2
+# Copyright 2020 the V8 project authors. All rights reserved.
3
+# Use of this source code is governed by a BSD-style license that can be
4
+# found in the LICENSE file.
5
+
6
+
7
+"""
8
+Helper script to execute a single-processed fuzzing session.
9
+
10
+Creates fuzz tests in workdir/output/dir-<dir number>/fuzz-XXX.js.
11
+Expects the <dir number> as single parameter.
12
+"""
13
+
14
+import os
15
+import subprocess
16
+import sys
17
+import time
18
+
19
+BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
20
+APP_DIR = os.path.join(BASE_PATH, 'workdir', 'app_dir')
21
+FUZZ_EXE = os.path.join(BASE_PATH, 'workdir', 'fuzzer', 'ochang_js_fuzzer')
22
+INPUT_DIR = os.path.join(BASE_PATH, 'workdir', 'input')
23
+TEST_CASES = os.path.join(BASE_PATH, 'workdir', 'output')
24
+
25
+COUNT = 64
26
+FUZZ = ('FUZZ_MODE=foozzie APP_NAME=d8 APP_DIR=%s %s -o %%s -n %s -i %s > %%s'
27
+ % (APP_DIR, FUZZ_EXE, COUNT, INPUT_DIR))
28
+
29
+assert(len(sys.argv) > 1)
30
+dir_number = int(sys.argv[1])
31
+assert(dir_number >= 0)
32
+
33
+path = os.path.join(TEST_CASES, 'dir-%d' % dir_number)
34
+assert not os.path.exists(path), 'Need fresh workdir for fuzzing'
35
+os.makedirs(path)
36
+
37
+start = time.time()
38
+subprocess.check_call(
39
+ FUZZ % (path, os.path.join(path, 'out.log')), shell=True)
40
+duration = int(time.time() - start)
41
+
42
+with open(os.path.join(path, 'duration.log'), 'w') as f:
43
+ f.write(str(duration))
compiler/forget/packages/js-fuzzer/tools/minimize.py
new
+44
@@ -0,0 +1,44 @@
1
+#!/usr/bin/env python3
2
+# Copyright 2020 the V8 project authors. All rights reserved.
3
+# Use of this source code is governed by a BSD-style license that can be
4
+# found in the LICENSE file.
5
+
6
+
7
+"""
8
+Helper script to forge a command line for clusterfuzz' minimizer for
9
+each failure found during a fuzzing session with workbench.py.
10
+
11
+Expects the path to the minimizer tools, e.g. something like:
12
+path/to/src/python/bot/minimizer
13
+"""
14
+
15
+import json
16
+from multiprocessing import cpu_count
17
+import os
18
+import sys
19
+
20
+PROCESSES = cpu_count()
21
+BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
22
+OUT_PATH = os.path.join(BASE_PATH, 'out.js')
23
+FAILURES_JSON_PATH = os.path.join(
24
+ BASE_PATH, 'workdir', 'output', 'failures.json')
25
+
26
+assert len(sys.argv) > 1, 'Need to specify minimizer path.'
27
+minimizer_path = sys.argv[1]
28
+
29
+def getcmd(command):
30
+ parts = command.split(' ')
31
+ prefix = command[:-(len(parts[-1]) + 1)]
32
+ return ('python %s/run.py -t%d -mjs -o %s "%s" %s' %
33
+ (minimizer_path, PROCESSES, OUT_PATH, prefix, parts[-1]))
34
+
35
+with open(FAILURES_JSON_PATH) as f:
36
+ failures = json.load(f)
37
+
38
+for failure in failures:
39
+ print('*********************************************************')
40
+ print('Source: ' + failure['source'])
41
+ print('Command:')
42
+ print(failure['command'])
43
+ print('Minimize:')
44
+ print(getcmd(failure['command']))
compiler/forget/packages/js-fuzzer/tools/run_one.py
new
+106
@@ -0,0 +1,106 @@
1
+#!/usr/bin/env python3
2
+# Copyright 2020 the V8 project authors. All rights reserved.
3
+# Use of this source code is governed by a BSD-style license that can be
4
+# found in the LICENSE file.
5
+
6
+
7
+"""
8
+Helper script to execute fuzz tests in a single process.
9
+
10
+Expects fuzz tests in workdir/output/dir-<dir number>/fuzz-XXX.js.
11
+Expects the <dir number> as single parameter.
12
+"""
13
+
14
+import json
15
+import os
16
+import random
17
+import re
18
+import subprocess
19
+import sys
20
+
21
+BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
22
+FOOZZIE = os.path.join(BASE_PATH, 'workdir', 'app_dir', 'v8_foozzie.py')
23
+TEST_CASES = os.path.join(BASE_PATH, 'workdir', 'output')
24
+
25
+assert os.path.exists(FOOZZIE)
26
+
27
+# Output pattern from foozzie.py when it finds a failure.
28
+FAILURE_RE = re.compile(
29
+ r'# V8 correctness failure.'
30
+ r'# V8 correctness configs: (?P<configs>.*).'
31
+ r'# V8 correctness sources: (?P<source>.*).'
32
+ r'# V8 correctness suppression:.*', re.S)
33
+
34
+assert(len(sys.argv) > 1)
35
+dir_number = int(sys.argv[1])
36
+assert(dir_number >= 0)
37
+
38
+test_dir = os.path.join(TEST_CASES, 'dir-%d' % dir_number)
39
+assert os.path.exists(test_dir)
40
+
41
+def failure_state(command, stdout):
42
+ return dict(FAILURE_RE.search(stdout).groupdict(), command=command)
43
+
44
+def random_seed():
45
+ """Returns random, non-zero seed."""
46
+ seed = 0
47
+ while not seed:
48
+ seed = random.SystemRandom().randint(-2147483648, 2147483647)
49
+ return seed
50
+
51
+def run(fuzz_file, flag_file):
52
+ """Executes the differential-fuzzing harness foozzie with one fuzz test."""
53
+ with open(flag_file) as f:
54
+ flags = f.read().split(' ')
55
+ args = [FOOZZIE, '--random-seed=%d' % random_seed()] + flags + [fuzz_file]
56
+ cmd = ' '.join(args)
57
+ try:
58
+ output = subprocess.check_output(cmd, stderr=subprocess.PIPE, shell=True)
59
+ return (cmd, output.decode('utf-8'))
60
+ except Exception as e:
61
+ return (cmd, e.output.decode('utf-8'))
62
+
63
+
64
+def list_tests():
65
+ """Iterates all fuzz tests and corresponding flags in the given base dir."""
66
+ for f in os.listdir(test_dir):
67
+ if f.startswith('fuzz'):
68
+ n = int(re.match(r'fuzz-(\d+)\.js', f).group(1))
69
+ ff = 'flags-%d.js' % n
70
+ yield (os.path.join(test_dir, f), os.path.join(test_dir, ff))
71
+
72
+# Some counters for the statistics.
73
+count = 0
74
+count_timeout = 0
75
+count_crash = 0
76
+count_failure = 0
77
+failures = []
78
+
79
+# Execute all tests in the given directory. Interpret foozzie's output and add
80
+# it to the statistics.
81
+for fuzz_file, flag_file in list_tests():
82
+ cmd, output = run(fuzz_file, flag_file)
83
+ count += 1
84
+ if '# V8 correctness - pass' in output:
85
+ continue
86
+ if '# V8 correctness - T-I-M-E-O-U-T' in output:
87
+ count_timeout += 1
88
+ continue
89
+ if '# V8 correctness - C-R-A-S-H' in output:
90
+ count_crash += 1
91
+ continue
92
+ count_failure += 1
93
+ failures.append(failure_state(cmd, output))
94
+
95
+with open(os.path.join(test_dir, 'failures.json'), 'w') as f:
96
+ json.dump(failures, f)
97
+
98
+stats = {
99
+ 'total': count,
100
+ 'timeout': count_timeout,
101
+ 'crash': count_crash,
102
+ 'failure': count_failure,
103
+}
104
+
105
+with open(os.path.join(test_dir, 'stats.json'), 'w') as f:
106
+ json.dump(stats, f)
compiler/forget/packages/js-fuzzer/tools/workbench.py
new
+127
@@ -0,0 +1,127 @@
1
+#!/usr/bin/env python3
2
+# Copyright 2020 the V8 project authors. All rights reserved.
3
+# Use of this source code is governed by a BSD-style license that can be
4
+# found in the LICENSE file.
5
+
6
+
7
+"""
8
+Tool to execute multiprocessed fuzzing and testing sessions.
9
+
10
+Expects a single parameter with the number of sessions.
11
+
12
+Regularly updates a stats.json and failures.json during executions. E.g.
13
+stay up-to-date with:
14
+cat workdir/output/stats.json | python -m json.tool
15
+"""
16
+
17
+# TODO(machenbach): This is currently tailored for differential fuzzing
18
+# with foozzie. It could be generalized, but that'd require duplicating
19
+# clusterfuzz' stack analysis to some degree. E.g. understanding asan
20
+# or DCHECK failures.
21
+
22
+from __future__ import print_function
23
+
24
+import json
25
+import math
26
+from multiprocessing import Pool, cpu_count
27
+import os
28
+import random
29
+import subprocess
30
+import sys
31
+
32
+PROCESSES = cpu_count()
33
+BASE_PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
34
+TEST_CASES = os.path.join(BASE_PATH, 'workdir', 'output')
35
+FUZZ_ONE = os.path.join(BASE_PATH, 'tools', 'fuzz_one.py')
36
+RUN_ONE = os.path.join(BASE_PATH, 'tools', 'run_one.py')
37
+
38
+os.chdir(BASE_PATH)
39
+
40
+if os.path.exists(TEST_CASES):
41
+ if not os.path.isdir(TEST_CASES) or os.listdir(TEST_CASES):
42
+ sys.exit("'output' must be an empty directory")
43
+else:
44
+ os.mkdir(TEST_CASES)
45
+
46
+# Use ~40000 for 24 hours of fuzzing on a modern work station.
47
+RUNS = 8
48
+if len(sys.argv) > 1:
49
+ RUNS = int(sys.argv[1])
50
+
51
+def run(n):
52
+ """Multiprocessed function that executes a single fuzz session and
53
+ afterwards executes all fuzz tests and collects the statistics.
54
+
55
+ Args:
56
+ n: Subdirectory index of this run.
57
+ """
58
+ subprocess.check_call([sys.executable, FUZZ_ONE, str(n)])
59
+ subprocess.check_call([sys.executable, RUN_ONE, str(n)])
60
+ test_dir = os.path.join(TEST_CASES, 'dir-%d' % n)
61
+ with open(os.path.join(test_dir, 'stats.json')) as f:
62
+ stats = json.load(f)
63
+ with open(os.path.join(test_dir, 'failures.json')) as f:
64
+ failures = json.load(f)
65
+ return (stats, failures)
66
+
67
+
68
+class Stats(object):
69
+ def __init__(self):
70
+ self.total = 0
71
+ self.crash = 0
72
+ self.timeout = 0
73
+ self.failure = 0
74
+ self.dupe = 0
75
+ self.failures = []
76
+ self.known_states = set()
77
+
78
+ def add(self, stats, failures):
79
+ # Aggregate common stats.
80
+ self.total += stats['total']
81
+ self.crash += stats['crash']
82
+ self.timeout += stats['timeout']
83
+
84
+ # Dedupe failures.
85
+ for failure in failures:
86
+ if failure['source'] in self.known_states:
87
+ self.dupe += 1
88
+ continue
89
+
90
+ self.known_states.add(failure['source'])
91
+ self.failure += 1
92
+ self.failures.append(failure)
93
+
94
+ @property
95
+ def stats(self):
96
+ return {
97
+ 'total': self.total,
98
+ 'crash': self.crash,
99
+ 'failure': self.failure,
100
+ 'dupe': self.dupe,
101
+ 'timeout': self.timeout,
102
+ }
103
+
104
+all_stats = Stats()
105
+count = 0
106
+pool = Pool(processes=PROCESSES)
107
+
108
+# Iterate over all runs multiprocessed and merge the statistics and
109
+# failure data of the single runs.
110
+for stats, failures in pool.imap_unordered(run, range(RUNS)):
111
+ all_stats.add(stats, failures)
112
+ count += 1
113
+ if count % max(1, int(RUNS / 20)) == 0:
114
+ print('Progress: %d runs (%d%%)' % (count, count * 100 / RUNS))
115
+
116
+ # Update overall stats.
117
+ with open(os.path.join(TEST_CASES, 'stats.json'), 'w') as f:
118
+ json.dump(all_stats.stats, f)
119
+ with open(os.path.join(TEST_CASES, 'failures.json'), 'w') as f:
120
+ json.dump(all_stats.failures, f)
121
+
122
+print('Ran %(total)d test cases (%(timeout)d timeouts, '
123
+ '%(crash)d crashes, %(failure)d failures, %(dupe)d dupes)'
124
+ % all_stats.stats)
125
+
126
+for failure in all_stats.failures:
127
+ print(failure)