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;
 }