@samitouri / QOS-React-2 / commits / 31ec959e9b

[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)