jfuzz: Add try..catch..finally blocks
Adds try..catch..finally blocks to generated code.
Also adds invocations of a nop() function.
Bug: 69128828
Test: Manually generating, dx'ing, and verifying outputs
Change-Id: I5e54d63e694acf8106c6deb966a584b9253001b6
diff --git a/tools/jfuzz/README.md b/tools/jfuzz/README.md
index 10d175b..4edfe1b 100644
--- a/tools/jfuzz/README.md
+++ b/tools/jfuzz/README.md
@@ -28,6 +28,8 @@
(higher values yield deeper nested conditionals)
-n : defines a fuzzing nest for for/while/do-while loops
(higher values yield deeper nested loops)
+ -t : defines a fuzzing nest for try-catch-finally blocks
+ (higher values yield deeper nested try-catch-finally blocks)
-v : prints version number and exits
-h : prints help and exits
diff --git a/tools/jfuzz/jfuzz.cc b/tools/jfuzz/jfuzz.cc
index 7990c6c..825648b 100644
--- a/tools/jfuzz/jfuzz.cc
+++ b/tools/jfuzz/jfuzz.cc
@@ -30,9 +30,6 @@
/*
* Operators.
*/
-
-#define EMIT(x) fputs((x)[random0(sizeof(x)/sizeof(const char*))], out_);
-
static constexpr const char* kIncDecOps[] = { "++", "--" };
static constexpr const char* kIntUnaryOps[] = { "+", "-", "~" };
static constexpr const char* kFpUnaryOps[] = { "+", "-" };
@@ -51,11 +48,21 @@
static constexpr const char* kRelOps[] = { "==", "!=", ">", ">=", "<", "<=" };
/*
+ * Exceptions.
+ */
+static const char* kExceptionTypes[] = {
+ "IllegalStateException",
+ "NullPointerException",
+ "IllegalArgumentException",
+ "ArrayIndexOutOfBoundsException"
+};
+
+/*
* Version of JFuzz. Increase this each time changes are made to the program
* to preserve the property that a given version of JFuzz yields the same
* fuzzed program for a deterministic random seed.
*/
-const char* VERSION = "1.4";
+const char* VERSION = "1.5";
/*
* Maximum number of array dimensions, together with corresponding maximum size
@@ -64,6 +71,14 @@
static const uint32_t kMaxDim = 10;
static const uint32_t kMaxDimSize[kMaxDim + 1] = { 0, 1000, 32, 10, 6, 4, 3, 3, 2, 2, 2 };
+/*
+ * Utility function to return the number of elements in an array.
+ */
+template <typename T, uint32_t N>
+constexpr uint32_t countof(T const (&)[N]) {
+ return N;
+}
+
/**
* A class that generates a random program that compiles correctly. The program
* is generated using rules that generate various programming constructs. Each rule
@@ -78,7 +93,8 @@
uint32_t expr_depth,
uint32_t stmt_length,
uint32_t if_nest,
- uint32_t loop_nest)
+ uint32_t loop_nest,
+ uint32_t try_nest)
: out_(out),
fuzz_random_engine_(seed),
fuzz_seed_(seed),
@@ -86,6 +102,7 @@
fuzz_stmt_length_(stmt_length),
fuzz_if_nest_(if_nest),
fuzz_loop_nest_(loop_nest),
+ fuzz_try_nest_(try_nest),
return_type_(randomType()),
array_type_(randomType()),
array_dim_(random1(kMaxDim)),
@@ -97,6 +114,7 @@
loop_nest_(0),
switch_nest_(0),
do_nest_(0),
+ try_nest_(0),
boolean_local_(0),
int_local_(0),
long_local_(0),
@@ -168,6 +186,12 @@
}
}
+ // Emits a random strong selected from an array of operator strings.
+ template <std::uint32_t N>
+ inline void emitOneOf(const char* const (&ops)[N]) {
+ fputs(ops[random0(N)], out_);
+ }
+
//
// Expressions.
//
@@ -177,9 +201,9 @@
if (tp == kBoolean) {
fputc('!', out_);
} else if (isInteger(tp)) {
- EMIT(kIntUnaryOps);
+ emitOneOf(kIntUnaryOps);
} else { // isFP(tp)
- EMIT(kFpUnaryOps);
+ emitOneOf(kFpUnaryOps);
}
}
@@ -188,38 +212,38 @@
if (tp == kBoolean) {
// Not applicable, just leave "as is".
} else { // isInteger(tp) || isFP(tp)
- EMIT(kIncDecOps);
+ emitOneOf(kIncDecOps);
}
}
// Emit a binary operator (same type in-out).
void emitBinaryOp(Type tp) {
if (tp == kBoolean) {
- EMIT(kBoolBinOps);
+ emitOneOf(kBoolBinOps);
} else if (isInteger(tp)) {
- EMIT(kIntBinOps);
+ emitOneOf(kIntBinOps);
} else { // isFP(tp)
- EMIT(kFpBinOps);
+ emitOneOf(kFpBinOps);
}
}
// Emit an assignment operator (same type in-out).
void emitAssignmentOp(Type tp) {
if (tp == kBoolean) {
- EMIT(kBoolAssignOps);
+ emitOneOf(kBoolAssignOps);
} else if (isInteger(tp)) {
- EMIT(kIntAssignOps);
+ emitOneOf(kIntAssignOps);
} else { // isFP(tp)
- EMIT(kFpAssignOps);
+ emitOneOf(kFpAssignOps);
}
}
// Emit a relational operator (one type in, boolean out).
void emitRelationalOp(Type tp) {
if (tp == kBoolean) {
- EMIT(kBoolRelOps);
+ emitOneOf(kBoolRelOps);
} else { // isInteger(tp) || isFP(tp)
- EMIT(kRelOps);
+ emitOneOf(kRelOps);
}
}
@@ -808,7 +832,7 @@
fputs("{\n", out_);
indentation_ += 2;
emitIndentation();
- fprintf(out_, "int i%u = %d;", loop_nest_, isWhile ? -1 : 0);
+ fprintf(out_, "int i%u = %d;\n", loop_nest_, isWhile ? -1 : 0);
emitIndentation();
if (isWhile) {
fprintf(out_, "while (++i%u < ", loop_nest_);
@@ -871,6 +895,73 @@
return mayFollowTrue || mayFollowFalse;
}
+ bool emitTry() {
+ fputs("try {\n", out_);
+ indentation_ += 2;
+ bool mayFollow = emitStatementList();
+ indentation_ -= 2;
+ emitIndentation();
+ fputc('}', out_);
+ return mayFollow;
+ }
+
+ bool emitCatch() {
+ uint32_t count = random1(countof(kExceptionTypes));
+ bool mayFollow = false;
+ for (uint32_t i = 0; i < count; ++i) {
+ fprintf(out_, " catch (%s ex%u_%u) {\n", kExceptionTypes[i], try_nest_, i);
+ indentation_ += 2;
+ mayFollow |= emitStatementList();
+ indentation_ -= 2;
+ emitIndentation();
+ fputc('}', out_);
+ }
+ return mayFollow;
+ }
+
+ bool emitFinally() {
+ fputs(" finally {\n", out_);
+ indentation_ += 2;
+ bool mayFollow = emitStatementList();
+ indentation_ -= 2;
+ emitIndentation();
+ fputc('}', out_);
+ return mayFollow;
+ }
+
+ // Emit a try-catch-finally block.
+ bool emitTryCatchFinally() {
+ // Apply a hard limit on the number of catch blocks. This is for
+ // javac which fails if blocks within try-catch-finally are too
+ // large (much less than you'd expect).
+ if (try_nest_ > fuzz_try_nest_) {
+ return emitAssignment(); // fall back
+ }
+
+ ++try_nest_; // Entering try-catch-finally
+
+ bool mayFollow = emitTry();
+ switch (random0(3)) {
+ case 0: // try..catch
+ mayFollow |= emitCatch();
+ break;
+ case 1: // try..finally
+ mayFollow &= emitFinally();
+ break;
+ case 2: // try..catch..finally
+ // When determining whether code may follow, we observe that a
+ // finally block always follows after try and catch
+ // block. Code may only follow if the finally block permits
+ // and either the try or catch block allows code to follow.
+ mayFollow = (mayFollow | emitCatch()) & emitFinally();
+ break;
+ }
+ fputc('\n', out_);
+
+ --try_nest_; // Leaving try-catch-finally
+ return mayFollow;
+ }
+
// Emit a switch statement.
bool emitSwitch() {
// Continuing if nest becomes less likely as the depth grows.
@@ -915,6 +1006,11 @@
return mayFollow;
}
+ bool emitNopCall() {
+ fputs("nop();\n", out_);
+ return true;
+ }
+
// Emit an assignment statement.
bool emitAssignment() {
Type tp = randomType();
@@ -930,16 +1026,18 @@
// Emit a single statement. Returns true if statements may follow.
bool emitStatement() {
switch (random1(16)) { // favor assignments
- case 1: return emitReturn(false); break;
- case 2: return emitContinue(); break;
- case 3: return emitBreak(); break;
- case 4: return emitScope(); break;
- case 5: return emitArrayInit(); break;
- case 6: return emitForLoop(); break;
- case 7: return emitDoLoop(); break;
- case 8: return emitIfStmt(); break;
- case 9: return emitSwitch(); break;
- default: return emitAssignment(); break;
+ case 1: return emitReturn(false); break;
+ case 2: return emitContinue(); break;
+ case 3: return emitBreak(); break;
+ case 4: return emitScope(); break;
+ case 5: return emitArrayInit(); break;
+ case 6: return emitForLoop(); break;
+ case 7: return emitDoLoop(); break;
+ case 8: return emitIfStmt(); break;
+ case 9: return emitSwitch(); break;
+ case 10: return emitTryCatchFinally(); break;
+ case 11: return emitNopCall(); break;
+ default: return emitAssignment(); break;
}
}
@@ -1109,6 +1207,11 @@
fputs(" }\n", out_);
}
+ // Emit a static void method.
+ void emitStaticNopMethod() {
+ fputs(" public static void nop() {}\n", out_);
+ }
+
// Emit program header. Emit command line options in the comments.
void emitHeader() {
fputs("\n/**\n * AOSP JFuzz Tester.\n", out_);
@@ -1133,6 +1236,7 @@
emitArrayDecl();
emitTestConstructor();
emitTestMethod();
+ emitStaticNopMethod();
emitMainMethod();
indentation_ -= 2;
fputs("}\n\n", out_);
@@ -1167,6 +1271,7 @@
const uint32_t fuzz_stmt_length_;
const uint32_t fuzz_if_nest_;
const uint32_t fuzz_loop_nest_;
+ const uint32_t fuzz_try_nest_;
// Return and array setup.
const Type return_type_;
@@ -1182,6 +1287,7 @@
uint32_t loop_nest_;
uint32_t switch_nest_;
uint32_t do_nest_;
+ uint32_t try_nest_;
uint32_t boolean_local_;
uint32_t int_local_;
uint32_t long_local_;
@@ -1203,6 +1309,7 @@
uint32_t stmt_length = 8;
uint32_t if_nest = 2;
uint32_t loop_nest = 3;
+ uint32_t try_nest = 2;
// Parse options.
while (1) {
@@ -1226,6 +1333,9 @@
case 'n':
loop_nest = strtoul(optarg, nullptr, 0);
break;
+ case 't':
+ try_nest = strtoul(optarg, nullptr, 0);
+ break;
case 'v':
fprintf(stderr, "jfuzz version %s\n", VERSION);
return 0;
@@ -1234,7 +1344,7 @@
fprintf(stderr,
"usage: %s [-s seed] "
"[-d expr-depth] [-l stmt-length] "
- "[-i if-nest] [-n loop-nest] [-v] [-h]\n",
+ "[-i if-nest] [-n loop-nest] [-t try-nest] [-v] [-h]\n",
argv[0]);
return 1;
}
@@ -1244,7 +1354,7 @@
srand(seed);
// Generate fuzzed program.
- JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest);
+ JFuzz fuzz(stdout, seed, expr_depth, stmt_length, if_nest, loop_nest, try_nest);
fuzz.emitProgram();
return 0;
}