Commit 0ffc1ba5 authored by Michael Achenbach's avatar Michael Achenbach Committed by V8 LUCI CQ

[js-fuzzer] Make db more robust to failing code fragments.

Until now, the cross-over mutator would choke on several expressions
from DB and bail out (just wastes some time). We also have a script,
test_db.js to test on how many expressions it is going to bail out.

With this change, we already omit adding such expressions to the
DB in the first place. As a result, the test_db script now returns
zero failing expressions (while all other expressions remain).

Regression tests that now no longer apply are removed, instead a
test is added that ensures that a failing expression isn't added
to the DB.

No-Try: true
Bug: chromium:1044942
Change-Id: I14a4fe802c99114cf3a8f71188273475a7cb9c13
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3129340Reviewed-by: 's avatarLiviu Rau <liviurau@chromium.org>
Commit-Queue: Michael Achenbach <machenbach@chromium.org>
Cr-Commit-Position: refs/heads/main@{#76598}
parent 797e4afe
...@@ -34,7 +34,6 @@ function main() { ...@@ -34,7 +34,6 @@ function main() {
} }
const mutateDb = new db.MutateDbWriter(program.output_dir); const mutateDb = new db.MutateDbWriter(program.output_dir);
const expressions = new Set();
const inputDir = path.resolve(program.input_dir); const inputDir = path.resolve(program.input_dir);
for (const corpusName of program.args) { for (const corpusName of program.args) {
...@@ -53,7 +52,7 @@ function main() { ...@@ -53,7 +52,7 @@ function main() {
} }
try{ try{
mutateDb.process(source, expressions); mutateDb.process(source);
} catch (e) { } catch (e) {
console.log(e); console.log(e);
} }
......
...@@ -11,11 +11,13 @@ const fs = require('fs'); ...@@ -11,11 +11,13 @@ const fs = require('fs');
const fsPath = require('path'); const fsPath = require('path');
const babelGenerator = require('@babel/generator').default; const babelGenerator = require('@babel/generator').default;
const babelTemplate = require('@babel/template').default;
const babelTraverse = require('@babel/traverse').default; const babelTraverse = require('@babel/traverse').default;
const babelTypes = require('@babel/types'); const babelTypes = require('@babel/types');
const globals = require('globals'); const globals = require('globals');
const random = require('./random.js'); const random = require('./random.js');
const sourceHelpers = require('./source_helpers.js');
const globalIdentifiers = new Set(Object.keys(globals.builtin)); const globalIdentifiers = new Set(Object.keys(globals.builtin));
const propertyNames = new Set([ const propertyNames = new Set([
...@@ -238,6 +240,29 @@ function _markSkipped(path) { ...@@ -238,6 +240,29 @@ function _markSkipped(path) {
} }
} }
/**
* Returns true if an expression can be applied or false otherwise.
*/
function isValid(expression) {
const expressionTemplate = babelTemplate(
expression.source,
sourceHelpers.BABYLON_REPLACE_VAR_OPTIONS);
const dependencies = {};
if (expression.dependencies) {
for (const dependency of expression.dependencies) {
dependencies[dependency] = babelTypes.identifier('__v_0');
}
}
try {
expressionTemplate(dependencies);
} catch (e) {
return false;
}
return true;
}
class MutateDbWriter { class MutateDbWriter {
constructor(outputDir) { constructor(outputDir) {
this.seen = new Set(); this.seen = new Set();
...@@ -393,6 +418,11 @@ class MutateDbWriter { ...@@ -393,6 +418,11 @@ class MutateDbWriter {
return; return;
} }
// Test results.
if (!isValid(expression)) {
return;
}
// Write results. // Write results.
let dirPath = fsPath.join(self.outputDir, expression.type); let dirPath = fsPath.join(self.outputDir, expression.type);
if (!fs.existsSync(dirPath)) { if (!fs.existsSync(dirPath)) {
......
...@@ -36,12 +36,9 @@ class CrossOverMutator extends mutator.Mutator { ...@@ -36,12 +36,9 @@ class CrossOverMutator extends mutator.Mutator {
{canHaveSuper: canHaveSuper}); {canHaveSuper: canHaveSuper});
// Insert the statement. // Insert the statement.
var templateOptions = Object.assign({}, sourceHelpers.BABYLON_OPTIONS);
templateOptions['placeholderPattern'] = /^VAR_[0-9]+$/;
let toInsert = babelTemplate( let toInsert = babelTemplate(
randomExpression.source, randomExpression.source,
templateOptions); sourceHelpers.BABYLON_REPLACE_VAR_OPTIONS);
const dependencies = {}; const dependencies = {};
if (randomExpression.dependencies) { if (randomExpression.dependencies) {
......
...@@ -46,6 +46,9 @@ const BABYLON_OPTIONS = { ...@@ -46,6 +46,9 @@ const BABYLON_OPTIONS = {
], ],
} }
const BABYLON_REPLACE_VAR_OPTIONS = Object.assign({}, BABYLON_OPTIONS);
BABYLON_REPLACE_VAR_OPTIONS['placeholderPattern'] = /^VAR_[0-9]+$/;
function _isV8OrSpiderMonkeyLoad(path) { function _isV8OrSpiderMonkeyLoad(path) {
// 'load' and 'loadRelativeToScript' used by V8 and SpiderMonkey. // 'load' and 'loadRelativeToScript' used by V8 and SpiderMonkey.
return (babelTypes.isIdentifier(path.node.callee) && return (babelTypes.isIdentifier(path.node.callee) &&
...@@ -445,6 +448,7 @@ function generateCode(source, dependencies=[]) { ...@@ -445,6 +448,7 @@ function generateCode(source, dependencies=[]) {
module.exports = { module.exports = {
BABYLON_OPTIONS: BABYLON_OPTIONS, BABYLON_OPTIONS: BABYLON_OPTIONS,
BABYLON_REPLACE_VAR_OPTIONS: BABYLON_REPLACE_VAR_OPTIONS,
generateCode: generateCode, generateCode: generateCode,
loadDependencyAbs: loadDependencyAbs, loadDependencyAbs: loadDependencyAbs,
loadResource: loadResource, loadResource: loadResource,
......
// Copyright 2021 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 Test the script building the DB.
*/
'use strict';
const assert = require('assert');
const { execSync } = require("child_process");
const fs = require('fs');
const path = require('path');
const tempy = require('tempy');
function buildDb(inputDir, corpusName, outputDir) {
execSync(
`node build_db.js -i ${inputDir} -o ${outputDir} ${corpusName}`,
{stdio: ['pipe']});
}
describe('DB tests', () => {
// Test feeds an expression that does not apply.
it('omits erroneous expressions', () => {
const outPath = tempy.directory();
buildDb('test_data/db', 'this', outPath);
const indexFile = path.join(outPath, 'index.json');
const indexJSON = JSON.parse(fs.readFileSync(indexFile), 'utf-8');
assert.deepEqual(
indexJSON, {"statements": [], "superStatements": [], "all": []});
});
});
...@@ -38,31 +38,6 @@ function execFile(jsFile) { ...@@ -38,31 +38,6 @@ function execFile(jsFile) {
execSync("node " + jsFile, {stdio: ['pipe']}); execSync("node " + jsFile, {stdio: ['pipe']});
} }
function buildDb(inputDir, corpusName, outputDir) {
execSync(
`node build_db.js -i ${inputDir} -o ${outputDir} ${corpusName}`,
{stdio: ['pipe']});
}
function assertFuzzWithDbThrows(dbInputDir, corpusName, settings, regexp) {
const outPath = tempy.directory();
buildDb(dbInputDir, corpusName, outPath);
settings['MUTATE_CROSSOVER_INSERT'] = 1.0;
assert.throws(
() => {
createFuzzTest(
outPath, settings,
['regress/build_db/cross_over_mutator_input.js']);
},
err => {
assert(regexp.test(err));
return true;
},
'unexpected error',
);
}
describe('Regression tests', () => { describe('Regression tests', () => {
beforeEach(() => { beforeEach(() => {
helpers.deterministicRandom(sandbox); helpers.deterministicRandom(sandbox);
...@@ -135,25 +110,4 @@ describe('Regression tests', () => { ...@@ -135,25 +110,4 @@ describe('Regression tests', () => {
['regress/numbers/input_indices.js']); ['regress/numbers/input_indices.js']);
execFile(file); execFile(file);
}); });
it('create call expression', () => {
// TODO(machenbach): Build_db extracts a function expression without
// parentheses, re-parsing this later fails in cross-over mutator.
assertFuzzWithDbThrows(
'test_data/regress/build_db',
'destructuring',
this.settings,
SYNTAX_ERROR_RE);
});
it('create assignment expression', () => {
// TODO(machenbach): Build_db extracts some assignment expressions with a
// spurious dependency. This leads to an "unknown substitution" error
// when applying the template.
assertFuzzWithDbThrows(
'test_data/regress/build_db',
'this',
this.settings,
/.*Unknown substitution.*/);
});
}); });
// 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.
let x = 2;
let y = 2;
Math.pow(x, y);
// 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.
let x, y;
(function([ x = y = 1 ]) {}([]));
...@@ -48,7 +48,7 @@ function main() { ...@@ -48,7 +48,7 @@ function main() {
// super. // super.
const source = sourceHelpers.loadSource( const source = sourceHelpers.loadSource(
__dirname, __dirname,
'test_data/regress/build_db/cross_over_mutator_class_input.js'); 'test_data/cross_over_mutator_class_input.js');
try { try {
mutator.mutate(source); mutator.mutate(source);
nPass++; nPass++;
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment