Clean regex fuzzer API

Introduces a class to clearly identify the interface of the mutation
functions used during regex-based fuzzing.

Bug: None

Change-Id: Ia6d125227cffc1c0b8944764c4e21825fd31d5cd
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/96360
Reviewed-by: Ryan Harrison <rharrison@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
Commit-Queue: Alastair Donaldson <allydonaldson@googlemail.com>
This commit is contained in:
Alastair F. Donaldson 2022-07-19 09:31:33 +00:00 committed by Dawn LUCI CQ
parent 1cfe319290
commit 853cbadc8f
4 changed files with 271 additions and 214 deletions

View File

@ -64,39 +64,40 @@ extern "C" size_t LLVMFuzzerCustomMutator(uint8_t* data,
MutationKind mutation_kind = static_cast<MutationKind>(
generator.GetUInt32(static_cast<uint32_t>(MutationKind::kNumMutationKinds)));
WgslMutator mutator(generator);
switch (mutation_kind) {
case MutationKind::kSwapIntervals:
if (!SwapRandomIntervals(delimiter, wgsl_code, generator)) {
if (!mutator.SwapRandomIntervals(delimiter, wgsl_code)) {
return 0;
}
break;
case MutationKind::kDeleteInterval:
if (!DeleteRandomInterval(delimiter, wgsl_code, generator)) {
if (!mutator.DeleteRandomInterval(delimiter, wgsl_code)) {
return 0;
}
break;
case MutationKind::kDuplicateInterval:
if (!DuplicateRandomInterval(delimiter, wgsl_code, generator)) {
if (!mutator.DuplicateRandomInterval(delimiter, wgsl_code)) {
return 0;
}
break;
case MutationKind::kReplaceIdentifier:
if (!ReplaceRandomIdentifier(wgsl_code, generator)) {
if (!mutator.ReplaceRandomIdentifier(wgsl_code)) {
return 0;
}
break;
case MutationKind::kReplaceLiteral:
if (!ReplaceRandomIntLiteral(wgsl_code, generator)) {
if (!mutator.ReplaceRandomIntLiteral(wgsl_code)) {
return 0;
}
break;
case MutationKind::kInsertReturnStatement:
if (!InsertReturnStatement(wgsl_code, generator)) {
if (!mutator.InsertReturnStatement(wgsl_code)) {
return 0;
}
break;

View File

@ -21,26 +21,44 @@
namespace tint::fuzzers::regex_fuzzer {
namespace {
class WgslMutatorTest : public WgslMutator {
public:
explicit WgslMutatorTest(RandomGenerator& generator) : WgslMutator(generator) {}
using WgslMutator::DeleteInterval;
using WgslMutator::DuplicateInterval;
using WgslMutator::FindClosingBrace;
using WgslMutator::GetFunctionBodyPositions;
using WgslMutator::GetIdentifiers;
using WgslMutator::GetIntLiterals;
using WgslMutator::ReplaceRegion;
using WgslMutator::SwapIntervals;
};
// Swaps two non-consecutive regions in the edge
TEST(SwapRegionsTest, SwapIntervalsEdgeNonConsecutive) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;";
std::string all_regions = R1 + R2 + R3;
// this call should swap R1 with R3.
SwapIntervals(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions);
mutator.SwapIntervals(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions);
ASSERT_EQ(R3 + R2 + R1, all_regions);
}
// Swaps two non-consecutive regions not in the edge
TEST(SwapRegionsTest, SwapIntervalsNonConsecutiveNonEdge) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// this call should swap R2 with R4.
SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length() + R3.length(), R4.length(),
all_regions);
mutator.SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length() + R3.length(),
R4.length(), all_regions);
ASSERT_EQ(R1 + R4 + R3 + R2 + R5, all_regions);
}
@ -48,12 +66,15 @@ TEST(SwapRegionsTest, SwapIntervalsNonConsecutiveNonEdge) {
// Swaps two consecutive regions not in the edge (sorrounded by other
// regions)
TEST(SwapRegionsTest, SwapIntervalsConsecutiveEdge) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4;
// this call should swap R2 with R3.
SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length(), R3.length(), all_regions);
mutator.SwapIntervals(R1.length(), R2.length(), R1.length() + R2.length(), R3.length(),
all_regions);
ASSERT_EQ(R1 + R3 + R2 + R4, all_regions);
}
@ -61,113 +82,137 @@ TEST(SwapRegionsTest, SwapIntervalsConsecutiveEdge) {
// Swaps two consecutive regions not in the edge (not sorrounded by other
// regions)
TEST(SwapRegionsTest, SwapIntervalsConsecutiveNonEdge) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// this call should swap R4 with R5.
SwapIntervals(R1.length() + R2.length() + R3.length(), R4.length(),
R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), all_regions);
mutator.SwapIntervals(R1.length() + R2.length() + R3.length(), R4.length(),
R1.length() + R2.length() + R3.length() + R4.length(), R5.length(),
all_regions);
ASSERT_EQ(R1 + R2 + R3 + R5 + R4, all_regions);
}
// Deletes the first region.
TEST(DeleteRegionTest, DeleteFirstRegion) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// This call should delete R1.
DeleteInterval(0, R1.length(), all_regions);
mutator.DeleteInterval(0, R1.length(), all_regions);
ASSERT_EQ(";" + R2 + R3 + R4 + R5, all_regions);
}
// Deletes the last region.
TEST(DeleteRegionTest, DeleteLastRegion) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// This call should delete R5.
DeleteInterval(R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), all_regions);
mutator.DeleteInterval(R1.length() + R2.length() + R3.length() + R4.length(), R5.length(),
all_regions);
ASSERT_EQ(R1 + R2 + R3 + R4 + ";", all_regions);
}
// Deletes the middle region.
TEST(DeleteRegionTest, DeleteMiddleRegion) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// This call should delete R3.
DeleteInterval(R1.length() + R2.length(), R3.length(), all_regions);
mutator.DeleteInterval(R1.length() + R2.length(), R3.length(), all_regions);
ASSERT_EQ(R1 + R2 + ";" + R4 + R5, all_regions);
}
TEST(InsertRegionTest, InsertRegionTest1) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// This call should insert R2 after R4.
DuplicateInterval(R1.length(), R2.length(),
R1.length() + R2.length() + R3.length() + R4.length() - 1, all_regions);
mutator.DuplicateInterval(R1.length(), R2.length(),
R1.length() + R2.length() + R3.length() + R4.length() - 1,
all_regions);
ASSERT_EQ(R1 + R2 + R3 + R4 + R2.substr(1, R2.size() - 1) + R5, all_regions);
}
TEST(InsertRegionTest, InsertRegionTest2) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// This call should insert R3 after R1.
DuplicateInterval(R1.length() + R2.length(), R3.length(), R1.length() - 1, all_regions);
mutator.DuplicateInterval(R1.length() + R2.length(), R3.length(), R1.length() - 1, all_regions);
ASSERT_EQ(R1 + R3.substr(1, R3.length() - 1) + R2 + R3 + R4 + R5, all_regions);
}
TEST(InsertRegionTest, InsertRegionTest3) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = ";region1;", R2 = ";regionregion2;", R3 = ";regionregionregion3;",
R4 = ";regionregionregionregion4;", R5 = ";regionregionregionregionregion5;";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// This call should insert R2 after R5.
DuplicateInterval(R1.length(), R2.length(), all_regions.length() - 1, all_regions);
mutator.DuplicateInterval(R1.length(), R2.length(), all_regions.length() - 1, all_regions);
ASSERT_EQ(R1 + R2 + R3 + R4 + R5 + R2.substr(1, R2.length() - 1), all_regions);
}
TEST(ReplaceIdentifierTest, ReplaceIdentifierTest1) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = "|region1|", R2 = "; region2;", R3 = "---------region3---------",
R4 = "++region4++", R5 = "***region5***";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// Replaces R3 with R1.
ReplaceRegion(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions);
mutator.ReplaceRegion(0, R1.length(), R1.length() + R2.length(), R3.length(), all_regions);
ASSERT_EQ(R1 + R2 + R1 + R4 + R5, all_regions);
}
TEST(ReplaceIdentifierTest, ReplaceIdentifierTest2) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string R1 = "|region1|", R2 = "; region2;", R3 = "---------region3---------",
R4 = "++region4++", R5 = "***region5***";
std::string all_regions = R1 + R2 + R3 + R4 + R5;
// Replaces R5 with R3.
ReplaceRegion(R1.length() + R2.length(), R3.length(),
R1.length() + R2.length() + R3.length() + R4.length(), R5.length(), all_regions);
mutator.ReplaceRegion(R1.length() + R2.length(), R3.length(),
R1.length() + R2.length() + R3.length() + R4.length(), R5.length(),
all_regions);
ASSERT_EQ(R1 + R2 + R3 + R4 + R3, all_regions);
}
TEST(GetIdentifierTest, GetIdentifierTest1) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string wgsl_code =
R"(fn clamp_0acf8f() {
var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
@ -187,7 +232,7 @@ TEST(GetIdentifierTest, GetIdentifierTest1) {
clamp_0acf8f();
})";
std::vector<std::pair<size_t, size_t>> identifiers_pos = GetIdentifiers(wgsl_code);
std::vector<std::pair<size_t, size_t>> identifiers_pos = mutator.GetIdentifiers(wgsl_code);
std::vector<std::pair<size_t, size_t>> ground_truth = {
std::make_pair(3, 12), std::make_pair(28, 3), std::make_pair(37, 4),
@ -204,6 +249,8 @@ TEST(GetIdentifierTest, GetIdentifierTest1) {
}
TEST(TestGetLiteralsValues, TestGetLiteralsValues1) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string wgsl_code =
R"(fn clamp_0acf8f() {
var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
@ -227,7 +274,7 @@ TEST(TestGetLiteralsValues, TestGetLiteralsValues1) {
foo_1 = 5 + 7;
var foo_3 : i32 = -20;)";
std::vector<std::pair<size_t, size_t>> literals_pos = GetIntLiterals(wgsl_code);
std::vector<std::pair<size_t, size_t>> literals_pos = mutator.GetIntLiterals(wgsl_code);
std::vector<std::string> ground_truth = {"3", "10", "5", "7", "-20"};
@ -241,6 +288,8 @@ TEST(TestGetLiteralsValues, TestGetLiteralsValues1) {
}
TEST(InsertReturnTest, FindClosingBrace) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string wgsl_code =
R"(fn clamp_0acf8f() {
if(false){
@ -269,7 +318,7 @@ TEST(InsertReturnTest, FindClosingBrace) {
var foo_3 : i32 = -20;
)";
size_t opening_bracket_pos = 18;
size_t closing_bracket_pos = FindClosingBrace(opening_bracket_pos, wgsl_code);
size_t closing_bracket_pos = mutator.FindClosingBrace(opening_bracket_pos, wgsl_code);
// The -1 is needed since the function body starts after the left bracket.
std::string function_body =
@ -286,6 +335,8 @@ TEST(InsertReturnTest, FindClosingBrace) {
}
TEST(InsertReturnTest, FindClosingBraceFailing) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string wgsl_code =
R"(fn clamp_0acf8f() {
// This comment } causes the test to fail.
@ -314,7 +365,7 @@ TEST(InsertReturnTest, FindClosingBraceFailing) {
foo_1 = 5 + 7;
var foo_3 : i32 = -20;)";
size_t opening_bracket_pos = 18;
size_t closing_bracket_pos = FindClosingBrace(opening_bracket_pos, wgsl_code);
size_t closing_bracket_pos = mutator.FindClosingBrace(opening_bracket_pos, wgsl_code);
// The -1 is needed since the function body starts after the left bracket.
std::string function_body =
@ -330,6 +381,8 @@ TEST(InsertReturnTest, FindClosingBraceFailing) {
}
TEST(TestInsertReturn, TestInsertReturn1) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string wgsl_code =
R"(fn clamp_0acf8f() {
var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
@ -390,6 +443,8 @@ TEST(TestInsertReturn, TestInsertReturn1) {
}
TEST(TestInsertReturn, TestFunctionPositions) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string wgsl_code =
R"(fn clamp_0acf8f() {
var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>());
@ -418,12 +473,14 @@ TEST(TestInsertReturn, TestFunctionPositions) {
foo_1 = 5 + 7;
var foo_3 : i32 = -20;)";
std::vector<size_t> function_positions = GetFunctionBodyPositions(wgsl_code);
std::vector<size_t> function_positions = mutator.GetFunctionBodyPositions(wgsl_code);
std::vector<size_t> expected_positions = {180, 586};
ASSERT_EQ(expected_positions, function_positions);
}
TEST(TestInsertReturn, TestMissingSemicolon) {
RandomGenerator generator(0);
WgslMutatorTest mutator(generator);
std::string wgsl_code =
R"(fn clamp_0acf8f() {
var res: vec2<f32> = clamp(vec2<f32>(), vec2<f32>(), vec2<f32>())
@ -452,8 +509,7 @@ TEST(TestInsertReturn, TestMissingSemicolon) {
foo_1 = 5 + 7;
var foo_3 : i32 = -20;)";
RandomGenerator generator(0);
InsertReturnStatement(wgsl_code, generator);
mutator.InsertReturnStatement(wgsl_code);
// No semicolons found in the function's body, so wgsl_code
// should remain unchanged.

View File

@ -26,7 +26,9 @@
namespace tint::fuzzers::regex_fuzzer {
std::vector<size_t> FindDelimiterIndices(const std::string& delimiter,
WgslMutator::WgslMutator(RandomGenerator& generator) : generator_(generator) {}
std::vector<size_t> WgslMutator::FindDelimiterIndices(const std::string& delimiter,
const std::string& wgsl_code) {
std::vector<size_t> result;
for (size_t pos = wgsl_code.find(delimiter, 0); pos != std::string::npos;
@ -37,7 +39,7 @@ std::vector<size_t> FindDelimiterIndices(const std::string& delimiter,
return result;
}
std::vector<std::pair<size_t, size_t>> GetIdentifiers(const std::string& wgsl_code) {
std::vector<std::pair<size_t, size_t>> WgslMutator::GetIdentifiers(const std::string& wgsl_code) {
std::vector<std::pair<size_t, size_t>> result;
// This regular expression works by looking for a character that
@ -61,7 +63,7 @@ std::vector<std::pair<size_t, size_t>> GetIdentifiers(const std::string& wgsl_co
return result;
}
std::vector<std::pair<size_t, size_t>> GetIntLiterals(const std::string& s) {
std::vector<std::pair<size_t, size_t>> WgslMutator::GetIntLiterals(const std::string& s) {
std::vector<std::pair<size_t, size_t>> result;
// Looks for integer literals in decimal or hexadecimal form.
@ -83,7 +85,7 @@ std::vector<std::pair<size_t, size_t>> GetIntLiterals(const std::string& s) {
return result;
}
size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code) {
size_t WgslMutator::FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code) {
size_t open_bracket_count = 1;
size_t pos = opening_bracket_pos + 1;
while (open_bracket_count >= 1 && pos < wgsl_code.size()) {
@ -97,7 +99,7 @@ size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code
return (pos == wgsl_code.size() && open_bracket_count >= 1) ? 0 : pos - 1;
}
std::vector<size_t> GetFunctionBodyPositions(const std::string& wgsl_code) {
std::vector<size_t> WgslMutator::GetFunctionBodyPositions(const std::string& wgsl_code) {
// Finds all the functions with a non-void return value.
std::regex function_regex("fn.*?->.*?\\{");
std::smatch match;
@ -113,7 +115,7 @@ std::vector<size_t> GetFunctionBodyPositions(const std::string& wgsl_code) {
return result;
}
bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) {
bool WgslMutator::InsertReturnStatement(std::string& wgsl_code) {
std::vector<size_t> function_body_positions = GetFunctionBodyPositions(wgsl_code);
// No function was found in wgsl_code.
@ -123,7 +125,7 @@ bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) {
// Pick a random function's opening bracket, find the corresponding closing
// bracket, and find a semi-colon within the function body.
size_t left_bracket_pos = generator.GetRandomElement(function_body_positions);
size_t left_bracket_pos = generator_.GetRandomElement(function_body_positions);
size_t right_bracket_pos = FindClosingBrace(left_bracket_pos, wgsl_code);
@ -141,14 +143,14 @@ bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) {
return false;
}
size_t semicolon_position = generator.GetRandomElement(semicolon_positions);
size_t semicolon_position = generator_.GetRandomElement(semicolon_positions);
// Get all identifiers and integer literals to use as potential return values.
std::vector<std::pair<size_t, size_t>> identifiers = GetIdentifiers(wgsl_code);
auto return_values = identifiers;
std::vector<std::pair<size_t, size_t>> int_literals = GetIntLiterals(wgsl_code);
return_values.insert(return_values.end(), int_literals.begin(), int_literals.end());
std::pair<size_t, size_t> return_value = generator.GetRandomElement(return_values);
std::pair<size_t, size_t> return_value = generator_.GetRandomElement(return_values);
std::string return_statement =
"return " + wgsl_code.substr(return_value.first, return_value.second) + ";";
@ -157,7 +159,7 @@ bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator) {
return true;
}
void SwapIntervals(size_t idx1,
void WgslMutator::SwapIntervals(size_t idx1,
size_t reg1_len,
size_t idx2,
size_t reg2_len,
@ -172,16 +174,19 @@ void SwapIntervals(size_t idx1,
wgsl_code.replace(idx1 + 1, region_1.size(), region_2);
}
void DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code) {
void WgslMutator::DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code) {
wgsl_code.erase(idx1 + 1, reg_len - 1);
}
void DuplicateInterval(size_t idx1, size_t reg1_len, size_t idx2, std::string& wgsl_code) {
void WgslMutator::DuplicateInterval(size_t idx1,
size_t reg1_len,
size_t idx2,
std::string& wgsl_code) {
std::string region = wgsl_code.substr(idx1 + 1, reg1_len - 1);
wgsl_code.insert(idx2 + 1, region);
}
void ReplaceRegion(size_t idx1,
void WgslMutator::ReplaceRegion(size_t idx1,
size_t id1_len,
size_t idx2,
size_t id2_len,
@ -191,7 +196,7 @@ void ReplaceRegion(size_t idx1,
wgsl_code.replace(idx2, region_2.size(), region_1);
}
void ReplaceInterval(size_t start_index,
void WgslMutator::ReplaceInterval(size_t start_index,
size_t length,
std::string replacement_text,
std::string& wgsl_code) {
@ -199,9 +204,7 @@ void ReplaceInterval(size_t start_index,
wgsl_code.replace(start_index, length, replacement_text);
}
bool SwapRandomIntervals(const std::string& delimiter,
std::string& wgsl_code,
RandomGenerator& generator) {
bool WgslMutator::SwapRandomIntervals(const std::string& delimiter, std::string& wgsl_code) {
std::vector<size_t> delimiter_positions = FindDelimiterIndices(delimiter, wgsl_code);
// Need to have at least 3 indices.
@ -212,12 +215,12 @@ bool SwapRandomIntervals(const std::string& delimiter,
// Choose indices:
// interval_1_start < interval_1_end <= interval_2_start < interval_2_end
uint32_t interval_1_start =
generator.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()) - 2u);
uint32_t interval_1_end = generator.GetUInt32(
generator_.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()) - 2u);
uint32_t interval_1_end = generator_.GetUInt32(
interval_1_start + 1u, static_cast<uint32_t>(delimiter_positions.size()) - 1u);
uint32_t interval_2_start =
generator.GetUInt32(interval_1_end, static_cast<uint32_t>(delimiter_positions.size()) - 1u);
uint32_t interval_2_end = generator.GetUInt32(
uint32_t interval_2_start = generator_.GetUInt32(
interval_1_end, static_cast<uint32_t>(delimiter_positions.size()) - 1u);
uint32_t interval_2_end = generator_.GetUInt32(
interval_2_start + 1u, static_cast<uint32_t>(delimiter_positions.size()));
SwapIntervals(delimiter_positions[interval_1_start],
@ -229,9 +232,7 @@ bool SwapRandomIntervals(const std::string& delimiter,
return true;
}
bool DeleteRandomInterval(const std::string& delimiter,
std::string& wgsl_code,
RandomGenerator& generator) {
bool WgslMutator::DeleteRandomInterval(const std::string& delimiter, std::string& wgsl_code) {
std::vector<size_t> delimiter_positions = FindDelimiterIndices(delimiter, wgsl_code);
// Need to have at least 2 indices.
@ -240,9 +241,9 @@ bool DeleteRandomInterval(const std::string& delimiter,
}
uint32_t interval_start =
generator.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()) - 1u);
uint32_t interval_end =
generator.GetUInt32(interval_start + 1u, static_cast<uint32_t>(delimiter_positions.size()));
generator_.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()) - 1u);
uint32_t interval_end = generator_.GetUInt32(interval_start + 1u,
static_cast<uint32_t>(delimiter_positions.size()));
DeleteInterval(delimiter_positions[interval_start],
delimiter_positions[interval_end] - delimiter_positions[interval_start],
@ -251,9 +252,7 @@ bool DeleteRandomInterval(const std::string& delimiter,
return true;
}
bool DuplicateRandomInterval(const std::string& delimiter,
std::string& wgsl_code,
RandomGenerator& generator) {
bool WgslMutator::DuplicateRandomInterval(const std::string& delimiter, std::string& wgsl_code) {
std::vector<size_t> delimiter_positions = FindDelimiterIndices(delimiter, wgsl_code);
// Need to have at least 2 indices
@ -262,11 +261,11 @@ bool DuplicateRandomInterval(const std::string& delimiter,
}
uint32_t interval_start =
generator.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()) - 1u);
uint32_t interval_end =
generator.GetUInt32(interval_start + 1u, static_cast<uint32_t>(delimiter_positions.size()));
generator_.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()) - 1u);
uint32_t interval_end = generator_.GetUInt32(interval_start + 1u,
static_cast<uint32_t>(delimiter_positions.size()));
uint32_t duplication_point =
generator.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()));
generator_.GetUInt32(static_cast<uint32_t>(delimiter_positions.size()));
DuplicateInterval(delimiter_positions[interval_start],
delimiter_positions[interval_end] - delimiter_positions[interval_start],
@ -275,7 +274,7 @@ bool DuplicateRandomInterval(const std::string& delimiter,
return true;
}
bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator) {
bool WgslMutator::ReplaceRandomIdentifier(std::string& wgsl_code) {
std::vector<std::pair<size_t, size_t>> identifiers = GetIdentifiers(wgsl_code);
// Need at least 2 identifiers
@ -283,12 +282,12 @@ bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator)
return false;
}
uint32_t id1_index = generator.GetUInt32(static_cast<uint32_t>(identifiers.size()));
uint32_t id2_index = generator.GetUInt32(static_cast<uint32_t>(identifiers.size()));
uint32_t id1_index = generator_.GetUInt32(static_cast<uint32_t>(identifiers.size()));
uint32_t id2_index = generator_.GetUInt32(static_cast<uint32_t>(identifiers.size()));
// The two identifiers must be different
while (id1_index == id2_index) {
id2_index = generator.GetUInt32(static_cast<uint32_t>(identifiers.size()));
id2_index = generator_.GetUInt32(static_cast<uint32_t>(identifiers.size()));
}
ReplaceRegion(identifiers[id1_index].first, identifiers[id1_index].second,
@ -297,7 +296,7 @@ bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator)
return true;
}
bool ReplaceRandomIntLiteral(std::string& wgsl_code, RandomGenerator& generator) {
bool WgslMutator::ReplaceRandomIntLiteral(std::string& wgsl_code) {
std::vector<std::pair<size_t, size_t>> literals = GetIntLiterals(wgsl_code);
// Need at least one integer literal
@ -305,13 +304,13 @@ bool ReplaceRandomIntLiteral(std::string& wgsl_code, RandomGenerator& generator)
return false;
}
uint32_t literal_index = generator.GetUInt32(static_cast<uint32_t>(literals.size()));
uint32_t literal_index = generator_.GetUInt32(static_cast<uint32_t>(literals.size()));
// INT_MAX = 2147483647, INT_MIN = -2147483648
std::vector<std::string> boundary_values = {"2147483647", "-2147483648", "1",
"-1", "0", "4294967295"};
uint32_t boundary_index = generator.GetUInt32(static_cast<uint32_t>(boundary_values.size()));
uint32_t boundary_index = generator_.GetUInt32(static_cast<uint32_t>(boundary_values.size()));
ReplaceInterval(literals[literal_index].first, literals[literal_index].second,
boundary_values[boundary_index], wgsl_code);

View File

@ -23,152 +23,153 @@
namespace tint::fuzzers::regex_fuzzer {
/// A function that given a delimiter, returns a vector that contains
/// all the positions of the delimiter in the WGSL code.
/// @param delimiter - the delimiter of the enclosed region.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @return a vector with the positions of the delimiter in the WGSL code.
std::vector<size_t> FindDelimiterIndices(const std::string& delimiter,
const std::string& wgsl_code);
/// Class encapsulating code for regex-based mutation of WGSL shaders.
class WgslMutator {
public:
/// Constructor
/// @param generator - pseudo-random generator to use in mutator
explicit WgslMutator(RandomGenerator& generator);
/// A function that finds all the identifiers in a WGSL-like string.
/// @param wgsl_code - the WGSL-like string where the identifiers will be found.
/// @return a vector with the positions and the length of all the
/// identifiers in wgsl_code.
std::vector<std::pair<size_t, size_t>> GetIdentifiers(const std::string& wgsl_code);
/// A function that, given WGSL-like string and a delimiter,
/// generates another WGSL-like string by picking two random regions
/// enclosed by the delimiter and swapping them.
/// @param delimiter - the delimiter that will be used to find enclosed regions.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @return true if a swap happened or false otherwise.
bool SwapRandomIntervals(const std::string& delimiter, std::string& wgsl_code);
/// A function that returns returns the starting position
/// and the length of all the integer literals in a WGSL-like string.
/// @param wgsl_code - the WGSL-like string where the int literals
/// will be found.
/// @return a vector with the starting positions and the length
/// of all the integer literals.
std::vector<std::pair<size_t, size_t>> GetIntLiterals(const std::string& wgsl_code);
/// A function that, given a WGSL-like string and a delimiter,
/// generates another WGSL-like string by deleting a random
/// region enclosed by the delimiter.
/// @param delimiter - the delimiter that will be used to find enclosed regions.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @return true if a deletion happened or false otherwise.
bool DeleteRandomInterval(const std::string& delimiter, std::string& wgsl_code);
/// Finds a possible closing brace corresponding to the opening
/// brace at position opening_bracket_pos.
/// @param opening_bracket_pos - the position of the opening brace.
/// @param wgsl_code - the WGSL-like string where the closing brace.
/// @return the position of the closing bracket or 0 if there is no closing
/// brace.
size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code);
/// A function that, given a WGSL-like string and a delimiter,
/// generates another WGSL-like string by duplicating a random
/// region enclosed by the delimiter.
/// @param delimiter - the delimiter that will be used to find enclosed regions.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @return true if a duplication happened or false otherwise.
bool DuplicateRandomInterval(const std::string& delimiter, std::string& wgsl_code);
/// Returns the starting_position of the bodies of the functions
/// that follow the regular expression: fn.*?->.*?\\{, which searches for the
/// keyword fn followed by the function name, its return type and opening brace.
/// @param wgsl_code - the WGSL-like string where the functions will be
/// searched.
/// @return a vector with the starting position of the function bodies in
/// wgsl_code.
std::vector<size_t> GetFunctionBodyPositions(const std::string& wgsl_code);
/// Replaces a randomly-chosen identifier in wgsl_code.
/// @param wgsl_code - WGSL-like string where the replacement will occur.
/// @return true if a replacement happened or false otherwise.
bool ReplaceRandomIdentifier(std::string& wgsl_code);
/// Given 4 indices, idx1, idx2, idx3 and idx4 it swaps the regions
/// in the interval (idx1, idx2] with the region in the interval (idx3, idx4]
/// in wgsl_text.
/// @param idx1 - starting index of the first region.
/// @param reg1_len - length of the first region.
/// @param idx2 - starting index of the second region.
/// @param reg2_len - length of the second region.
/// @param wgsl_code - the string where the swap will occur.
void SwapIntervals(size_t idx1,
size_t reg1_len,
size_t idx2,
size_t reg2_len,
std::string& wgsl_code);
/// Replaces the value of a randomly-chosen integer with one of
/// the values in the set {INT_MAX, INT_MIN, 0, -1}.
/// @param wgsl_code - WGSL-like string where the replacement will occur.
/// @return true if a replacement happened or false otherwise.
bool ReplaceRandomIntLiteral(std::string& wgsl_code);
/// Given index idx1 it delets the region of length interval_len
/// starting at index idx1;
/// @param idx1 - starting index of the first region.
/// @param reg_len - terminating index of the second region.
/// @param wgsl_code - the string where the swap will occur.
void DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code);
/// Inserts a return statement in a randomly chosen function of a
/// WGSL-like string. The return value is a randomly-chosen identifier
/// or literal in the string.
/// @param wgsl_code - WGSL-like string that will be mutated.
/// @return true if the mutation was succesful or false otherwise.
bool InsertReturnStatement(std::string& wgsl_code);
/// Given 2 indices, idx1, idx2, it inserts the region of length
/// reg1_len starting at idx1 after idx2.
/// @param idx1 - starting index of region.
/// @param reg1_len - length of the region.
/// @param idx2 - the position where the region will be inserted.
/// @param wgsl_code - the string where the swap will occur.
void DuplicateInterval(size_t idx1, size_t reg1_len, size_t idx2, std::string& wgsl_code);
protected:
/// Given index idx1 it delets the region of length interval_len
/// starting at index idx1;
/// @param idx1 - starting index of the first region.
/// @param reg_len - terminating index of the second region.
/// @param wgsl_code - the string where the swap will occur.
void DeleteInterval(size_t idx1, size_t reg_len, std::string& wgsl_code);
/// Replaces a region of a WGSL-like string of length id2_len starting
/// at position idx2 with a region of length id1_len starting at
/// position idx1.
/// @param idx1 - starting position of the first region.
/// @param id1_len - length of the first region.
/// @param idx2 - starting position of the second region.
/// @param id2_len - length of the second region.
/// @param wgsl_code - the string where the replacement will occur.
void ReplaceRegion(size_t idx1,
/// Given 2 indices, idx1, idx2, it inserts the region of length
/// reg1_len starting at idx1 after idx2.
/// @param idx1 - starting index of region.
/// @param reg1_len - length of the region.
/// @param idx2 - the position where the region will be inserted.
/// @param wgsl_code - the string where the swap will occur.
void DuplicateInterval(size_t idx1, size_t reg1_len, size_t idx2, std::string& wgsl_code);
/// Finds a possible closing brace corresponding to the opening
/// brace at position opening_bracket_pos.
/// @param opening_bracket_pos - the position of the opening brace.
/// @param wgsl_code - the WGSL-like string where the closing brace.
/// @return the position of the closing bracket or 0 if there is no closing
/// brace.
size_t FindClosingBrace(size_t opening_bracket_pos, const std::string& wgsl_code);
/// Returns the starting_position of the bodies of the functions
/// that follow the regular expression: fn.*?->.*?\\{, which searches for the
/// keyword fn followed by the function name, its return type and opening brace.
/// @param wgsl_code - the WGSL-like string where the functions will be
/// searched.
/// @return a vector with the starting position of the function bodies in
/// wgsl_code.
std::vector<size_t> GetFunctionBodyPositions(const std::string& wgsl_code);
/// A function that finds all the identifiers in a WGSL-like string.
/// @param wgsl_code - the WGSL-like string where the identifiers will be found.
/// @return a vector with the positions and the length of all the
/// identifiers in wgsl_code.
std::vector<std::pair<size_t, size_t>> GetIdentifiers(const std::string& wgsl_code);
/// A function that returns returns the starting position
/// and the length of all the integer literals in a WGSL-like string.
/// @param wgsl_code - the WGSL-like string where the int literals
/// will be found.
/// @return a vector with the starting positions and the length
/// of all the integer literals.
std::vector<std::pair<size_t, size_t>> GetIntLiterals(const std::string& wgsl_code);
/// Replaces a region of a WGSL-like string of length id2_len starting
/// at position idx2 with a region of length id1_len starting at
/// position idx1.
/// @param idx1 - starting position of the first region.
/// @param id1_len - length of the first region.
/// @param idx2 - starting position of the second region.
/// @param id2_len - length of the second region.
/// @param wgsl_code - the string where the replacement will occur.
void ReplaceRegion(size_t idx1,
size_t id1_len,
size_t idx2,
size_t id2_len,
std::string& wgsl_code);
/// Replaces an interval of length `length` starting at start_index
/// with the `replacement_text`.
/// @param start_index - starting position of the interval to be replaced.
/// @param length - length of the interval to be replaced.
/// @param replacement_text - the interval that will be used as a replacement.
/// @param wgsl_code - the WGSL-like string where the replacement will occur.
void ReplaceInterval(size_t start_index,
/// Given 4 indices, idx1, idx2, idx3 and idx4 it swaps the regions
/// in the interval (idx1, idx2] with the region in the interval (idx3, idx4]
/// in wgsl_text.
/// @param idx1 - starting index of the first region.
/// @param reg1_len - length of the first region.
/// @param idx2 - starting index of the second region.
/// @param reg2_len - length of the second region.
/// @param wgsl_code - the string where the swap will occur.
void SwapIntervals(size_t idx1,
size_t reg1_len,
size_t idx2,
size_t reg2_len,
std::string& wgsl_code);
private:
/// A function that given a delimiter, returns a vector that contains
/// all the positions of the delimiter in the WGSL code.
/// @param delimiter - the delimiter of the enclosed region.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @return a vector with the positions of the delimiter in the WGSL code.
std::vector<size_t> FindDelimiterIndices(const std::string& delimiter,
const std::string& wgsl_code);
/// Replaces an interval of length `length` starting at start_index
/// with the `replacement_text`.
/// @param start_index - starting position of the interval to be replaced.
/// @param length - length of the interval to be replaced.
/// @param replacement_text - the interval that will be used as a replacement.
/// @param wgsl_code - the WGSL-like string where the replacement will occur.
void ReplaceInterval(size_t start_index,
size_t length,
std::string replacement_text,
std::string& wgsl_code);
/// A function that, given WGSL-like string and a delimiter,
/// generates another WGSL-like string by picking two random regions
/// enclosed by the delimiter and swapping them.
/// @param delimiter - the delimiter that will be used to find enclosed regions.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @param generator - the random number generator.
/// @return true if a swap happened or false otherwise.
bool SwapRandomIntervals(const std::string& delimiter,
std::string& wgsl_code,
RandomGenerator& generator);
RandomGenerator& generator_;
};
/// A function that, given a WGSL-like string and a delimiter,
/// generates another WGSL-like string by deleting a random
/// region enclosed by the delimiter.
/// @param delimiter - the delimiter that will be used to find enclosed regions.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @param generator - the random number generator.
/// @return true if a deletion happened or false otherwise.
bool DeleteRandomInterval(const std::string& delimiter,
std::string& wgsl_code,
RandomGenerator& generator);
/// A function that, given a WGSL-like string and a delimiter,
/// generates another WGSL-like string by duplicating a random
/// region enclosed by the delimiter.
/// @param delimiter - the delimiter that will be used to find enclosed regions.
/// @param wgsl_code - the initial string (WGSL code) that will be mutated.
/// @param generator - the random number generator.
/// @return true if a duplication happened or false otherwise.
bool DuplicateRandomInterval(const std::string& delimiter,
std::string& wgsl_code,
RandomGenerator& generator);
/// Replaces a randomly-chosen identifier in wgsl_code.
/// @param wgsl_code - WGSL-like string where the replacement will occur.
/// @param generator - the random number generator.
/// @return true if a replacement happened or false otherwise.
bool ReplaceRandomIdentifier(std::string& wgsl_code, RandomGenerator& generator);
/// Replaces the value of a randomly-chosen integer with one of
/// the values in the set {INT_MAX, INT_MIN, 0, -1}.
/// @param wgsl_code - WGSL-like string where the replacement will occur.
/// @param generator - the random number generator.
/// @return true if a replacement happened or false otherwise.
bool ReplaceRandomIntLiteral(std::string& wgsl_code, RandomGenerator& generator);
/// Inserts a return statement in a randomly chosen function of a
/// WGSL-like string. The return value is a randomly-chosen identifier
/// or literal in the string.
/// @param wgsl_code - WGSL-like string that will be mutated.
/// @param generator - the random number generator.
/// @return true if the mutation was succesful or false otherwise.
bool InsertReturnStatement(std::string& wgsl_code, RandomGenerator& generator);
} // namespace tint::fuzzers::regex_fuzzer
#endif // SRC_TINT_FUZZERS_TINT_REGEX_FUZZER_WGSL_MUTATOR_H_