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>( MutationKind mutation_kind = static_cast<MutationKind>(
generator.GetUInt32(static_cast<uint32_t>(MutationKind::kNumMutationKinds))); generator.GetUInt32(static_cast<uint32_t>(MutationKind::kNumMutationKinds)));
WgslMutator mutator(generator);
switch (mutation_kind) { switch (mutation_kind) {
case MutationKind::kSwapIntervals: case MutationKind::kSwapIntervals:
if (!SwapRandomIntervals(delimiter, wgsl_code, generator)) { if (!mutator.SwapRandomIntervals(delimiter, wgsl_code)) {
return 0; return 0;
} }
break; break;
case MutationKind::kDeleteInterval: case MutationKind::kDeleteInterval:
if (!DeleteRandomInterval(delimiter, wgsl_code, generator)) { if (!mutator.DeleteRandomInterval(delimiter, wgsl_code)) {
return 0; return 0;
} }
break; break;
case MutationKind::kDuplicateInterval: case MutationKind::kDuplicateInterval:
if (!DuplicateRandomInterval(delimiter, wgsl_code, generator)) { if (!mutator.DuplicateRandomInterval(delimiter, wgsl_code)) {
return 0; return 0;
} }
break; break;
case MutationKind::kReplaceIdentifier: case MutationKind::kReplaceIdentifier:
if (!ReplaceRandomIdentifier(wgsl_code, generator)) { if (!mutator.ReplaceRandomIdentifier(wgsl_code)) {
return 0; return 0;
} }
break; break;
case MutationKind::kReplaceLiteral: case MutationKind::kReplaceLiteral:
if (!ReplaceRandomIntLiteral(wgsl_code, generator)) { if (!mutator.ReplaceRandomIntLiteral(wgsl_code)) {
return 0; return 0;
} }
break; break;
case MutationKind::kInsertReturnStatement: case MutationKind::kInsertReturnStatement:
if (!InsertReturnStatement(wgsl_code, generator)) { if (!mutator.InsertReturnStatement(wgsl_code)) {
return 0; return 0;
} }
break; break;

View File

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

View File

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

View File

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