320d98709f
This is a JavaScript fuzzer originally authored by Oliver Chang. It is a mutation based fuzzer using Babel code transformations. For more information see the included README.md. The original code was altered: - Add new V8 copyright headers. - Make the test expectation generator aware of the headers. - Fix file endings for presubmit checks. - Fix `npm test` on fresh checkout with a new fake DB. - Make test skipping work with new v8/tools location. - OWNERS file. - New title section in README.md. No-Try: true Bug: chromium:1109770 Change-Id: Ie71752c0a37491a50500c49060a3c526716ef933 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2320330 Commit-Queue: Michael Achenbach <machenbach@chromium.org> Reviewed-by: Maya Lekova <mslekova@chromium.org> Cr-Commit-Position: refs/heads/master@{#69164}
142 lines
3.9 KiB
JavaScript
142 lines
3.9 KiB
JavaScript
// Copyright 2020 the V8 project authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
/**
|
|
* @fileoverview Corpus
|
|
*/
|
|
|
|
const program = require('commander');
|
|
const fs = require('fs');
|
|
const path = require('path');
|
|
|
|
const exceptions = require('./exceptions.js');
|
|
const random = require('./random.js');
|
|
const sourceHelpers = require('./source_helpers.js');
|
|
|
|
function* walkDirectory(directory, filter) {
|
|
// Generator for recursively walk a directory.
|
|
for (const filePath of fs.readdirSync(directory)) {
|
|
const currentPath = path.join(directory, filePath);
|
|
const stat = fs.lstatSync(currentPath);
|
|
if (stat.isFile()) {
|
|
if (!filter || filter(currentPath)) {
|
|
yield currentPath;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
if (stat.isDirectory()) {
|
|
for (let childFilePath of walkDirectory(currentPath, filter)) {
|
|
yield childFilePath;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
class Corpus {
|
|
// Input corpus.
|
|
constructor(inputDir, corpusName, extraStrict=false) {
|
|
this.inputDir = inputDir;
|
|
this.extraStrict = extraStrict;
|
|
|
|
// Filter for permitted JS files.
|
|
function isPermittedJSFile(absPath) {
|
|
return (absPath.endsWith('.js') &&
|
|
!exceptions.isTestSkippedAbs(absPath));
|
|
}
|
|
|
|
// Cache relative paths of all files in corpus.
|
|
this.skippedFiles = [];
|
|
this.softSkippedFiles = [];
|
|
this.permittedFiles = [];
|
|
const directory = path.join(inputDir, corpusName);
|
|
for (const absPath of walkDirectory(directory, isPermittedJSFile)) {
|
|
const relPath = path.relative(this.inputDir, absPath);
|
|
if (exceptions.isTestSkippedRel(relPath)) {
|
|
this.skippedFiles.push(relPath);
|
|
} else if (exceptions.isTestSoftSkippedAbs(absPath) ||
|
|
exceptions.isTestSoftSkippedRel(relPath)) {
|
|
this.softSkippedFiles.push(relPath);
|
|
} else {
|
|
this.permittedFiles.push(relPath);
|
|
}
|
|
}
|
|
random.shuffle(this.softSkippedFiles);
|
|
random.shuffle(this.permittedFiles);
|
|
}
|
|
|
|
// Relative paths of all files in corpus.
|
|
*relFiles() {
|
|
for (const relPath of this.permittedFiles) {
|
|
yield relPath;
|
|
}
|
|
for (const relPath of this.softSkippedFiles) {
|
|
yield relPath;
|
|
}
|
|
}
|
|
|
|
// Relative paths of all files in corpus including generated skipped.
|
|
*relFilesForGenSkipped() {
|
|
for (const relPath of this.relFiles()) {
|
|
yield relPath;
|
|
}
|
|
for (const relPath of this.skippedFiles) {
|
|
yield relPath;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns "count" relative test paths, randomly selected from soft-skipped
|
|
* and permitted files. Permitted files have a 4 times higher chance to
|
|
* be chosen.
|
|
*/
|
|
getRandomTestcasePaths(count) {
|
|
return random.twoBucketSample(
|
|
this.softSkippedFiles, this.permittedFiles, 4, count);
|
|
}
|
|
|
|
loadTestcase(relPath, strict, label) {
|
|
const start = Date.now();
|
|
try {
|
|
const source = sourceHelpers.loadSource(this.inputDir, relPath, strict);
|
|
if (program.verbose) {
|
|
const duration = Date.now() - start;
|
|
console.log(`Parsing ${relPath} ${label} took ${duration} ms.`);
|
|
}
|
|
return source;
|
|
} catch (e) {
|
|
console.log(`WARNING: failed to ${label} parse ${relPath}`);
|
|
console.log(e);
|
|
}
|
|
return undefined;
|
|
}
|
|
|
|
*loadTestcases(relPaths) {
|
|
for (const relPath of relPaths) {
|
|
if (this.extraStrict) {
|
|
// When re-generating the files marked sloppy, we additionally test if
|
|
// the file parses in strict mode.
|
|
this.loadTestcase(relPath, true, 'strict');
|
|
}
|
|
const source = this.loadTestcase(relPath, false, 'sloppy');
|
|
if (source) {
|
|
yield source;
|
|
}
|
|
}
|
|
}
|
|
|
|
getRandomTestcases(count) {
|
|
return Array.from(this.loadTestcases(this.getRandomTestcasePaths(count)));
|
|
}
|
|
|
|
getAllTestcases() {
|
|
return this.loadTestcases(this.relFilesForGenSkipped());
|
|
}
|
|
}
|
|
|
|
module.exports = {
|
|
Corpus: Corpus,
|
|
walkDirectory: walkDirectory,
|
|
}
|