Merge "Add implicit null checks for the optimizing compiler"
diff --git a/Android.mk b/Android.mk
index 370ea02..76c3aa5 100644
--- a/Android.mk
+++ b/Android.mk
@@ -105,6 +105,7 @@
 include $(art_path)/patchoat/Android.mk
 include $(art_path)/dalvikvm/Android.mk
 include $(art_path)/tools/Android.mk
+include $(art_path)/tools/dexfuzz/Android.mk
 include $(art_path)/sigchainlib/Android.mk
 
 
diff --git a/compiler/dex/bb_optimizations.cc b/compiler/dex/bb_optimizations.cc
index e535813..11a7e44 100644
--- a/compiler/dex/bb_optimizations.cc
+++ b/compiler/dex/bb_optimizations.cc
@@ -51,4 +51,32 @@
   return false;
 }
 
+/*
+ * MethodUseCount pass implementation start.
+ */
+bool MethodUseCount::Gate(const PassDataHolder* data) const {
+  DCHECK(data != nullptr);
+  CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
+  DCHECK(c_unit != nullptr);
+  // First initialize the data.
+  c_unit->mir_graph->InitializeMethodUses();
+
+  // Now check if the pass is to be ignored.
+  bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
+
+  return res;
+}
+
+bool MethodUseCount::Worker(PassDataHolder* data) const {
+  DCHECK(data != nullptr);
+  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+  CompilationUnit* c_unit = pass_me_data_holder->c_unit;
+  DCHECK(c_unit != nullptr);
+  BasicBlock* bb = pass_me_data_holder->bb;
+  DCHECK(bb != nullptr);
+  c_unit->mir_graph->CountUses(bb);
+  // No need of repeating, so just return false.
+  return false;
+}
+
 }  // namespace art
diff --git a/compiler/dex/bb_optimizations.h b/compiler/dex/bb_optimizations.h
index b07a415..aac2644 100644
--- a/compiler/dex/bb_optimizations.h
+++ b/compiler/dex/bb_optimizations.h
@@ -171,27 +171,6 @@
   }
 };
 
-/**
- * @class TypeInference
- * @brief Type inference pass.
- */
-class TypeInference : public PassME {
- public:
-  TypeInference()
-    : PassME("TypeInference", kRepeatingPreOrderDFSTraversal, "4_post_type_cfg") {
-  }
-
-  bool Worker(PassDataHolder* data) const {
-    DCHECK(data != nullptr);
-    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
-    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
-    DCHECK(c_unit != nullptr);
-    BasicBlock* bb = pass_me_data_holder->bb;
-    DCHECK(bb != nullptr);
-    return c_unit->mir_graph->InferTypes(bb);
-  }
-};
-
 class ClassInitCheckElimination : public PassME {
  public:
   ClassInitCheckElimination()
@@ -279,6 +258,48 @@
 };
 
 /**
+ * @class ConstantPropagation
+ * @brief Perform a constant propagation pass.
+ */
+class ConstantPropagation : public PassME {
+ public:
+  ConstantPropagation() : PassME("ConstantPropagation") {
+  }
+
+  void Start(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    c_unit->mir_graph->InitializeConstantPropagation();
+  }
+
+  bool Worker(PassDataHolder* data) const {
+    DCHECK(data != nullptr);
+    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    DCHECK(c_unit != nullptr);
+    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+    DCHECK(bb != nullptr);
+    c_unit->mir_graph->DoConstantPropagation(bb);
+    // No need of repeating, so just return false.
+    return false;
+  }
+};
+
+/**
+ * @class MethodUseCount
+ * @brief Count the register uses of the method
+ */
+class MethodUseCount : public PassME {
+ public:
+  MethodUseCount() : PassME("UseCount") {
+  }
+
+  bool Worker(PassDataHolder* data) const;
+
+  bool Gate(const PassDataHolder* data) const;
+};
+
+/**
  * @class BasicBlock Optimizations
  * @brief Any simple BasicBlock optimization can be put here.
  */
diff --git a/compiler/dex/mir_optimization.cc b/compiler/dex/mir_optimization.cc
index 15b8341..ebbd28f 100644
--- a/compiler/dex/mir_optimization.cc
+++ b/compiler/dex/mir_optimization.cc
@@ -35,6 +35,7 @@
 void MIRGraph::SetConstant(int32_t ssa_reg, int32_t value) {
   is_constant_v_->SetBit(ssa_reg);
   constant_values_[ssa_reg] = value;
+  reg_location_[ssa_reg].is_const = true;
 }
 
 void MIRGraph::SetConstantWide(int32_t ssa_reg, int64_t value) {
@@ -42,6 +43,8 @@
   is_constant_v_->SetBit(ssa_reg + 1);
   constant_values_[ssa_reg] = Low32Bits(value);
   constant_values_[ssa_reg + 1] = High32Bits(value);
+  reg_location_[ssa_reg].is_const = true;
+  reg_location_[ssa_reg + 1].is_const = true;
 }
 
 void MIRGraph::DoConstantPropagation(BasicBlock* bb) {
diff --git a/compiler/dex/pass_driver_me_opts.cc b/compiler/dex/pass_driver_me_opts.cc
index c476b2a..6bb94c3 100644
--- a/compiler/dex/pass_driver_me_opts.cc
+++ b/compiler/dex/pass_driver_me_opts.cc
@@ -43,8 +43,9 @@
   GetPassInstance<NullCheckElimination>(),
   GetPassInstance<BBCombine>(),
   GetPassInstance<CodeLayout>(),
-  GetPassInstance<TypeInference>(),
   GetPassInstance<GlobalValueNumberingPass>(),
+  GetPassInstance<ConstantPropagation>(),
+  GetPassInstance<MethodUseCount>(),
   GetPassInstance<BBOptimizations>(),
   GetPassInstance<SuspendCheckElimination>(),
 };
diff --git a/compiler/dex/pass_driver_me_post_opt.cc b/compiler/dex/pass_driver_me_post_opt.cc
index 9b56c0d..5e2140d 100644
--- a/compiler/dex/pass_driver_me_post_opt.cc
+++ b/compiler/dex/pass_driver_me_post_opt.cc
@@ -40,9 +40,8 @@
     GetPassInstance<CreatePhiNodes>(),
     GetPassInstance<SSAConversion>(),
     GetPassInstance<PhiNodeOperands>(),
-    GetPassInstance<ConstantPropagation>(),
     GetPassInstance<PerformInitRegLocations>(),
-    GetPassInstance<MethodUseCount>(),
+    GetPassInstance<TypeInference>(),
     GetPassInstance<FinishSSATransformation>(),
 };
 
diff --git a/compiler/dex/post_opt_passes.cc b/compiler/dex/post_opt_passes.cc
index 675dbcf..92078b4 100644
--- a/compiler/dex/post_opt_passes.cc
+++ b/compiler/dex/post_opt_passes.cc
@@ -20,35 +20,6 @@
 
 namespace art {
 
-/*
- * MethodUseCount pass implementation start.
- */
-bool MethodUseCount::Gate(const PassDataHolder* data) const {
-  DCHECK(data != nullptr);
-  CompilationUnit* c_unit = down_cast<const PassMEDataHolder*>(data)->c_unit;
-  DCHECK(c_unit != nullptr);
-  // First initialize the data.
-  c_unit->mir_graph->InitializeMethodUses();
-
-  // Now check if the pass is to be ignored.
-  bool res = ((c_unit->disable_opt & (1 << kPromoteRegs)) == 0);
-
-  return res;
-}
-
-bool MethodUseCount::Worker(PassDataHolder* data) const {
-  DCHECK(data != nullptr);
-  PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
-  CompilationUnit* c_unit = pass_me_data_holder->c_unit;
-  DCHECK(c_unit != nullptr);
-  BasicBlock* bb = pass_me_data_holder->bb;
-  DCHECK(bb != nullptr);
-  c_unit->mir_graph->CountUses(bb);
-  // No need of repeating, so just return false.
-  return false;
-}
-
-
 bool ClearPhiInstructions::Worker(PassDataHolder* data) const {
   DCHECK(data != nullptr);
   PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
diff --git a/compiler/dex/post_opt_passes.h b/compiler/dex/post_opt_passes.h
index 964355b..55ae874 100644
--- a/compiler/dex/post_opt_passes.h
+++ b/compiler/dex/post_opt_passes.h
@@ -63,20 +63,6 @@
 };
 
 /**
- * @class MethodUseCount
- * @brief Count the register uses of the method
- */
-class MethodUseCount : public PassME {
- public:
-  MethodUseCount() : PassME("UseCount") {
-  }
-
-  bool Worker(PassDataHolder* data) const;
-
-  bool Gate(const PassDataHolder* data) const;
-};
-
-/**
  * @class ClearPhiInformation
  * @brief Clear the PHI nodes from the CFG.
  */
@@ -274,30 +260,22 @@
 };
 
 /**
- * @class ConstantPropagation
- * @brief Perform a constant propagation pass.
+ * @class TypeInference
+ * @brief Type inference pass.
  */
-class ConstantPropagation : public PassMEMirSsaRep {
+class TypeInference : public PassMEMirSsaRep {
  public:
-  ConstantPropagation() : PassMEMirSsaRep("ConstantPropagation") {
+  TypeInference() : PassMEMirSsaRep("TypeInference", kRepeatingPreOrderDFSTraversal) {
   }
 
   bool Worker(PassDataHolder* data) const {
     DCHECK(data != nullptr);
-    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
+    PassMEDataHolder* pass_me_data_holder = down_cast<PassMEDataHolder*>(data);
+    CompilationUnit* c_unit = pass_me_data_holder->c_unit;
     DCHECK(c_unit != nullptr);
-    BasicBlock* bb = down_cast<PassMEDataHolder*>(data)->bb;
+    BasicBlock* bb = pass_me_data_holder->bb;
     DCHECK(bb != nullptr);
-    c_unit->mir_graph->DoConstantPropagation(bb);
-    // No need of repeating, so just return false.
-    return false;
-  }
-
-  void Start(PassDataHolder* data) const {
-    DCHECK(data != nullptr);
-    CompilationUnit* c_unit = down_cast<PassMEDataHolder*>(data)->c_unit;
-    DCHECK(c_unit != nullptr);
-    c_unit->mir_graph->InitializeConstantPropagation();
+    return c_unit->mir_graph->InferTypes(bb);
   }
 };
 
diff --git a/compiler/dex/quick/arm/int_arm.cc b/compiler/dex/quick/arm/int_arm.cc
index 03e0e92..eb15611 100644
--- a/compiler/dex/quick/arm/int_arm.cc
+++ b/compiler/dex/quick/arm/int_arm.cc
@@ -573,6 +573,7 @@
     // GenArithOpIntLit will directly generate exception-throwing code, and multiply-by-zero will
     // have been optimized away earlier.
     op->op = kOpInvalid;
+    op->shift = 0;
     return true;
   }
 
diff --git a/compiler/dex/vreg_analysis.cc b/compiler/dex/vreg_analysis.cc
index a541c7d..62c4089 100644
--- a/compiler/dex/vreg_analysis.cc
+++ b/compiler/dex/vreg_analysis.cc
@@ -442,7 +442,7 @@
   for (int i = 0; i < GetNumSSARegs(); i++) {
     loc[i] = fresh_loc;
     loc[i].s_reg_low = i;
-    loc[i].is_const = is_constant_v_->IsBitSet(i);
+    loc[i].is_const = false;  // Constants will be marked by constant propagation pass later.
     loc[i].wide = false;
   }
 
diff --git a/compiler/optimizing/code_generator.cc b/compiler/optimizing/code_generator.cc
index 08cbb20..cdfd989 100644
--- a/compiler/optimizing/code_generator.cc
+++ b/compiler/optimizing/code_generator.cc
@@ -698,11 +698,9 @@
 }
 
 void CodeGenerator::EmitParallelMoves(Location from1, Location to1, Location from2, Location to2) {
-  MoveOperands move1(from1, to1, nullptr);
-  MoveOperands move2(from2, to2, nullptr);
   HParallelMove parallel_move(GetGraph()->GetArena());
-  parallel_move.AddMove(&move1);
-  parallel_move.AddMove(&move2);
+  parallel_move.AddMove(from1, to1, nullptr);
+  parallel_move.AddMove(from2, to2, nullptr);
   GetMoveResolver()->EmitNativeCode(&parallel_move);
 }
 
diff --git a/compiler/optimizing/code_generator_arm.cc b/compiler/optimizing/code_generator_arm.cc
index 2d3fa5f..1ca1cee 100644
--- a/compiler/optimizing/code_generator_arm.cc
+++ b/compiler/optimizing/code_generator_arm.cc
@@ -49,9 +49,6 @@
 static constexpr size_t kRuntimeParameterFpuRegistersLength =
     arraysize(kRuntimeParameterFpuRegisters);
 
-static constexpr DRegister DTMP = D7;
-static constexpr SRegister STMP = S14;
-
 class InvokeRuntimeCallingConvention : public CallingConvention<Register, SRegister> {
  public:
   InvokeRuntimeCallingConvention()
@@ -477,11 +474,6 @@
   blocked_core_registers_[R10] = true;
   blocked_core_registers_[R11] = true;
 
-  // Don't allocate our temporary double register.
-  blocked_fpu_registers_[STMP] = true;
-  blocked_fpu_registers_[STMP + 1] = true;
-  DCHECK_EQ(FromLowSToD(STMP), DTMP);
-
   blocked_fpu_registers_[S16] = true;
   blocked_fpu_registers_[S17] = true;
   blocked_fpu_registers_[S18] = true;
@@ -795,7 +787,7 @@
         __ StoreToOffset(kStoreWord, IP, SP, location.GetStackIndex());
       }
     } else {
-      DCHECK(const_to_move->IsLongConstant()) << const_to_move;
+      DCHECK(const_to_move->IsLongConstant()) << const_to_move->DebugName();
       int64_t value = const_to_move->AsLongConstant()->GetValue();
       if (location.IsRegisterPair()) {
         __ LoadImmediate(location.AsRegisterPairLow<Register>(), Low32Bits(value));
@@ -3321,15 +3313,6 @@
       DCHECK(destination.IsStackSlot());
       __ StoreSToOffset(source.AsFpuRegister<SRegister>(), SP, destination.GetStackIndex());
     }
-  } else if (source.IsFpuRegisterPair()) {
-    if (destination.IsFpuRegisterPair()) {
-      __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
-               FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
-    } else {
-      DCHECK(destination.IsDoubleStackSlot()) << destination;
-      __ StoreDToOffset(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
-                        SP, destination.GetStackIndex());
-    }
   } else if (source.IsDoubleStackSlot()) {
     if (destination.IsFpuRegisterPair()) {
       __ LoadDFromOffset(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()),
@@ -3402,9 +3385,9 @@
   } else if (source.IsStackSlot() && destination.IsStackSlot()) {
     Exchange(source.GetStackIndex(), destination.GetStackIndex());
   } else if (source.IsFpuRegister() && destination.IsFpuRegister()) {
-    __ vmovs(STMP, source.AsFpuRegister<SRegister>());
+    __ vmovrs(IP, source.AsFpuRegister<SRegister>());
     __ vmovs(source.AsFpuRegister<SRegister>(), destination.AsFpuRegister<SRegister>());
-    __ vmovs(destination.AsFpuRegister<SRegister>(), STMP);
+    __ vmovsr(destination.AsFpuRegister<SRegister>(), IP);
   } else if (source.IsFpuRegister() || destination.IsFpuRegister()) {
     SRegister reg = source.IsFpuRegister() ? source.AsFpuRegister<SRegister>()
                                            : destination.AsFpuRegister<SRegister>();
@@ -3412,29 +3395,10 @@
         ? destination.GetStackIndex()
         : source.GetStackIndex();
 
-    __ vmovs(STMP, reg);
-    __ LoadSFromOffset(reg, SP, mem);
-    __ StoreSToOffset(STMP, SP, mem);
-  } else if (source.IsFpuRegisterPair() && destination.IsFpuRegisterPair()) {
-    __ vmovd(DTMP, FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()));
-    __ vmovd(FromLowSToD(source.AsFpuRegisterPairLow<SRegister>()),
-             FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()));
-    __ vmovd(FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>()), DTMP);
-  } else if (source.IsFpuRegisterPair() || destination.IsFpuRegisterPair()) {
-    DRegister reg = source.IsFpuRegisterPair()
-        ? FromLowSToD(source.AsFpuRegisterPairLow<SRegister>())
-        : FromLowSToD(destination.AsFpuRegisterPairLow<SRegister>());
-    int mem = source.IsFpuRegisterPair()
-        ? destination.GetStackIndex()
-        : source.GetStackIndex();
-
-    __ vmovd(DTMP, reg);
-    __ LoadDFromOffset(reg, SP, mem);
-    __ StoreDToOffset(DTMP, SP, mem);
+    __ vmovrs(IP, reg);
+    __ LoadFromOffset(kLoadWord, IP, SP, mem);
+    __ StoreToOffset(kStoreWord, IP, SP, mem);
   } else if (source.IsDoubleStackSlot() && destination.IsDoubleStackSlot()) {
-    // TODO: We could use DTMP and ask for a pair scratch register (float or core).
-    // This would save four instructions if two scratch registers are available, and
-    // two instructions if not.
     Exchange(source.GetStackIndex(), destination.GetStackIndex());
     Exchange(source.GetHighStackIndex(kArmWordSize), destination.GetHighStackIndex(kArmWordSize));
   } else {
diff --git a/compiler/optimizing/code_generator_x86.cc b/compiler/optimizing/code_generator_x86.cc
index 22b2aa0..9052b8f 100644
--- a/compiler/optimizing/code_generator_x86.cc
+++ b/compiler/optimizing/code_generator_x86.cc
@@ -3382,7 +3382,7 @@
       __ movl(Address(ESP, destination.GetStackIndex()), imm);
     }
   } else {
-    LOG(FATAL) << "Unimplemented";
+    LOG(FATAL) << "Unimplemented move: " << destination << " <- " << source;
   }
 }
 
diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc
index e55175f..291b14c 100644
--- a/compiler/optimizing/graph_checker.cc
+++ b/compiler/optimizing/graph_checker.cc
@@ -363,9 +363,29 @@
   }
 }
 
+void SSAChecker::VisitIf(HIf* instruction) {
+  VisitInstruction(instruction);
+  HInstruction* input = instruction->InputAt(0);
+  if (input->IsIntConstant()) {
+    int value = input->AsIntConstant()->GetValue();
+    if (value != 0 && value != 1) {
+      std::stringstream error;
+      error << "If instruction " << instruction->GetId()
+            << " has a non-boolean constant input whose value is: "
+            << value << ".";
+      errors_.push_back(error.str());
+    }
+  } else if (instruction->InputAt(0)->GetType() != Primitive::kPrimBoolean) {
+    std::stringstream error;
+    error << "If instruction " << instruction->GetId()
+          << " has a non-boolean input type: "
+          << instruction->InputAt(0)->GetType() << ".";
+    errors_.push_back(error.str());
+  }
+}
+
 void SSAChecker::VisitCondition(HCondition* op) {
   VisitInstruction(op);
-  // TODO: check inputs types, and special case the `null` check.
   if (op->GetType() != Primitive::kPrimBoolean) {
     std::stringstream error;
     error << "Condition " << op->DebugName() << " " << op->GetId()
@@ -373,6 +393,46 @@
           << op->GetType() << ".";
     errors_.push_back(error.str());
   }
+  HInstruction* lhs = op->InputAt(0);
+  HInstruction* rhs = op->InputAt(1);
+  if (lhs->GetType() == Primitive::kPrimNot) {
+    if (!op->IsEqual() && !op->IsNotEqual()) {
+      std::stringstream error;
+      error << "Condition " << op->DebugName() << " " << op->GetId()
+            << " uses an object as left-hand side input.";
+      errors_.push_back(error.str());
+    }
+    if (rhs->IsIntConstant() && rhs->AsIntConstant()->GetValue() != 0) {
+      std::stringstream error;
+      error << "Condition " << op->DebugName() << " " << op->GetId()
+            << " compares an object with a non-0 integer: "
+            << rhs->AsIntConstant()->GetValue()
+            << ".";
+      errors_.push_back(error.str());
+    }
+  } else if (rhs->GetType() == Primitive::kPrimNot) {
+    if (!op->IsEqual() && !op->IsNotEqual()) {
+      std::stringstream error;
+      error << "Condition " << op->DebugName() << " " << op->GetId()
+            << " uses an object as right-hand side input.";
+      errors_.push_back(error.str());
+    }
+    if (lhs->IsIntConstant() && lhs->AsIntConstant()->GetValue() != 0) {
+      std::stringstream error;
+      error << "Condition " << op->DebugName() << " " << op->GetId()
+            << " compares a non-0 integer with an object: "
+            << lhs->AsIntConstant()->GetValue()
+            << ".";
+      errors_.push_back(error.str());
+    }
+  } else if (PrimitiveKind(lhs->GetType()) != PrimitiveKind(rhs->GetType())) {
+    std::stringstream error;
+    error << "Condition " << op->DebugName() << " " << op->GetId()
+          << " has inputs of different type: "
+          << lhs->GetType() << ", and " << rhs->GetType()
+          << ".";
+    errors_.push_back(error.str());
+  }
 }
 
 void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) {
diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h
index ba60cb9..ae1557b 100644
--- a/compiler/optimizing/graph_checker.h
+++ b/compiler/optimizing/graph_checker.h
@@ -101,6 +101,7 @@
   void VisitPhi(HPhi* phi) OVERRIDE;
   void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE;
   void VisitCondition(HCondition* op) OVERRIDE;
+  void VisitIf(HIf* instruction) OVERRIDE;
 
  private:
   DISALLOW_COPY_AND_ASSIGN(SSAChecker);
diff --git a/compiler/optimizing/intrinsics_x86_64.cc b/compiler/optimizing/intrinsics_x86_64.cc
index c1f4c94..2c23945 100644
--- a/compiler/optimizing/intrinsics_x86_64.cc
+++ b/compiler/optimizing/intrinsics_x86_64.cc
@@ -116,7 +116,7 @@
     Location cc_loc = calling_convention_visitor.GetNextLocation(input->GetType());
     Location actual_loc = locations->InAt(i);
 
-    parallel_move.AddMove(new (arena) MoveOperands(actual_loc, cc_loc, nullptr));
+    parallel_move.AddMove(actual_loc, cc_loc, nullptr);
   }
 
   codegen->GetMoveResolver()->EmitNativeCode(&parallel_move);
diff --git a/compiler/optimizing/locations.h b/compiler/optimizing/locations.h
index d41b3ae..68d6059 100644
--- a/compiler/optimizing/locations.h
+++ b/compiler/optimizing/locations.h
@@ -290,18 +290,6 @@
     return value_ == other.value_;
   }
 
-  // Returns whether this location contains `other`.
-  bool Contains(Location other) const {
-    if (Equals(other)) return true;
-    if (IsRegisterPair() && other.IsRegister()) {
-      return low() == other.reg() || high() == other.reg();
-    }
-    if (IsFpuRegisterPair() && other.IsFpuRegister()) {
-      return low() == other.reg() || high() == other.reg();
-    }
-    return false;
-  }
-
   const char* DebugString() const {
     switch (GetKind()) {
       case kInvalid: return "I";
diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc
index 4133cf6..ade3138 100644
--- a/compiler/optimizing/nodes.cc
+++ b/compiler/optimizing/nodes.cc
@@ -643,7 +643,12 @@
   } else if (GetLeft()->IsLongConstant() && GetRight()->IsLongConstant()) {
     int64_t value = Evaluate(GetLeft()->AsLongConstant()->GetValue(),
                              GetRight()->AsLongConstant()->GetValue());
-    return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+    if (GetResultType() == Primitive::kPrimLong) {
+      return new(GetBlock()->GetGraph()->GetArena()) HLongConstant(value);
+    } else {
+      DCHECK(GetResultType() == Primitive::kPrimInt);
+      return new(GetBlock()->GetGraph()->GetArena()) HIntConstant(value);
+    }
   }
   return nullptr;
 }
diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h
index f2aa043..fa51f27 100644
--- a/compiler/optimizing/nodes.h
+++ b/compiler/optimizing/nodes.h
@@ -2755,7 +2755,7 @@
 
   // True if this blocks a move from the given location.
   bool Blocks(Location loc) const {
-    return !IsEliminated() && source_.Contains(loc);
+    return !IsEliminated() && source_.Equals(loc);
   }
 
   // A move is redundant if it's been eliminated, if its source and
@@ -2784,8 +2784,6 @@
   // This is only used in debug mode, to ensure we do not connect interval siblings
   // in the same parallel move.
   HInstruction* instruction_;
-
-  DISALLOW_COPY_AND_ASSIGN(MoveOperands);
 };
 
 static constexpr size_t kDefaultNumberOfMoves = 4;
@@ -2795,24 +2793,46 @@
   explicit HParallelMove(ArenaAllocator* arena)
       : HTemplateInstruction(SideEffects::None()), moves_(arena, kDefaultNumberOfMoves) {}
 
-  void AddMove(MoveOperands* move) {
-    if (kIsDebugBuild) {
-      if (move->GetInstruction() != nullptr) {
+  void AddMove(Location source, Location destination, HInstruction* instruction) {
+    DCHECK(source.IsValid());
+    DCHECK(destination.IsValid());
+    // The parallel move resolver does not handle pairs. So we decompose the
+    // pair locations into two moves.
+    if (source.IsPair() && destination.IsPair()) {
+      AddMove(source.ToLow(), destination.ToLow(), instruction);
+      AddMove(source.ToHigh(), destination.ToHigh(), nullptr);
+    } else if (source.IsPair()) {
+      DCHECK(destination.IsDoubleStackSlot());
+      AddMove(source.ToLow(), Location::StackSlot(destination.GetStackIndex()), instruction);
+      AddMove(source.ToHigh(), Location::StackSlot(destination.GetHighStackIndex(4)), nullptr);
+    } else if (destination.IsPair()) {
+      DCHECK(source.IsDoubleStackSlot());
+      AddMove(Location::StackSlot(source.GetStackIndex()), destination.ToLow(), instruction);
+      // TODO: rewrite GetHighStackIndex to not require a word size. It's supposed to
+      // always be 4.
+      static constexpr int kHighOffset = 4;
+      AddMove(Location::StackSlot(source.GetHighStackIndex(kHighOffset)),
+              destination.ToHigh(),
+              nullptr);
+    } else {
+      if (kIsDebugBuild) {
+        if (instruction != nullptr) {
+          for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
+            DCHECK_NE(moves_.Get(i).GetInstruction(), instruction)
+              << "Doing parallel moves for the same instruction.";
+          }
+        }
         for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
-          DCHECK_NE(moves_.Get(i)->GetInstruction(), move->GetInstruction())
-            << "Doing parallel moves for the same instruction.";
+          DCHECK(!destination.Equals(moves_.Get(i).GetDestination()))
+              << "Same destination for two moves in a parallel move.";
         }
       }
-      for (size_t i = 0, e = moves_.Size(); i < e; ++i) {
-        DCHECK(!move->GetDestination().Contains(moves_.Get(i)->GetDestination()))
-            << "Same destination for two moves in a parallel move.";
-      }
+      moves_.Add(MoveOperands(source, destination, instruction));
     }
-    moves_.Add(move);
   }
 
   MoveOperands* MoveOperandsAt(size_t index) const {
-    return moves_.Get(index);
+    return moves_.GetRawStorage() + index;
   }
 
   size_t NumMoves() const { return moves_.Size(); }
@@ -2820,7 +2840,7 @@
   DECLARE_INSTRUCTION(ParallelMove);
 
  private:
-  GrowableArray<MoveOperands*> moves_;
+  GrowableArray<MoveOperands> moves_;
 
   DISALLOW_COPY_AND_ASSIGN(HParallelMove);
 };
diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc
index daec0f0..1e0d65a 100644
--- a/compiler/optimizing/optimizing_compiler.cc
+++ b/compiler/optimizing/optimizing_compiler.cc
@@ -208,11 +208,12 @@
   SsaRedundantPhiElimination redundant_phi(graph);
   SsaDeadPhiElimination dead_phi(graph);
   HDeadCodeElimination dce(graph);
-  HConstantFolding fold(graph);
+  HConstantFolding fold1(graph);
   InstructionSimplifier simplify1(graph);
 
   HInliner inliner(graph, dex_compilation_unit, driver, stats);
 
+  HConstantFolding fold2(graph);
   GVNOptimization gvn(graph);
   BoundsCheckElimination bce(graph);
   InstructionSimplifier simplify2(graph);
@@ -224,9 +225,10 @@
     &dead_phi,
     &intrinsics,
     &dce,
-    &fold,
+    &fold1,
     &simplify1,
     &inliner,
+    &fold2,
     &gvn,
     &bce,
     &simplify2
diff --git a/compiler/optimizing/parallel_move_resolver.cc b/compiler/optimizing/parallel_move_resolver.cc
index b8f5070..debe466 100644
--- a/compiler/optimizing/parallel_move_resolver.cc
+++ b/compiler/optimizing/parallel_move_resolver.cc
@@ -57,6 +57,9 @@
   // unallocated, or the move was already eliminated).
   for (size_t i = 0; i < parallel_move->NumMoves(); ++i) {
     MoveOperands* move = parallel_move->MoveOperandsAt(i);
+    // The parallel move resolver algorithm does not work with register pairs.
+    DCHECK(!move->GetSource().IsPair());
+    DCHECK(!move->GetDestination().IsPair());
     if (!move->IsRedundant()) {
       moves_.Add(move);
     }
diff --git a/compiler/optimizing/parallel_move_test.cc b/compiler/optimizing/parallel_move_test.cc
index 7ab41b6..28b5697 100644
--- a/compiler/optimizing/parallel_move_test.cc
+++ b/compiler/optimizing/parallel_move_test.cc
@@ -26,20 +26,26 @@
  public:
   explicit TestParallelMoveResolver(ArenaAllocator* allocator) : ParallelMoveResolver(allocator) {}
 
+  void Dump(Location location) {
+    if (location.IsConstant()) {
+      message_ << "C";
+    } else if (location.IsPair()) {
+      message_ << location.low() << "," << location.high();
+    } else {
+      message_ << location.reg();
+    }
+  }
+
   virtual void EmitMove(size_t index) {
     MoveOperands* move = moves_.Get(index);
     if (!message_.str().empty()) {
       message_ << " ";
     }
     message_ << "(";
-    if (move->GetSource().IsConstant()) {
-      message_ << "C";
-    } else {
-      message_ << move->GetSource().reg();
-    }
-    message_ << " -> "
-             << move->GetDestination().reg()
-             << ")";
+    Dump(move->GetSource());
+    message_ << " -> ";
+    Dump(move->GetDestination());
+    message_ << ")";
   }
 
   virtual void EmitSwap(size_t index) {
@@ -47,11 +53,11 @@
     if (!message_.str().empty()) {
       message_ << " ";
     }
-    message_ << "("
-             << move->GetSource().reg()
-             << " <-> "
-             << move->GetDestination().reg()
-             << ")";
+    message_ << "(";
+    Dump(move->GetSource());
+    message_ << " <-> ";
+    Dump(move->GetDestination());
+    message_ << ")";
   }
 
   virtual void SpillScratch(int reg ATTRIBUTE_UNUSED) {}
@@ -73,10 +79,10 @@
                                         size_t number_of_moves) {
   HParallelMove* moves = new (allocator) HParallelMove(allocator);
   for (size_t i = 0; i < number_of_moves; ++i) {
-    moves->AddMove(new (allocator) MoveOperands(
+    moves->AddMove(
         Location::RegisterLocation(operands[i][0]),
         Location::RegisterLocation(operands[i][1]),
-        nullptr));
+        nullptr);
   }
   return moves;
 }
@@ -131,16 +137,66 @@
   ArenaAllocator allocator(&pool);
   TestParallelMoveResolver resolver(&allocator);
   HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
-  moves->AddMove(new (&allocator) MoveOperands(
+  moves->AddMove(
       Location::ConstantLocation(new (&allocator) HIntConstant(0)),
       Location::RegisterLocation(0),
-      nullptr));
-  moves->AddMove(new (&allocator) MoveOperands(
+      nullptr);
+  moves->AddMove(
       Location::RegisterLocation(1),
       Location::RegisterLocation(2),
-      nullptr));
+      nullptr);
   resolver.EmitNativeCode(moves);
   ASSERT_STREQ("(1 -> 2) (C -> 0)", resolver.GetMessage().c_str());
 }
 
+TEST(ParallelMoveTest, Pairs) {
+  ArenaPool pool;
+  ArenaAllocator allocator(&pool);
+
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(4),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2 -> 4) (0 -> 2) (1 -> 3)", resolver.GetMessage().c_str());
+  }
+
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(4),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2 -> 4) (0 -> 2) (1 -> 3)", resolver.GetMessage().c_str());
+  }
+
+  {
+    TestParallelMoveResolver resolver(&allocator);
+    HParallelMove* moves = new (&allocator) HParallelMove(&allocator);
+    moves->AddMove(
+        Location::RegisterPairLocation(0, 1),
+        Location::RegisterPairLocation(2, 3),
+        nullptr);
+    moves->AddMove(
+        Location::RegisterLocation(2),
+        Location::RegisterLocation(0),
+        nullptr);
+    resolver.EmitNativeCode(moves);
+    ASSERT_STREQ("(2 <-> 0) (1 -> 3)", resolver.GetMessage().c_str());
+  }
+}
+
 }  // namespace art
diff --git a/compiler/optimizing/register_allocator.cc b/compiler/optimizing/register_allocator.cc
index 93ed44e..1b42e94 100644
--- a/compiler/optimizing/register_allocator.cc
+++ b/compiler/optimizing/register_allocator.cc
@@ -1051,7 +1051,7 @@
     move = previous->AsParallelMove();
   }
   DCHECK_EQ(move->GetLifetimePosition(), user->GetLifetimePosition());
-  move->AddMove(new (allocator_) MoveOperands(source, destination, nullptr));
+  move->AddMove(source, destination, nullptr);
 }
 
 static bool IsInstructionStart(size_t position) {
@@ -1123,7 +1123,7 @@
     }
   }
   DCHECK_EQ(move->GetLifetimePosition(), position);
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::InsertParallelMoveAtExitOf(HBasicBlock* block,
@@ -1153,7 +1153,7 @@
   } else {
     move = previous->AsParallelMove();
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::InsertParallelMoveAtEntryOf(HBasicBlock* block,
@@ -1172,7 +1172,7 @@
     move->SetLifetimePosition(block->GetLifetimeStart());
     block->InsertInstructionBefore(move, first);
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::InsertMoveAfter(HInstruction* instruction,
@@ -1196,7 +1196,7 @@
     move->SetLifetimePosition(position);
     instruction->GetBlock()->InsertInstructionBefore(move, instruction->GetNext());
   }
-  move->AddMove(new (allocator_) MoveOperands(source, destination, instruction));
+  move->AddMove(source, destination, instruction);
 }
 
 void RegisterAllocator::ConnectSiblings(LiveInterval* interval) {
diff --git a/compiler/optimizing/ssa_liveness_analysis.h b/compiler/optimizing/ssa_liveness_analysis.h
index b632c4d..a123313 100644
--- a/compiler/optimizing/ssa_liveness_analysis.h
+++ b/compiler/optimizing/ssa_liveness_analysis.h
@@ -530,7 +530,7 @@
   bool SameRegisterKind(Location other) const;
 
   bool HasHighInterval() const {
-    return !IsHighInterval() && (GetParent()->high_or_low_interval_ != nullptr);
+    return IsLowInterval();
   }
 
   bool HasLowInterval() const {
diff --git a/runtime/arch/mips/quick_entrypoints_mips.S b/runtime/arch/mips/quick_entrypoints_mips.S
index 666528a..df2feb7 100644
--- a/runtime/arch/mips/quick_entrypoints_mips.S
+++ b/runtime/arch/mips/quick_entrypoints_mips.S
@@ -190,12 +190,12 @@
     .cfi_rel_offset 19, 32
     sw     $s2, 28($sp)
     .cfi_rel_offset 18, 28
-    sw     $a3, 12($sp)
-    .cfi_rel_offset 7, 12
-    sw     $a2, 8($sp)
-    .cfi_rel_offset 6, 8
-    sw     $a1, 4($sp)
-    .cfi_rel_offset 5, 4
+    sw     $a3, 24($sp)
+    .cfi_rel_offset 7, 24
+    sw     $a2, 20($sp)
+    .cfi_rel_offset 6, 20
+    sw     $a1, 16($sp)
+    .cfi_rel_offset 5, 16
     # bottom will hold Method*
 .endm
 
@@ -257,11 +257,11 @@
     .cfi_restore 19
     lw     $s2, 28($sp)
     .cfi_restore 18
-    lw     $a3, 12($sp)
+    lw     $a3, 24($sp)
     .cfi_restore 7
-    lw     $a2, 8($sp)
+    lw     $a2, 20($sp)
     .cfi_restore 6
-    lw     $a1, 4($sp)
+    lw     $a1, 16($sp)
     .cfi_restore 5
     addiu  $sp, $sp, 64           # pop frame
     .cfi_adjust_cfa_offset -64
diff --git a/runtime/base/stl_util.h b/runtime/base/stl_util.h
index ff9f40c..3c5565c 100644
--- a/runtime/base/stl_util.h
+++ b/runtime/base/stl_util.h
@@ -57,8 +57,8 @@
 // If container is NULL, this function is a no-op.
 //
 // As an alternative to calling STLDeleteElements() directly, consider
-// ElementDeleter (defined below), which ensures that your container's elements
-// are deleted when the ElementDeleter goes out of scope.
+// using a container of std::unique_ptr, which ensures that your container's
+// elements are deleted when the container goes out of scope.
 template <class T>
 void STLDeleteElements(T *container) {
   if (!container) return;
diff --git a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
index cd34d88..9947b55 100644
--- a/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
+++ b/runtime/entrypoints/quick/quick_trampoline_entrypoints.cc
@@ -132,7 +132,7 @@
   static constexpr size_t kNumQuickFprArgs = 0;  // 0 arguments passed in FPRs.
   static constexpr bool kGprFprLockstep = false;
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Fpr1Offset = 0;  // Offset of first FPR arg.
-  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 4;  // Offset of first GPR arg.
+  static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_Gpr1Offset = 16;  // Offset of first GPR arg.
   static constexpr size_t kQuickCalleeSaveFrame_RefAndArgs_LrOffset = 60;  // Offset of return address.
   static size_t GprIndexToGprOffset(uint32_t gpr_index) {
     return gpr_index * GetBytesPerGprSpillLocation(kRuntimeISA);
diff --git a/runtime/mirror/object.cc b/runtime/mirror/object.cc
index 8bccd9e..6914f38 100644
--- a/runtime/mirror/object.cc
+++ b/runtime/mirror/object.cc
@@ -39,6 +39,8 @@
 namespace art {
 namespace mirror {
 
+Atomic<uint32_t> Object::hash_code_seed(987654321U + std::time(nullptr));
+
 class CopyReferenceFieldsWithReadBarrierVisitor {
  public:
   explicit CopyReferenceFieldsWithReadBarrierVisitor(Object* dest_obj)
@@ -136,16 +138,19 @@
 }
 
 uint32_t Object::GenerateIdentityHashCode() {
-  static Atomic<uint32_t> seed(987654321U + std::time(nullptr));
   uint32_t expected_value, new_value;
   do {
-    expected_value = seed.LoadRelaxed();
+    expected_value = hash_code_seed.LoadRelaxed();
     new_value = expected_value * 1103515245 + 12345;
-  } while ((expected_value & LockWord::kHashMask) == 0 ||
-      !seed.CompareExchangeWeakRelaxed(expected_value, new_value));
+  } while (!hash_code_seed.CompareExchangeWeakRelaxed(expected_value, new_value) ||
+      (expected_value & LockWord::kHashMask) == 0);
   return expected_value & LockWord::kHashMask;
 }
 
+void Object::SetHashCodeSeed(uint32_t new_seed) {
+  hash_code_seed.StoreRelaxed(new_seed);
+}
+
 int32_t Object::IdentityHashCode() const {
   mirror::Object* current_this = const_cast<mirror::Object*>(this);
   while (true) {
diff --git a/runtime/mirror/object.h b/runtime/mirror/object.h
index 221feca..07d15b5 100644
--- a/runtime/mirror/object.h
+++ b/runtime/mirror/object.h
@@ -419,6 +419,11 @@
   void VisitReferences(const Visitor& visitor, const JavaLangRefVisitor& ref_visitor)
       NO_THREAD_SAFETY_ANALYSIS;
 
+  // Used by object_test.
+  static void SetHashCodeSeed(uint32_t new_seed);
+  // Generate an identity hash code. Public for object test.
+  static uint32_t GenerateIdentityHashCode();
+
  protected:
   // Accessors for non-Java type fields
   template<class T, VerifyObjectFlags kVerifyFlags = kDefaultVerifyFlags, bool kIsVolatile = false>
@@ -471,9 +476,6 @@
     }
   }
 
-  // Generate an identity hash code.
-  static uint32_t GenerateIdentityHashCode();
-
   // A utility function that copies an object in a read barrier and
   // write barrier-aware way. This is internally used by Clone() and
   // Class::CopyOf().
@@ -481,6 +483,8 @@
                             size_t num_bytes)
       SHARED_LOCKS_REQUIRED(Locks::mutator_lock_);
 
+  static Atomic<uint32_t> hash_code_seed;
+
   // The Class representing the type of the object.
   HeapReference<Class> klass_;
   // Monitor and hash code information.
diff --git a/runtime/mirror/object_test.cc b/runtime/mirror/object_test.cc
index f9c00ce..fb42d28 100644
--- a/runtime/mirror/object_test.cc
+++ b/runtime/mirror/object_test.cc
@@ -731,5 +731,14 @@
   // TODO: test that interfaces trump superclasses.
 }
 
+TEST_F(ObjectTest, IdentityHashCode) {
+  // Regression test for b/19046417 which had an infinite loop if the
+  // (seed & LockWord::kHashMask) == 0. seed 0 triggered the infinite loop since we did the check
+  // before the CAS which resulted in the same seed the next loop iteration.
+  mirror::Object::SetHashCodeSeed(0);
+  int32_t hash_code = mirror::Object::GenerateIdentityHashCode();
+  EXPECT_NE(hash_code, 0);
+}
+
 }  // namespace mirror
 }  // namespace art
diff --git a/test/440-stmp/expected.txt b/test/440-stmp/expected.txt
new file mode 100644
index 0000000..e995b05
--- /dev/null
+++ b/test/440-stmp/expected.txt
@@ -0,0 +1 @@
+-118.0
diff --git a/test/440-stmp/info.txt b/test/440-stmp/info.txt
new file mode 100644
index 0000000..c4a7bf1
--- /dev/null
+++ b/test/440-stmp/info.txt
@@ -0,0 +1,3 @@
+Regression test for optimizing, that used to consider
+a S/D register a temp, while it conflicted with the
+hard-float calling convention.
diff --git a/test/440-stmp/src/Main.java b/test/440-stmp/src/Main.java
new file mode 100644
index 0000000..2dd10f8
--- /dev/null
+++ b/test/440-stmp/src/Main.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+class Main {
+  public static void main(String[] args) {
+    new Main().bar();
+  }
+
+  public void bar() {
+    // Use up all available D registers on ARM.
+    baz(a, b, c, d, e, f, g, h, i, j, k, l, m, n, o);
+  }
+
+  public static void baz(float a, float b, float c, float d, float e, float f, float g,
+                         float h, float i, float j, float k, float l, float m, float n, float o) {
+    System.out.println(a - b - c - d - e - f - g - h - i - j - k - l - m - n - o);
+  }
+
+  float a = 1.0f;
+  float b = 2.0f;
+  float c = 3.0f;
+  float d = 4.0f;
+  float e = 5.0f;
+  float f = 6.0f;
+  float g = 7.0f;
+  float h = 8.0f;
+  float i = 9.0f;
+  float j = 10.0f;
+  float k = 11.0f;
+  float l = 12.0f;
+  float m = 13.0f;
+  float n = 14.0f;
+  float o = 15.0f;
+}
diff --git a/test/441-checker-inliner/expected.txt b/test/441-checker-inliner/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/441-checker-inliner/expected.txt
diff --git a/test/441-checker-inliner/info.txt b/test/441-checker-inliner/info.txt
new file mode 100644
index 0000000..66a3270
--- /dev/null
+++ b/test/441-checker-inliner/info.txt
@@ -0,0 +1 @@
+Tests inlining in the optimizing compiler.
diff --git a/compiler/optimizing/test/Inliner.java b/test/441-checker-inliner/src/Main.java
similarity index 72%
rename from compiler/optimizing/test/Inliner.java
rename to test/441-checker-inliner/src/Main.java
index 54cce62..631b140 100644
--- a/compiler/optimizing/test/Inliner.java
+++ b/test/441-checker-inliner/src/Main.java
@@ -14,14 +14,14 @@
 * limitations under the License.
 */
 
-public class Inliner {
+public class Main {
 
-  // CHECK-START: void Inliner.InlineVoid() inliner (before)
+  // CHECK-START: void Main.InlineVoid() inliner (before)
   // CHECK-DAG:     [[Const42:i\d+]] IntConstant 42
   // CHECK-DAG:                      InvokeStaticOrDirect
   // CHECK-DAG:                      InvokeStaticOrDirect [ [[Const42]] ]
 
-  // CHECK-START: void Inliner.InlineVoid() inliner (after)
+  // CHECK-START: void Main.InlineVoid() inliner (after)
   // CHECK-NOT:                      InvokeStaticOrDirect
 
   public static void InlineVoid() {
@@ -29,12 +29,12 @@
     returnVoidWithOneParameter(42);
   }
 
-  // CHECK-START: int Inliner.InlineParameter(int) inliner (before)
+  // CHECK-START: int Main.InlineParameter(int) inliner (before)
   // CHECK-DAG:     [[Param:i\d+]]  ParameterValue
   // CHECK-DAG:     [[Result:i\d+]] InvokeStaticOrDirect [ [[Param]] ]
   // CHECK-DAG:                     Return [ [[Result]] ]
 
-  // CHECK-START: int Inliner.InlineParameter(int) inliner (after)
+  // CHECK-START: int Main.InlineParameter(int) inliner (after)
   // CHECK-DAG:     [[Param:i\d+]]  ParameterValue
   // CHECK-DAG:                     Return [ [[Param]] ]
 
@@ -42,12 +42,12 @@
     return returnParameter(a);
   }
 
-  // CHECK-START: long Inliner.InlineWideParameter(long) inliner (before)
+  // CHECK-START: long Main.InlineWideParameter(long) inliner (before)
   // CHECK-DAG:     [[Param:j\d+]]  ParameterValue
   // CHECK-DAG:     [[Result:j\d+]] InvokeStaticOrDirect [ [[Param]] ]
   // CHECK-DAG:                     Return [ [[Result]] ]
 
-  // CHECK-START: long Inliner.InlineWideParameter(long) inliner (after)
+  // CHECK-START: long Main.InlineWideParameter(long) inliner (after)
   // CHECK-DAG:     [[Param:j\d+]]  ParameterValue
   // CHECK-DAG:                     Return [ [[Param]] ]
 
@@ -55,12 +55,12 @@
     return returnWideParameter(a);
   }
 
-  // CHECK-START: java.lang.Object Inliner.InlineReferenceParameter(java.lang.Object) inliner (before)
+  // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (before)
   // CHECK-DAG:     [[Param:l\d+]]  ParameterValue
   // CHECK-DAG:     [[Result:l\d+]] InvokeStaticOrDirect [ [[Param]] ]
   // CHECK-DAG:                     Return [ [[Result]] ]
 
-  // CHECK-START: java.lang.Object Inliner.InlineReferenceParameter(java.lang.Object) inliner (after)
+  // CHECK-START: java.lang.Object Main.InlineReferenceParameter(java.lang.Object) inliner (after)
   // CHECK-DAG:     [[Param:l\d+]]  ParameterValue
   // CHECK-DAG:                     Return [ [[Param]] ]
 
@@ -68,11 +68,11 @@
     return returnReferenceParameter(o);
   }
 
-  // CHECK-START: int Inliner.InlineInt() inliner (before)
+  // CHECK-START: int Main.InlineInt() inliner (before)
   // CHECK-DAG:     [[Result:i\d+]] InvokeStaticOrDirect
   // CHECK-DAG:                     Return [ [[Result]] ]
 
-  // CHECK-START: int Inliner.InlineInt() inliner (after)
+  // CHECK-START: int Main.InlineInt() inliner (after)
   // CHECK-DAG:     [[Const4:i\d+]] IntConstant 4
   // CHECK-DAG:                     Return [ [[Const4]] ]
 
@@ -80,11 +80,11 @@
     return returnInt();
   }
 
-  // CHECK-START: long Inliner.InlineWide() inliner (before)
+  // CHECK-START: long Main.InlineWide() inliner (before)
   // CHECK-DAG:     [[Result:j\d+]] InvokeStaticOrDirect
   // CHECK-DAG:                     Return [ [[Result]] ]
 
-  // CHECK-START: long Inliner.InlineWide() inliner (after)
+  // CHECK-START: long Main.InlineWide() inliner (after)
   // CHECK-DAG:     [[Const8:j\d+]] LongConstant 8
   // CHECK-DAG:                     Return [ [[Const8]] ]
 
@@ -92,13 +92,13 @@
     return returnWide();
   }
 
-  // CHECK-START: int Inliner.InlineAdd() inliner (before)
+  // CHECK-START: int Main.InlineAdd() inliner (before)
   // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
   // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
   // CHECK-DAG:     [[Result:i\d+]] InvokeStaticOrDirect
   // CHECK-DAG:                     Return [ [[Result]] ]
 
-  // CHECK-START: int Inliner.InlineAdd() inliner (after)
+  // CHECK-START: int Main.InlineAdd() inliner (after)
   // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
   // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
   // CHECK-DAG:     [[Add:i\d+]]    Add [ [[Const3]] [[Const5]] ]
@@ -108,25 +108,25 @@
     return returnAdd(3, 5);
   }
 
-  // CHECK-START: int Inliner.InlineFieldAccess() inliner (before)
+  // CHECK-START: int Main.InlineFieldAccess() inliner (before)
   // CHECK-DAG:     [[After:i\d+]]  InvokeStaticOrDirect
   // CHECK-DAG:                     Return [ [[After]] ]
 
-  // CHECK-START: int Inliner.InlineFieldAccess() inliner (after)
+  // CHECK-START: int Main.InlineFieldAccess() inliner (after)
   // CHECK-DAG:     [[Const1:i\d+]] IntConstant 1
   // CHECK-DAG:     [[Before:i\d+]] StaticFieldGet
   // CHECK-DAG:     [[After:i\d+]]  Add [ [[Before]] [[Const1]] ]
   // CHECK-DAG:                     StaticFieldSet [ {{l\d+}} [[After]] ]
   // CHECK-DAG:                     Return [ [[After]] ]
 
-  // CHECK-START: int Inliner.InlineFieldAccess() inliner (after)
+  // CHECK-START: int Main.InlineFieldAccess() inliner (after)
   // CHECK-NOT:                     InvokeStaticOrDirect
 
   public static int InlineFieldAccess() {
     return incCounter();
   }
 
-  // CHECK-START: int Inliner.InlineWithControlFlow(boolean) inliner (before)
+  // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (before)
   // CHECK-DAG:     [[Const1:i\d+]] IntConstant 1
   // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
   // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
@@ -135,7 +135,7 @@
   // CHECK-DAG:     [[Phi:i\d+]]    Phi [ [[Add]] [[Sub]] ]
   // CHECK-DAG:                     Return [ [[Phi]] ]
 
-  // CHECK-START: int Inliner.InlineWithControlFlow(boolean) inliner (after)
+  // CHECK-START: int Main.InlineWithControlFlow(boolean) inliner (after)
   // CHECK-DAG:     [[Const1:i\d+]] IntConstant 1
   // CHECK-DAG:     [[Const3:i\d+]] IntConstant 3
   // CHECK-DAG:     [[Const5:i\d+]] IntConstant 5
@@ -199,4 +199,44 @@
   private static int incCounter() {
     return ++counter;
   }
+
+  public static void main(String[] args) {
+    InlineVoid();
+
+    if (InlineInt() != 4) {
+      throw new Error();
+    }
+
+    if (InlineWide() != 8L) {
+      throw new Error();
+    }
+
+    if (InlineParameter(42) != 42) {
+      throw new Error();
+    }
+
+    if (InlineWideParameter(0x100000001L) != 0x100000001L) {
+      throw new Error();
+    }
+
+    if (InlineReferenceParameter(Main.class) != Main.class) {
+      throw new Error();
+    }
+
+    if (InlineAdd() != 8) {
+      throw new Error();
+    }
+
+    if (InlineFieldAccess() != 43 || InlineFieldAccess() != 44) {
+      throw new Error();
+    }
+
+    if (InlineWithControlFlow(true) != 4) {
+      throw new Error();
+    }
+
+    if (InlineWithControlFlow(false) != 2) {
+      throw new Error();
+    }
+  }
 }
diff --git a/test/442-checker-constant-folding/expected.txt b/test/442-checker-constant-folding/expected.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/test/442-checker-constant-folding/expected.txt
diff --git a/test/442-checker-constant-folding/info.txt b/test/442-checker-constant-folding/info.txt
new file mode 100644
index 0000000..5073972
--- /dev/null
+++ b/test/442-checker-constant-folding/info.txt
@@ -0,0 +1 @@
+Tests constant folding in the optimizing compiler.
diff --git a/compiler/optimizing/test/ConstantFolding.java b/test/442-checker-constant-folding/src/Main.java
similarity index 68%
rename from compiler/optimizing/test/ConstantFolding.java
rename to test/442-checker-constant-folding/src/Main.java
index d08006b..de2c5c7 100644
--- a/compiler/optimizing/test/ConstantFolding.java
+++ b/test/442-checker-constant-folding/src/Main.java
@@ -14,19 +14,19 @@
 * limitations under the License.
 */
 
-public class ConstantFolding {
+public class Main {
 
   /**
    * Tiny three-register program exercising int constant folding
    * on negation.
    */
 
-  // CHECK-START: int ConstantFolding.IntNegation() constant_folding (before)
+  // CHECK-START: int Main.IntNegation() constant_folding (before)
   // CHECK-DAG:     [[Const42:i\d+]]  IntConstant 42
   // CHECK-DAG:     [[Neg:i\d+]]      Neg [ [[Const42]] ]
   // CHECK-DAG:                       Return [ [[Neg]] ]
 
-  // CHECK-START: int ConstantFolding.IntNegation() constant_folding (after)
+  // CHECK-START: int Main.IntNegation() constant_folding (after)
   // CHECK-DAG:     [[ConstN42:i\d+]] IntConstant -42
   // CHECK-DAG:                       Return [ [[ConstN42]] ]
 
@@ -42,13 +42,13 @@
    * on addition.
    */
 
-  // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (before)
+  // CHECK-START: int Main.IntAddition1() constant_folding (before)
   // CHECK-DAG:     [[Const1:i\d+]]  IntConstant 1
   // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
   // CHECK-DAG:     [[Add:i\d+]]     Add [ [[Const1]] [[Const2]] ]
   // CHECK-DAG:                      Return [ [[Add]] ]
 
-  // CHECK-START: int ConstantFolding.IntAddition1() constant_folding (after)
+  // CHECK-START: int Main.IntAddition1() constant_folding (after)
   // CHECK-DAG:     [[Const3:i\d+]]  IntConstant 3
   // CHECK-DAG:                      Return [ [[Const3]] ]
 
@@ -65,7 +65,7 @@
   * on addition.
   */
 
-  // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (before)
+  // CHECK-START: int Main.IntAddition2() constant_folding (before)
   // CHECK-DAG:     [[Const1:i\d+]]  IntConstant 1
   // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
   // CHECK-DAG:     [[Const5:i\d+]]  IntConstant 5
@@ -75,7 +75,7 @@
   // CHECK-DAG:     [[Add3:i\d+]]    Add [ [[Add1]] [[Add2]] ]
   // CHECK-DAG:                      Return [ [[Add3]] ]
 
-  // CHECK-START: int ConstantFolding.IntAddition2() constant_folding (after)
+  // CHECK-START: int Main.IntAddition2() constant_folding (after)
   // CHECK-DAG:     [[Const14:i\d+]] IntConstant 14
   // CHECK-DAG:                      Return [ [[Const14]] ]
 
@@ -96,19 +96,19 @@
    * on subtraction.
    */
 
-  // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (before)
-  // CHECK-DAG:     [[Const5:i\d+]]  IntConstant 5
+  // CHECK-START: int Main.IntSubtraction() constant_folding (before)
+  // CHECK-DAG:     [[Const6:i\d+]]  IntConstant 6
   // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
-  // CHECK-DAG:     [[Sub:i\d+]]     Sub [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Sub:i\d+]]     Sub [ [[Const6]] [[Const2]] ]
   // CHECK-DAG:                      Return [ [[Sub]] ]
 
-  // CHECK-START: int ConstantFolding.IntSubtraction() constant_folding (after)
-  // CHECK-DAG:     [[Const3:i\d+]]  IntConstant 3
-  // CHECK-DAG:                      Return [ [[Const3]] ]
+  // CHECK-START: int Main.IntSubtraction() constant_folding (after)
+  // CHECK-DAG:     [[Const4:i\d+]]  IntConstant 4
+  // CHECK-DAG:                      Return [ [[Const4]] ]
 
   public static int IntSubtraction() {
     int a, b, c;
-    a = 5;
+    a = 6;
     b = 2;
     c = a - b;
     return c;
@@ -119,13 +119,13 @@
    * on addition.
    */
 
-  // CHECK-START: long ConstantFolding.LongAddition() constant_folding (before)
+  // CHECK-START: long Main.LongAddition() constant_folding (before)
   // CHECK-DAG:     [[Const1:j\d+]]  LongConstant 1
   // CHECK-DAG:     [[Const2:j\d+]]  LongConstant 2
   // CHECK-DAG:     [[Add:j\d+]]     Add [ [[Const1]] [[Const2]] ]
   // CHECK-DAG:                      Return [ [[Add]] ]
 
-  // CHECK-START: long ConstantFolding.LongAddition() constant_folding (after)
+  // CHECK-START: long Main.LongAddition() constant_folding (after)
   // CHECK-DAG:     [[Const3:j\d+]]  LongConstant 3
   // CHECK-DAG:                      Return [ [[Const3]] ]
 
@@ -142,19 +142,19 @@
    * on subtraction.
    */
 
-  // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (before)
-  // CHECK-DAG:     [[Const5:j\d+]]  LongConstant 5
+  // CHECK-START: long Main.LongSubtraction() constant_folding (before)
+  // CHECK-DAG:     [[Const6:j\d+]]  LongConstant 6
   // CHECK-DAG:     [[Const2:j\d+]]  LongConstant 2
-  // CHECK-DAG:     [[Sub:j\d+]]     Sub [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Sub:j\d+]]     Sub [ [[Const6]] [[Const2]] ]
   // CHECK-DAG:                      Return [ [[Sub]] ]
 
-  // CHECK-START: long ConstantFolding.LongSubtraction() constant_folding (after)
-  // CHECK-DAG:     [[Const3:j\d+]]  LongConstant 3
-  // CHECK-DAG:                      Return [ [[Const3]] ]
+  // CHECK-START: long Main.LongSubtraction() constant_folding (after)
+  // CHECK-DAG:     [[Const4:j\d+]]  LongConstant 4
+  // CHECK-DAG:                      Return [ [[Const4]] ]
 
   public static long LongSubtraction() {
     long a, b, c;
-    a = 5L;
+    a = 6L;
     b = 2L;
     c = a - b;
     return c;
@@ -164,19 +164,19 @@
    * Three-register program with a constant (static) condition.
    */
 
-  // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (before)
-  // CHECK-DAG:     [[Const5:i\d+]]  IntConstant 5
+  // CHECK-START: int Main.StaticCondition() constant_folding (before)
+  // CHECK-DAG:     [[Const7:i\d+]]  IntConstant 7
   // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
-  // CHECK-DAG:     [[Cond:z\d+]]    GreaterThanOrEqual [ [[Const5]] [[Const2]] ]
+  // CHECK-DAG:     [[Cond:z\d+]]    GreaterThanOrEqual [ [[Const7]] [[Const2]] ]
   // CHECK-DAG:                      If [ [[Cond]] ]
 
-  // CHECK-START: int ConstantFolding.StaticCondition() constant_folding (after)
+  // CHECK-START: int Main.StaticCondition() constant_folding (after)
   // CHECK-DAG:     [[Const1:i\d+]]  IntConstant 1
   // CHECK-DAG:                      If [ [[Const1]] ]
 
   public static int StaticCondition() {
     int a, b, c;
-    a = 5;
+    a = 7;
     b = 2;
     if (a < b)
       c = a + b;
@@ -194,7 +194,7 @@
    * (forward) post-order traversal of the the dominator tree.
    */
 
-  // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (before)
+  // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (before)
   // CHECK-DAG:     [[Const2:i\d+]]  IntConstant 2
   // CHECK-DAG:     [[Const5:i\d+]]  IntConstant 5
   // CHECK-DAG:     [[Add:i\d+]]     Add [ [[Const5]] [[Const2]] ]
@@ -202,7 +202,7 @@
   // CHECK-DAG:     [[Phi:i\d+]]     Phi [ [[Add]] [[Sub]] ]
   // CHECK-DAG:                      Return [ [[Phi]] ]
 
-  // CHECK-START: int ConstantFolding.JumpsAndConditionals(boolean) constant_folding (after)
+  // CHECK-START: int Main.JumpsAndConditionals(boolean) constant_folding (after)
   // CHECK-DAG:     [[Const3:i\d+]]  IntConstant 3
   // CHECK-DAG:     [[Const7:i\d+]]  IntConstant 7
   // CHECK-DAG:     [[Phi:i\d+]]     Phi [ [[Const7]] [[Const3]] ]
@@ -218,4 +218,42 @@
       c = a - b;
     return c;
   }
+
+  public static void main(String[] args) {
+    if (IntNegation() != -42) {
+      throw new Error();
+    }
+
+    if (IntAddition1() != 3) {
+      throw new Error();
+    }
+
+    if (IntAddition2() != 14) {
+      throw new Error();
+    }
+
+    if (IntSubtraction() != 4) {
+      throw new Error();
+    }
+
+    if (LongAddition() != 3L) {
+      throw new Error();
+    }
+
+    if (LongSubtraction() != 4L) {
+      throw new Error();
+    }
+
+    if (StaticCondition() != 5) {
+      throw new Error();
+    }
+
+    if (JumpsAndConditionals(true) != 7) {
+      throw new Error();
+    }
+
+    if (JumpsAndConditionals(false) != 3) {
+      throw new Error();
+    }
+  }
 }
diff --git a/test/Android.run-test.mk b/test/Android.run-test.mk
index 04c590e..bd9941d 100644
--- a/test/Android.run-test.mk
+++ b/test/Android.run-test.mk
@@ -303,6 +303,20 @@
 
 TEST_ART_BROKEN_DEFAULT_RUN_TESTS :=
 
+# Tests known to be broken for the optimizing compiler on 32-bit targets due to
+# inability to allocate registers for methods with long values.
+TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS := \
+  441-checker-inliner \
+  442-checker-constant-folding \
+
+ifneq (,$(filter optimizing,$(COMPILER_TYPES)))
+  ART_TEST_KNOWN_BROKEN += $(call all-run-test-names,$(TARGET_TYPES),$(RUN_TYPES),$(PREBUILD_TYPES), \
+      optimizing,$(RELOCATE_TYPES),$(TRACE_TYPES),$(GC_TYPES),$(JNI_TYPES), \
+      $(IMAGE_TYPES),$(PICTEST_TYPES),$(TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS),32)
+endif
+
+TEST_ART_BROKEN_OPTIMIZING_32_RUN_TESTS :=
+
 # Known broken tests for the arm64 optimizing compiler backend.
 TEST_ART_BROKEN_OPTIMIZING_ARM64_RUN_TESTS :=
 
diff --git a/test/etc/default-build b/test/etc/default-build
index 6731ad3..58c9564 100755
--- a/test/etc/default-build
+++ b/test/etc/default-build
@@ -17,6 +17,22 @@
 # Stop if something fails.
 set -e
 
+DX_FLAGS=""
+
+while true; do
+  if [ "x$1" = "x--dx-option" ]; then
+    shift
+    option="$1"
+    DX_FLAGS="${DX_FLAGS} $option"
+    shift
+  elif expr "x$1" : "x--" >/dev/null 2>&1; then
+    echo "unknown $0 option: $1" 1>&2
+    exit 1
+  else
+    break
+  fi
+done
+
 if [ -e classes.dex ]; then
   zip $TEST_NAME.jar classes.dex
   exit 0
@@ -30,7 +46,8 @@
 fi
 
 if [ ${NEED_DEX} = "true" ]; then
-  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex --dump-width=1000 classes
+  ${DX} -JXmx256m --debug --dex --dump-to=classes.lst --output=classes.dex \
+    --dump-width=1000 ${DX_FLAGS} classes
 fi
 
 if [ -d smali ]; then
@@ -43,7 +60,8 @@
   mkdir classes-ex
   ${JAVAC} -d classes-ex -cp classes `find src-ex -name '*.java'`
   if [ ${NEED_DEX} = "true" ]; then
-    ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex --dump-width=1000 classes-ex
+    ${DX} -JXmx256m --debug --dex --dump-to=classes-ex.lst --output=classes-ex.dex \
+      --dump-width=1000 ${DX_FLAGS} classes-ex
 
     # quick shuffle so that the stored name is "classes.dex"
     mv classes.dex classes-1.dex
diff --git a/test/run-test b/test/run-test
index 2802b75..8ef3e3e 100755
--- a/test/run-test
+++ b/test/run-test
@@ -39,6 +39,7 @@
 else
   tmp_dir="${TMPDIR}/$USER/${test_dir}"
 fi
+checker="${progdir}/../tools/checker.py"
 
 export JAVA="java"
 export JAVAC="javac -g"
@@ -74,8 +75,10 @@
 check_cmd="check"
 output="output.txt"
 build_output="build-output.txt"
+cfg_output="cfg-output.txt"
 lib="libartd.so"
 run_args="--quiet"
+build_args=""
 
 prebuild_mode="yes"
 target_mode="yes"
@@ -503,6 +506,21 @@
 
 export TEST_NAME=`basename ${test_dir}`
 
+# Tests named '<number>-checker-*' will also have their CFGs verified with
+# Checker when compiled with Optimizing on host.
+if [[ "$TEST_NAME" =~ ^[0-9]+-checker- ]]; then
+  # Build Checker DEX files without dx's optimizations so the input to dex2oat
+  # better resembles the Java source. We always build the DEX the same way, even
+  # if Checker is not invoked and the test only runs the program.
+  build_args="${build_args} --dx-option --no-optimize"
+
+  if [ "$runtime" = "art" -a "$image_suffix" = "-optimizing" -a "$target_mode" = "no" ]; then
+    run_checker="yes"
+    run_args="${run_args} -Xcompiler-option --dump-cfg=$tmp_dir/$cfg_output \
+                          -Xcompiler-option -j1"
+  fi
+fi
+
 # To cause tests to fail fast, limit the file sizes created by dx, dex2oat and ART output to 2MB.
 file_size_limit=2048
 if echo "$test_dir" | grep 089; then
@@ -518,7 +536,7 @@
 good_build="yes"
 good_run="yes"
 if [ "$dev_mode" = "yes" ]; then
-    "./${build}" 2>&1
+    "./${build}" $build_args 2>&1
     build_exit="$?"
     echo "build exit status: $build_exit" 1>&2
     if [ "$build_exit" = '0' ]; then
@@ -531,11 +549,14 @@
         fi
     fi
 elif [ "$update_mode" = "yes" ]; then
-    "./${build}" >"$build_output" 2>&1
+    "./${build}" $build_args >"$build_output" 2>&1
     build_exit="$?"
     if [ "$build_exit" = '0' ]; then
         echo "${test_dir}: running..." 1>&2
         "./${run}" $run_args "$@" >"$output" 2>&1
+        if [ "$run_checker" = "yes" ]; then
+          "$checker" -q "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+        fi
         sed -e 's/[[:cntrl:]]$//g' < "$output" >"${td_expected}"
         good="yes"
     else
@@ -544,7 +565,7 @@
     fi
 elif [ "$build_only" = "yes" ]; then
     good="yes"
-    "./${build}" >"$build_output" 2>&1
+    "./${build}" $build_args >"$build_output" 2>&1
     build_exit="$?"
     if [ "$build_exit" '!=' '0' ]; then
         cp "$build_output" "$output"
@@ -559,7 +580,7 @@
     find $tmp_dir -mindepth 1  ! -regex ".*/\(.*jar\|$output\|$expected\)" | xargs rm -rf
     exit 0
 else
-    "./${build}" >"$build_output" 2>&1
+    "./${build}" $build_args >"$build_output" 2>&1
     build_exit="$?"
     if [ "$build_exit" = '0' ]; then
         echo "${test_dir}: running..." 1>&2
@@ -568,6 +589,15 @@
         if [ "$run_exit" != "0" ]; then
             echo "run exit status: $run_exit" 1>&2
             good_run="no"
+        elif [ "$run_checker" = "yes" ]; then
+            "$checker" -q "$cfg_output" "$tmp_dir" >> "$output" 2>&1
+            checker_exit="$?"
+            if [ "$checker_exit" != "0" ]; then
+                echo "checker exit status: $checker_exit" 1>&2
+                good_run="no"
+            else
+                good_run="yes"
+            fi
         else
             good_run="yes"
         fi
diff --git a/tools/checker.py b/tools/checker.py
index 24784ce..55f015e 100755
--- a/tools/checker.py
+++ b/tools/checker.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python2
 #
 # Copyright (C) 2014 The Android Open Source Project
 #
@@ -71,6 +71,7 @@
 #   constant folding returns an integer constant with value either 11 or 22.
 #
 
+from __future__ import print_function
 import argparse
 import os
 import re
@@ -700,8 +701,6 @@
                       help="print the contents of an output group")
   parser.add_argument("-q", "--quiet", action="store_true",
                       help="print only errors")
-  parser.add_argument("--no-clean", dest="no_clean", action="store_true",
-                      help="don't clean up generated files")
   return parser.parse_args()
 
 
@@ -725,6 +724,9 @@
     Logger.fail("Group \"" + groupName + "\" not found in the output")
 
 
+# Returns a list of files to scan for check annotations in the given path. Path
+# to a file is returned as a single-element list, directories are recursively
+# traversed and all '.java' files returned.
 def FindCheckFiles(path):
   if not path:
     Logger.fail("No source path provided")
@@ -753,19 +755,13 @@
 
 if __name__ == "__main__":
   args = ParseArguments()
+
   if args.quiet:
     Logger.Verbosity = Logger.Level.Error
 
-  tempFolder = tempfile.mkdtemp()
-  try:
-    if args.list_groups:
-      ListGroups(args.tested_file)
-    elif args.dump_group:
-      DumpGroup(args.tested_file, args.dump_group)
-    else:
-      RunChecks(args.check_prefix, args.source_path, args.tested_file)
-  finally:
-    if args.no_clean:
-      print("Files left in %s" % tempFolder)
-    else:
-      shutil.rmtree(tempFolder)
+  if args.list_groups:
+    ListGroups(args.tested_file)
+  elif args.dump_group:
+    DumpGroup(args.tested_file, args.dump_group)
+  else:
+    RunChecks(args.check_prefix, args.source_path, args.tested_file)
diff --git a/tools/checker_test.py b/tools/checker_test.py
index 1466b93..18152b5 100755
--- a/tools/checker_test.py
+++ b/tools/checker_test.py
@@ -1,4 +1,4 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python2
 #
 # Copyright (C) 2014 The Android Open Source Project
 #
@@ -359,6 +359,8 @@
 
 class TestOutputFile_Parse(unittest.TestCase):
   def __parsesTo(self, string, expected):
+    if isinstance(string, str):
+      string = unicode(string)
     outputStream = io.StringIO(string)
     return self.assertEqual(checker.OutputFile(outputStream).groups, expected)
 
@@ -421,6 +423,8 @@
 
 class TestCheckFile_Parse(unittest.TestCase):
   def __parsesTo(self, string, expected):
+    if isinstance(string, str):
+      string = unicode(string)
     checkStream = io.StringIO(string)
     return self.assertEqual(checker.CheckFile("CHECK", checkStream).groups, expected)
 
diff --git a/tools/dexfuzz/Android.mk b/tools/dexfuzz/Android.mk
new file mode 100644
index 0000000..d8f5582
--- /dev/null
+++ b/tools/dexfuzz/Android.mk
@@ -0,0 +1,37 @@
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+# --- dexfuzz.jar ----------------
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+LOCAL_JAR_MANIFEST := manifest.txt
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE := dexfuzz
+include $(BUILD_HOST_JAVA_LIBRARY)
+
+# --- dexfuzz script ----------------
+include $(CLEAR_VARS)
+LOCAL_IS_HOST_MODULE := true
+LOCAL_MODULE_TAGS := optional
+LOCAL_MODULE_CLASS := EXECUTABLES
+LOCAL_MODULE := dexfuzz
+include $(BUILD_SYSTEM)/base_rules.mk
+$(LOCAL_BUILT_MODULE): $(LOCAL_PATH)/dexfuzz $(ACP)
+	@echo "Copy: $(PRIVATE_MODULE) ($@)"
+	$(copy-file-to-new-target)
+	$(hide) chmod 755 $@
diff --git a/tools/dexfuzz/README b/tools/dexfuzz/README
new file mode 100644
index 0000000..c4795f2
--- /dev/null
+++ b/tools/dexfuzz/README
@@ -0,0 +1,130 @@
+DexFuzz
+=======
+
+DexFuzz is primarily a tool for fuzzing DEX files. Fuzzing is the introduction of
+subtle changes ("mutations") to a file to produce a new test case. These test cases
+can be used to test the various modes of execution available to ART (Interpreter,
+Quick compiler, Optimizing compiler) to check for bugs in these modes of execution.
+This is done by differential testing - each test file is executed with each mode of
+execution, and any differences between the resulting outputs may be an indication of
+a bug in one of the modes.
+
+For a wider overview of DexFuzz, see:
+
+http://community.arm.com/groups/android-community/blog/2014/11/26/the-art-of-fuzz-testing
+
+In typical operation, you provide DexFuzz with a set of DEX files that are the "seeds"
+for mutation - e.g. some tests taken from the ART test suite - and point it at an
+ADB-connected Android device, and it will fuzz these seed files, and execute the
+resulting new tests on the Android device.
+
+How to run DexFuzz
+==================
+
+1. Build dexfuzz with mmm tools/dexfuzz from within art/.
+2. Make sure you have an Android device connected via ADB, that is capable of
+   having DEX files pushed to it and executed with the dalvikvm command.
+3. Make sure you're in the Android build environment!
+   (That is, . build/envsetup.sh && lunch)
+4. Create a new directory, and place some DEX files in here. These are the seed files
+   that are mutated to form new tests.
+5. Create a directory on your device that mutated test files can be pushed to and
+   executed in, using dalvikvm. For example, /data/art-test/
+6. If you currently have multiple devices connected via ADB, find out the name of
+   your device using "adb devices -l".
+7. Run this command:
+
+dexfuzz --inputs=<seeds dir> --execute --repeat=<attempts> \
+    --dump-output <combination of ISA(s) and and backend(s)>
+
+You MUST specify one of the following ISAs:
+  --arm
+  --arm64
+  --x86
+  --x86_64
+  --mips
+  --mips64
+
+And also at least two of the following backends:
+  --interpreter
+  --quick
+  --optimizing
+
+Note that if you wanted to test both ARM and ARM64 on an ARM64 device, you can use
+--allarm. Also in this case only one backend is needed, if i.e., you wanted to test
+ARM Quick Backend vs. ARM64 Quick Backend.
+
+Some legal examples:
+  --arm --quick --optimizing
+  --x86 --quick --optimizing --interpreter
+  --allarm --quick
+
+Add in --device=<device name, e.g. device:generic> if you want to specify a device.
+Add in --execute-dir=<dir on device> if you want to specify an execution directory.
+  (The default is /data/art-test/)
+
+As the fuzzer works, you'll see output like:
+
+|-----------------------------------------------------------------|
+|Iterations|VerifyFail|MutateFail|Timed Out |Successful|Divergence|
+|-----------------------------------------------------------------|
+| 48       | 37       | 4        | 0        | 6        | 1        |
+
+Iterations - number of attempts we've made to mutate DEX files.
+VerifyFail - the number of mutated files that ended up failing to verify, either
+             on the host, or the target.
+MutateFail - because mutation is a random process, and has attempt thresholds to
+             avoid attempting to mutate a file indefinitely, it is possible that
+             an attempt to mutate a file doesn't actually mutate it. This counts
+             those occurrences.
+Timed Out  - mutated files that timed out for one or more backends.
+             Current timeouts are:
+               Quick - 5 seconds
+               Optimizing - 5 seconds
+               Intepreter - 30 seconds
+              (use --short-timeouts to set all backends to 2 seconds.)
+Successful - mutated files that executed and all backends agreed on the resulting
+             output. NB: if all backends crashed with the same output, this would
+             be considered a success - proper detection of crashes is still to come.
+Divergence - mutated files that executed and some backend disagreed about the
+             resulting output. Divergent programs are run multiple times with a
+             single backend, to check if they diverge from themselves, and these are
+             not included in the count. If multiple architectures are being used
+             (ARM/ARM64), and the divergences align with different architectures,
+             these are also not included in the count.
+
+8. Check report.log for the full report, including input file and RNG seed for each
+   test program. This allows you to recreate a bad program with, e.g.:
+
+dexfuzz --input=<input file> --seed=<seed value>
+
+Check dexfuzz --help for the full list of options.
+
+NOTE: DEX files with unicode strings are not fully supported yet, and DEX files with
+JNI elements are not supported at all currently.
+
+Mutation Likelihoods
+====================
+
+Each bytecode mutation has a chance out of 100% of firing. Following is the listing
+of each mutation's probability. If you wish to easily adjust these values, copy
+these values into a file called likelihoods.txt, and run dexfuzz with
+--likelihoods=likelihoods.txt.
+
+ArithOpChanger 75
+BranchShifter 30
+CmpBiasChanger 30
+ConstantValueChanger 70
+ConversionRepeater 50
+FieldFlagChanger 40
+InstructionDeleter 40
+InstructionDuplicator 80
+InstructionSwapper 80
+NewMethodCaller 10
+NonsenseStringPrinter 10
+PoolIndexChanger 30
+RandomInstructionGenerator 30
+SwitchBranchShifter 30
+TryBlockShifter 40
+ValuePrinter 40
+VRegChanger 60
diff --git a/tools/dexfuzz/dexfuzz b/tools/dexfuzz/dexfuzz
new file mode 100755
index 0000000..cd47008
--- /dev/null
+++ b/tools/dexfuzz/dexfuzz
@@ -0,0 +1,24 @@
+#!/bin/bash
+#
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+#
+# Wrapper script for calling dexfuzz.jar.
+#
+DEBUG=
+#DEBUG="-Xdebug -Xrunjdwp:transport=dt_socket,address=127.0.0.1:8888,server=y,suspend=n -XX:+HeapDumpOnOutOfMemoryError -ea"
+
+java ${DEBUG} -jar ${ANDROID_HOST_OUT}/framework/dexfuzz.jar "$@"
diff --git a/tools/dexfuzz/manifest.txt b/tools/dexfuzz/manifest.txt
new file mode 100644
index 0000000..9e4c214
--- /dev/null
+++ b/tools/dexfuzz/manifest.txt
@@ -0,0 +1 @@
+Main-Class: dexfuzz.DexFuzz
diff --git a/tools/dexfuzz/src/dexfuzz/DexFuzz.java b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
new file mode 100644
index 0000000..2fb9663
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/DexFuzz.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import dexfuzz.fuzzers.Fuzzer;
+import dexfuzz.fuzzers.FuzzerMultipleExecute;
+import dexfuzz.fuzzers.FuzzerMultipleNoExecute;
+import dexfuzz.fuzzers.FuzzerSingleExecute;
+import dexfuzz.fuzzers.FuzzerSingleNoExecute;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.listeners.ConsoleLoggerListener;
+import dexfuzz.listeners.LogFileListener;
+import dexfuzz.listeners.MultiplexerListener;
+import dexfuzz.listeners.UniqueProgramTrackerListener;
+import dexfuzz.listeners.UpdatingConsoleListener;
+
+/**
+ * Entrypoint class for dexfuzz.
+ */
+public class DexFuzz {
+  private static int majorVersion = 1;
+  private static int minorVersion = 0;
+  private static int seedChangeVersion = 0;
+
+  /**
+   * Entrypoint to dexfuzz.
+   */
+  public static void main(String[] args) {
+    // Report the version number, which should be incremented every time something will cause
+    // the same input seed to produce a different result than before.
+    Log.always(String.format("DexFuzz v%d.%d.%d",
+        majorVersion, minorVersion, seedChangeVersion));
+    Log.always("");
+
+    if (!Options.readOptions(args)) {
+      Log.error("Failed to validate options.");
+      Options.usage();
+    }
+
+    // Create the Listener, which will listen for events and report them.
+    BaseListener listener = null;
+    if (Options.repeat > 1 && Options.execute) {
+      // Create a Listener that is responsible for multiple Listeners.
+      MultiplexerListener multipleListener = new MultiplexerListener();
+      multipleListener.setup();
+      // Add the live updating listener, but only if we're not printing out lots of logs.
+      if (!Log.likelyToLog()) {
+        multipleListener.addListener(new UpdatingConsoleListener());
+      } else {
+        // If we are dumping out lots of logs, then use the ConsoleLogger instead.
+        multipleListener.addListener(new ConsoleLoggerListener());
+      }
+      // Add the file logging listener.
+      multipleListener.addListener(new LogFileListener(Options.reportLogFile));
+      // Add the unique program tracker.
+      multipleListener.addListener(new UniqueProgramTrackerListener(Options.uniqueDatabaseFile));
+      listener = multipleListener;
+    } else {
+      // Just use the basic listener.
+      listener = new ConsoleLoggerListener();
+    }
+
+    // Create the Fuzzer that uses a particular strategy for fuzzing.
+    Fuzzer fuzzer = null;
+    if ((Options.repeat > 1) && Options.execute) {
+      fuzzer = new FuzzerMultipleExecute(listener);
+    } else if ((Options.repeat > 1) && !Options.execute) {
+      fuzzer = new FuzzerMultipleNoExecute(listener);
+    } else if ((Options.repeat == 1) && Options.execute) {
+      fuzzer = new FuzzerSingleExecute(listener);
+    } else if ((Options.repeat == 1) && !Options.execute) {
+      fuzzer = new FuzzerSingleNoExecute(listener);
+    } else {
+      Log.errorAndQuit("Invalid options provided, desired fuzzer unknown.");
+    }
+    // TODO: Implement FuzzerFindMinimalMutations.
+    // TODO: Implement FuzzerGenerational.
+
+    // Actually run the Fuzzer.
+    fuzzer.run();
+    fuzzer.printTimingInfo();
+    fuzzer.shutdown();
+
+    // Cleanup the Listener.
+    listener.shutdown();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/ExecutionResult.java b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
new file mode 100644
index 0000000..3a8c6cb
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/ExecutionResult.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import java.util.List;
+
+/**
+ * Stores the output of an executed command.
+ */
+public class ExecutionResult {
+  public List<String> output;
+  public List<String> error;
+  public int returnValue;
+
+  private String flattenedOutput;
+  private String flattenedOutputWithNewlines;
+  private String flattenedError;
+  private String flattenedErrorWithNewlines;
+  private String flattenedAll;
+
+  private static final int TIMEOUT_RETURN_VALUE = 124;
+  private static final int SIGABORT_RETURN_VALUE = 134;
+
+  /**
+   * Get only the output, with all lines concatenated together, excluding newline characters.
+   */
+  public String getFlattenedOutput() {
+    if (flattenedOutput == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : output) {
+        builder.append(line);
+      }
+      flattenedOutput = builder.toString();
+    }
+    return flattenedOutput;
+  }
+
+  /**
+   * Get only the output, with all lines concatenated together, including newline characters.
+   */
+  public String getFlattenedOutputWithNewlines() {
+    if (flattenedOutputWithNewlines == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : output) {
+        builder.append(line).append("\n");
+      }
+      flattenedOutputWithNewlines = builder.toString();
+    }
+    return flattenedOutputWithNewlines;
+  }
+
+  /**
+   * Get only the error, with all lines concatenated together, excluding newline characters.
+   */
+  public String getFlattenedError() {
+    if (flattenedError == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : error) {
+        builder.append(line);
+      }
+      flattenedError = builder.toString();
+    }
+    return flattenedError;
+  }
+
+  /**
+   * Get only the error, with all lines concatenated together, including newline characters.
+   */
+  public String getFlattenedErrorWithNewlines() {
+    if (flattenedErrorWithNewlines == null) {
+      StringBuilder builder = new StringBuilder();
+      for (String line : error) {
+        builder.append(line).append("\n");
+      }
+      flattenedErrorWithNewlines = builder.toString();
+    }
+    return flattenedErrorWithNewlines;
+  }
+
+  /**
+   * Get both the output and error, concatenated together, excluding newline characters.
+   */
+  public String getFlattenedAll() {
+    if (flattenedAll == null) {
+      flattenedAll = getFlattenedOutput() + getFlattenedError();
+    }
+    return flattenedAll;
+  }
+
+  public boolean isTimeout() {
+    return (returnValue == TIMEOUT_RETURN_VALUE);
+  }
+
+  public boolean isSigabort() {
+    return (returnValue == SIGABORT_RETURN_VALUE);
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/Log.java b/tools/dexfuzz/src/dexfuzz/Log.java
new file mode 100644
index 0000000..853550b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Log.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+/**
+ * Provides access to the logging facilities of dexfuzz.
+ */
+public class Log {
+  private static LogTag threshold = LogTag.ERROR;
+
+  // Disable the constructor for this class.
+  private Log() { }
+
+  public static enum LogTag {
+    DEBUG,
+    INFO,
+    WARN,
+    ERROR,
+    ALWAYS
+  }
+
+  public static void setLoggingLevel(LogTag tag) {
+    threshold = tag;
+  }
+
+  public static boolean likelyToLog() {
+    return (threshold.ordinal() < LogTag.ERROR.ordinal());
+  }
+
+  public static void debug(String msg) {
+    log(LogTag.DEBUG, msg);
+  }
+
+  public static void info(String msg) {
+    log(LogTag.INFO, msg);
+  }
+
+  public static void warn(String msg) {
+    log(LogTag.WARN, msg);
+  }
+
+  public static void error(String msg) {
+    log(LogTag.ERROR, msg);
+  }
+
+  public static void always(String msg) {
+    System.out.println(msg);
+  }
+
+  private static void log(LogTag tag, String msg) {
+    if (tag.ordinal() >= threshold.ordinal()) {
+      System.out.println("[" + tag.toString() + "] " + msg);
+    }
+  }
+
+  /**
+   * Reports error and then terminates the program.
+   */
+  public static void errorAndQuit(String msg) {
+    error(msg);
+    // TODO: Signal sleeping threads.
+    System.exit(1);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/MutationStats.java b/tools/dexfuzz/src/dexfuzz/MutationStats.java
new file mode 100644
index 0000000..c65b4f2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/MutationStats.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A wrapper for a dictionary tracking what mutations have been performed.
+ */
+public class MutationStats {
+
+  public static class StatNotFoundException extends RuntimeException {
+    private static final long serialVersionUID = -7038515184655168470L;
+  }
+
+  private Map<String,Long> stats;
+  private List<String> statsOrder;
+
+  public MutationStats() {
+    stats = new HashMap<String,Long>();
+    statsOrder = new ArrayList<String>();
+  }
+
+  public void incrementStat(String statName) {
+    increaseStat(statName, 1);
+  }
+
+  /**
+   * Increase the named stat by the specified amount.
+   */
+  public void increaseStat(String statName, long amt) {
+    if (!stats.containsKey(statName)) {
+      stats.put(statName, 0L);
+      statsOrder.add(statName);
+    }
+    stats.put(statName, stats.get(statName) + amt);
+  }
+
+  /**
+   * Get a string representing the collected stats - looks like a JSON dictionary.
+   */
+  public String getStatsString() {
+    StringBuilder builder = new StringBuilder();
+    builder.append("{");
+    boolean first = true;
+    for (String statName : statsOrder) {
+      if (!first) {
+        builder.append(", ");
+      } else {
+        first = false;
+      }
+      builder.append("\"").append(statName).append("\": ").append(stats.get(statName));
+    }
+    builder.append("}");
+    return builder.toString();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/Options.java b/tools/dexfuzz/src/dexfuzz/Options.java
new file mode 100644
index 0000000..1ae7b5e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Options.java
@@ -0,0 +1,434 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import dexfuzz.Log.LogTag;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Stores options for dexfuzz.
+ */
+public class Options {
+  /**
+   * Constructor has been disabled for this class, which should only be used statically.
+   */
+  private Options() { }
+
+  // KEY VALUE OPTIONS
+  public static final List<String> inputFileList = new ArrayList<String>();
+  public static String outputFile = "";
+  public static long rngSeed = -1;
+  public static boolean usingProvidedSeed = false;
+  public static int methodMutations = 3;
+  public static int minMethods = 2;
+  public static int maxMethods = 10;
+  public static final Map<String,Integer> mutationLikelihoods = new HashMap<String,Integer>();
+  public static String executeClass = "Main";
+  public static String deviceName = "";
+  public static boolean usingSpecificDevice = false;
+  public static int repeat = 1;
+  public static String executeDirectory = "/data/art-test";
+  public static String dumpMutationsFile = "mutations.dump";
+  public static String loadMutationsFile = "mutations.dump";
+  public static String reportLogFile = "report.log";
+  public static String uniqueDatabaseFile = "unique_progs.db";
+
+  // FLAG OPTIONS
+  public static boolean execute;
+  public static boolean local;
+  public static boolean noBootImage;
+  public static boolean useInterpreter;
+  public static boolean useQuick;
+  public static boolean useOptimizing;
+  public static boolean useArchArm;
+  public static boolean useArchArm64;
+  public static boolean useArchX86;
+  public static boolean useArchX86_64;
+  public static boolean useArchMips;
+  public static boolean useArchMips64;
+  public static boolean skipHostVerify;
+  public static boolean shortTimeouts;
+  public static boolean dumpOutput;
+  public static boolean dumpVerify;
+  public static boolean mutateLimit;
+  public static boolean reportUnique;
+  public static boolean skipMutation;
+  public static boolean dumpMutations;
+  public static boolean loadMutations;
+
+  /**
+   * Print out usage information about dexfuzz, and then exit.
+   */
+  public static void usage() {
+    Log.always("DexFuzz Usage:");
+    Log.always("  --input=<file>         : Seed DEX file to be fuzzed");
+    Log.always("                           (Can specify multiple times.)");
+    Log.always("  --inputs=<file>        : Directory containing DEX files to be fuzzed.");
+    Log.always("  --output=<file>        : Output DEX file to be produced");
+    Log.always("");
+    Log.always("  --execute              : Execute the resulting fuzzed program");
+    Log.always("    --local              : Execute on host (Not available yet.)");
+    Log.always("    --device=<device>    : Execute on an ADB-connected-device, where <device> is");
+    Log.always("                           the argument given to adb -s. Default execution mode.");
+    Log.always("    --execute-dir=<dir>  : Push tests to this directory to execute them.");
+    Log.always("                           (Default: /data/art-test)");
+    Log.always("    --no-boot-image      : Use this flag when boot.art is not available.");
+    Log.always("    --skip-host-verify   : When executing, skip host-verification stage");
+    Log.always("    --execute-class=<c>  : When executing, execute this class (default: Main)");
+    Log.always("");
+    Log.always("    --interpreter        : Include the Interpreter in comparisons");
+    Log.always("    --quick              : Include the Quick Compiler in comparisons");
+    Log.always("    --optimizing         : Include the Optimizing Compiler in comparisons");
+    Log.always("");
+    Log.always("    --arm                : Include ARM backends in comparisons");
+    Log.always("    --arm64              : Include ARM64 backends in comparisons");
+    Log.always("    --allarm             : Short for --arm --arm64");
+    Log.always("    --x86                : Include x86 backends in comparisons");
+    Log.always("    --x86-64             : Include x86-64 backends in comparisons");
+    Log.always("    --mips               : Include MIPS backends in comparisons");
+    Log.always("    --mips64             : Include MIPS64 backends in comparisons");
+    Log.always("");
+    Log.always("    --dump-output        : Dump outputs of executed programs");
+    Log.always("    --dump-verify        : Dump outputs of verification");
+    Log.always("    --repeat=<n>         : Fuzz N programs, executing each one.");
+    Log.always("    --short-timeouts     : Shorten timeouts (faster; use if");
+    Log.always("                           you want to focus on output divergences)");
+    Log.always("  --seed=<seed>          : RNG seed to use");
+    Log.always("  --method-mutations=<n> : Maximum number of mutations to perform on each method.");
+    Log.always("                           (Default: 3)");
+    Log.always("  --min-methods=<n>      : Minimum number of methods to mutate. (Default: 2)");
+    Log.always("  --max-methods=<n>      : Maximum number of methods to mutate. (Default: 10)");
+    Log.always("  --one-mutation         : Short for --method-mutations=1 ");
+    Log.always("                             --min-methods=1 --max-methods=1");
+    Log.always("  --likelihoods=<file>   : A file containing a table of mutation likelihoods");
+    Log.always("  --mutate-limit         : Mutate only methods whose names end with _MUTATE");
+    Log.always("  --skip-mutation        : Do not actually mutate the input, just output it");
+    Log.always("                           after parsing");
+    Log.always("");
+    Log.always("  --dump-mutations[=<file>] : Dump an editable set of mutations applied");
+    Log.always("                              to <file> (default: mutations.dump)");
+    Log.always("  --load-mutations[=<file>] : Load and apply a set of mutations");
+    Log.always("                              from <file> (default: mutations.dump)");
+    Log.always("  --log=<tag>            : Set more verbose logging level: DEBUG, INFO, WARN");
+    Log.always("  --report=<file>        : Use <file> to report results when using --repeat");
+    Log.always("                           (Default: report.log)");
+    Log.always("  --report-unique        : Print out information about unique programs generated");
+    Log.always("  --unique-db=<file>     : Use <file> store results about unique programs");
+    Log.always("                           (Default: unique_progs.db)");
+    Log.always("");
+    System.exit(0);
+  }
+
+  /**
+   * Given a flag option (one that does not feature an =), handle it
+   * accordingly. Report an error and print usage info if the flag is not
+   * recognised.
+   */
+  private static void handleFlagOption(String flag) {
+    if (flag.equals("execute")) {
+      execute = true;
+    } else if (flag.equals("local")) {
+      local = true;
+    } else if (flag.equals("no-boot-image")) {
+      noBootImage = true;
+    } else if (flag.equals("skip-host-verify")) {
+      skipHostVerify = true;
+    } else if (flag.equals("interpreter")) {
+      useInterpreter = true;
+    } else if (flag.equals("quick")) {
+      useQuick = true;
+    } else if (flag.equals("optimizing")) {
+      useOptimizing = true;
+    } else if (flag.equals("arm")) {
+      useArchArm = true;
+    } else if (flag.equals("arm64")) {
+      useArchArm64 = true;
+    } else if (flag.equals("allarm")) {
+      useArchArm = true;
+      useArchArm64 = true;
+    } else if (flag.equals("x86")) {
+      useArchX86 = true;
+    } else if (flag.equals("x86-64")) {
+      useArchX86_64 = true;
+    } else if (flag.equals("mips")) {
+      useArchMips = true;
+    } else if (flag.equals("mips64")) {
+      useArchMips64 = true;
+    } else if (flag.equals("mutate-limit")) {
+      mutateLimit = true;
+    } else if (flag.equals("report-unique")) {
+      reportUnique = true;
+    } else if (flag.equals("dump-output")) {
+      dumpOutput = true;
+    } else if (flag.equals("dump-verify")) {
+      dumpVerify = true;
+    } else if (flag.equals("short-timeouts")) {
+      shortTimeouts = true;
+    } else if (flag.equals("skip-mutation")) {
+      skipMutation = true;
+    } else if (flag.equals("dump-mutations")) {
+      dumpMutations = true;
+    } else if (flag.equals("load-mutations")) {
+      loadMutations = true;
+    } else if (flag.equals("one-mutation")) {
+      methodMutations = 1;
+      minMethods = 1;
+      maxMethods = 1;
+    } else if (flag.equals("help")) {
+      usage();
+    } else {
+      Log.error("Unrecognised flag: --" + flag);
+      usage();
+    }
+  }
+
+  /**
+   * Given a key-value option (one that features an =), handle it
+   * accordingly. Report an error and print usage info if the key is not
+   * recognised.
+   */
+  private static void handleKeyValueOption(String key, String value) {
+    if (key.equals("input")) {
+      inputFileList.add(value);
+    } else if (key.equals("inputs")) {
+      File folder = new File(value);
+      if (folder.listFiles() == null) {
+        Log.errorAndQuit("Specified argument to --inputs is not a directory!");
+      }
+      for (File file : folder.listFiles()) {
+        String inputName = value + "/" + file.getName();
+        Log.always("Adding " + inputName + " to input seed files.");
+        inputFileList.add(inputName);
+      }
+    } else if (key.equals("output")) {
+      outputFile = value;
+    } else if (key.equals("seed")) {
+      rngSeed = Long.parseLong(value);
+      usingProvidedSeed = true;
+    } else if (key.equals("method-mutations")) {
+      methodMutations = Integer.parseInt(value);
+    } else if (key.equals("min-methods")) {
+      minMethods = Integer.parseInt(value);
+    } else if (key.equals("max-methods")) {
+      maxMethods = Integer.parseInt(value);
+    } else if (key.equals("repeat")) {
+      repeat = Integer.parseInt(value);
+    } else if (key.equals("log")) {
+      Log.setLoggingLevel(LogTag.valueOf(value.toUpperCase()));
+    } else if (key.equals("likelihoods")) {
+      setupMutationLikelihoodTable(value);
+    } else if (key.equals("dump-mutations")) {
+      dumpMutations = true;
+      dumpMutationsFile = value;
+    } else if (key.equals("load-mutations")) {
+      loadMutations = true;
+      loadMutationsFile = value;
+    } else if (key.equals("report")) {
+      reportLogFile = value;
+    } else if (key.equals("unique-db")) {
+      uniqueDatabaseFile = value;
+    } else if (key.equals("execute-class")) {
+      executeClass = value;
+    } else if (key.equals("device")) {
+      deviceName = value;
+      usingSpecificDevice = true;
+    } else if (key.equals("execute-dir")) {
+      executeDirectory = value;
+    } else {
+      Log.error("Unrecognised key: --" + key);
+      usage();
+    }
+  }
+
+  private static void setupMutationLikelihoodTable(String tableFilename) {
+    try {
+      BufferedReader reader = new BufferedReader(new FileReader(tableFilename));
+      String line = reader.readLine();
+      while (line != null) {
+        line = line.replaceAll("\\s+", " ");
+        String[] entries = line.split(" ");
+        String name = entries[0].toLowerCase();
+        int likelihood = Integer.parseInt(entries[1]);
+        if (likelihood > 100) {
+          likelihood = 100;
+        }
+        if (likelihood < 0) {
+          likelihood = 0;
+        }
+        mutationLikelihoods.put(name, likelihood);
+        line = reader.readLine();
+      }
+      reader.close();
+    } catch (FileNotFoundException e) {
+      Log.error("Unable to open mutation probability table file: " + tableFilename);
+    } catch (IOException e) {
+      Log.error("Unable to read mutation probability table file: " + tableFilename);
+    }
+  }
+
+  /**
+   * Called by the DexFuzz class during program initialisation to parse
+   * the program's command line arguments.
+   * @return If options were successfully read and validated.
+   */
+  public static boolean readOptions(String[] args) {
+    for (String arg : args) {
+      if (!(arg.startsWith("--"))) {
+        Log.error("Unrecognised option: " + arg);
+        usage();
+      }
+
+      // cut off the --
+      arg = arg.substring(2);
+
+      // choose between a --X=Y option (keyvalue) and a --X option (flag)
+      if (arg.contains("=")) {
+        String[] split = arg.split("=");
+        handleKeyValueOption(split[0], split[1]);
+      } else {
+        handleFlagOption(arg);
+      }
+    }
+
+    return validateOptions();
+  }
+
+  /**
+   * Checks if the current options settings are valid, called after reading
+   * all options.
+   * @return If the options are valid or not.
+   */
+  private static boolean validateOptions() {
+    // Deal with option assumptions.
+    if (inputFileList.isEmpty()) {
+      File seedFile = new File("fuzzingseed.dex");
+      if (seedFile.exists()) {
+        Log.always("Assuming --input=fuzzingseed.dex");
+        inputFileList.add("fuzzingseed.dex");
+      } else {
+        Log.errorAndQuit("No input given, and couldn't find fuzzingseed.dex!");
+        return false;
+      }
+    }
+
+    if (outputFile.equals("")) {
+      Log.always("Assuming --output=fuzzingseed_fuzzed.dex");
+      outputFile = "fuzzingseed_fuzzed.dex";
+    }
+
+
+    if (mutationLikelihoods.isEmpty()) {
+      File likelihoodsFile = new File("likelihoods.txt");
+      if (likelihoodsFile.exists()) {
+        Log.always("Assuming --likelihoods=likelihoods.txt ");
+        setupMutationLikelihoodTable("likelihoods.txt");
+      } else {
+        Log.always("Using default likelihoods (see README for values)");
+      }
+    }
+
+    // Now check for hard failures.
+    if (repeat < 1) {
+      Log.error("--repeat must be at least 1!");
+      return false;
+    }
+    if (usingProvidedSeed && repeat > 1) {
+      Log.error("Cannot use --repeat with --seed");
+      return false;
+    }
+    if (loadMutations && dumpMutations) {
+      Log.error("Cannot both load and dump mutations");
+      return false;
+    }
+    if (repeat == 1 && inputFileList.size() > 1) {
+      Log.error("Must use --repeat if you have provided more than one input");
+      return false;
+    }
+    if (methodMutations < 0) {
+      Log.error("Cannot use --method-mutations with a negative value.");
+      return false;
+    }
+    if (minMethods < 0) {
+      Log.error("Cannot use --min-methods with a negative value.");
+      return false;
+    }
+    if (maxMethods < 0) {
+      Log.error("Cannot use --max-methods with a negative value.");
+      return false;
+    }
+    if (maxMethods < minMethods) {
+      Log.error("Cannot use --max-methods that's smaller than --min-methods");
+      return false;
+    }
+    if (local && usingSpecificDevice) {
+      Log.error("Cannot use --local and --device!");
+      return false;
+    }
+    if (execute) {
+      if (!(useArchArm
+          || useArchArm64
+          || useArchX86
+          || useArchX86_64
+          || useArchMips
+          || useArchMips64)) {
+        Log.error("No architecture to execute on was specified!");
+        return false;
+      }
+      if ((useArchArm || useArchArm64) && (useArchX86 || useArchX86_64)) {
+        Log.error("Did you mean to specify ARM and x86?");
+        return false;
+      }
+      if ((useArchArm || useArchArm64) && (useArchMips || useArchMips64)) {
+        Log.error("Did you mean to specify ARM and MIPS?");
+        return false;
+      }
+      if ((useArchX86 || useArchX86_64) && (useArchMips || useArchMips64)) {
+        Log.error("Did you mean to specify x86 and MIPS?");
+        return false;
+      }
+      int backends = 0;
+      if (useInterpreter) {
+        backends++;
+      }
+      if (useQuick) {
+        backends++;
+      }
+      if (useOptimizing) {
+        backends++;
+      }
+      if (useArchArm && useArchArm64) {
+        // Could just be comparing quick-ARM versus quick-ARM64?
+        backends++;
+      }
+      if (backends < 2) {
+        Log.error("Not enough backends specified! Try --quick --interpreter!");
+        return false;
+      }
+    }
+
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/StreamConsumer.java b/tools/dexfuzz/src/dexfuzz/StreamConsumer.java
new file mode 100644
index 0000000..cd93374
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/StreamConsumer.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import java.io.BufferedReader;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.Semaphore;
+
+/**
+ * process.waitFor() can block if its output buffers are not drained.
+ * These threads are used to keep the buffers drained, and provide the final
+ * output once the command has finished executing. Each Executor has its own
+ * output and error StreamConsumers.
+ */
+public class StreamConsumer extends Thread {
+  private List<String> output;
+  private BufferedReader reader;
+
+  private State state;
+
+  private Semaphore workToBeDone;
+  private Semaphore outputIsReady;
+
+  enum State {
+    WAITING,
+    CONSUMING,
+    SHOULD_STOP_CONSUMING,
+    FINISHED,
+    ERROR
+  }
+
+  /**
+   * Create a StreamConsumer, will be immediately ready to start consuming.
+   */
+  public StreamConsumer() {
+    output = new ArrayList<String>();
+    workToBeDone = new Semaphore(0);
+    outputIsReady = new Semaphore(0);
+
+    state = State.WAITING;
+  }
+
+  /**
+   * Executor should call this to provide its StreamConsumers with the Streams
+   * for a Process it is about to call waitFor() on.
+   */
+  public void giveStreamAndStartConsuming(InputStream stream) {
+    output.clear();
+
+    reader = new BufferedReader(new InputStreamReader(stream));
+
+    changeState(State.CONSUMING, State.WAITING);
+
+    // Tell consumer there is work to be done.
+    workToBeDone.release();
+  }
+
+  /**
+   * Executor should call this once its call to waitFor() returns.
+   */
+  public void processFinished() {
+    changeState(State.SHOULD_STOP_CONSUMING, State.CONSUMING);
+  }
+
+  /**
+   * Executor should call this to get the captured output of this StreamConsumer.
+   */
+  public List<String> getOutput() {
+
+    try {
+      // Wait until the output is ready.
+      outputIsReady.acquire();
+    } catch (InterruptedException e) {
+      Log.error("Client of StreamConsumer was interrupted while waiting for output?");
+      return null;
+    }
+
+    // Take a copy of the Strings, so when we call output.clear(), we don't
+    // clear the ExecutionResult's list.
+    List<String> copy = new ArrayList<String>(output);
+    return copy;
+  }
+
+  /**
+   * Executor should call this when we're shutting down.
+   */
+  public void shutdown() {
+    changeState(State.FINISHED, State.WAITING);
+
+    // Tell Consumer there is work to be done (it will check first if FINISHED has been set.)
+    workToBeDone.release();
+  }
+
+  private void consume() {
+    try {
+
+      if (checkState(State.SHOULD_STOP_CONSUMING)) {
+        // Caller already called processFinished() before we even started
+        // consuming. Just get what we can and finish.
+        while (reader.ready()) {
+          output.add(reader.readLine());
+        }
+      } else {
+        // Caller's process is still executing, so just loop and consume.
+        while (checkState(State.CONSUMING)) {
+          Thread.sleep(50);
+          while (reader.ready()) {
+            output.add(reader.readLine());
+          }
+        }
+      }
+
+      if (checkState(State.SHOULD_STOP_CONSUMING)) {
+        changeState(State.WAITING, State.SHOULD_STOP_CONSUMING);
+      } else {
+        Log.error("StreamConsumer stopped consuming, but was not told to?");
+        setErrorState();
+      }
+
+      reader.close();
+
+    } catch (IOException e) {
+      Log.error("StreamConsumer caught IOException while consuming");
+      setErrorState();
+    } catch (InterruptedException e) {
+      Log.error("StreamConsumer caught InterruptedException while consuming");
+      setErrorState();
+    }
+
+    // Tell client of Consumer that the output is ready.
+    outputIsReady.release();
+  }
+
+  @Override
+  public void run() {
+    while (checkState(State.WAITING)) {
+      try {
+        // Wait until there is work to be done
+        workToBeDone.acquire();
+      } catch (InterruptedException e) {
+        Log.error("StreamConsumer caught InterruptedException while waiting for work");
+        setErrorState();
+        break;
+      }
+
+      // Check first if we're done
+      if (checkState(State.FINISHED)) {
+        break;
+      }
+
+      // Make sure we're either supposed to be consuming
+      // or supposed to be finishing up consuming
+      if (!(checkState(State.CONSUMING) || checkState(State.SHOULD_STOP_CONSUMING))) {
+        Log.error("invalid state: StreamConsumer told about work, but not CONSUMING?");
+        Log.error("state was: " + getCurrentState());
+        setErrorState();
+        break;
+      }
+
+      consume();
+    }
+  }
+
+  private synchronized boolean checkState(State expectedState) {
+    return (expectedState == state);
+  }
+
+  private synchronized void changeState(State newState, State previousState) {
+    if (state != previousState) {
+      Log.error("StreamConsumer Unexpected state: " + state + ", expected " + previousState);
+      state = State.ERROR;
+    } else {
+      state = newState;
+    }
+  }
+
+  private synchronized void setErrorState() {
+    state = State.ERROR;
+  }
+
+  private synchronized State getCurrentState() {
+    return state;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/Timer.java b/tools/dexfuzz/src/dexfuzz/Timer.java
new file mode 100644
index 0000000..8979b8a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/Timer.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz;
+
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * For timing splits of program execution.
+ */
+public class Timer {
+  /**
+   * The name of the timer, the phase of the program it is intended to time.
+   */
+  private String name;
+
+  /**
+   * A point in time remembered when start() is called.
+   */
+  private long startPoint;
+
+  /**
+   * A cumulative count of how much time has elapsed. Updated each time
+   * stop() is called.
+   */
+  private long elapsedTime;
+
+  /**
+   * Initialise a new timer with the provided name.
+   */
+  public Timer(String name) {
+    this.name = name;
+    this.elapsedTime = 0L;
+  }
+
+  /**
+   * Start timing.
+   */
+  public void start() {
+    startPoint = System.currentTimeMillis();
+  }
+
+  /**
+   * Stop timing, update how much time has elapsed.
+   */
+  public void stop() {
+    long endPoint = System.currentTimeMillis();
+    elapsedTime += (endPoint - startPoint);
+  }
+
+  /**
+   * Log the elapsed time this timer has recorded.
+   */
+  public void printTime(BaseListener listener) {
+    listener.handleTiming(name, ((float)elapsedTime) / 1000.0f);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Architecture.java b/tools/dexfuzz/src/dexfuzz/executors/Architecture.java
new file mode 100644
index 0000000..5cdabc3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Architecture.java
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+/**
+ * Every Executor must specify an Architecture. It is important that when reduced
+ * to lower case, these match the ISA string that ART would produce. For example,
+ * the architecture directory used for /data/dalvik-cache/${ISA}
+ */
+public enum Architecture {
+  ARM,
+  ARM64,
+  X86,
+  X86_64,
+  MIPS,
+  MIPS64
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
new file mode 100644
index 0000000..a945283
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64InterpreterExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64InterpreterExecutor extends Executor {
+
+  public Arm64InterpreterExecutor(BaseListener listener, Device device) {
+    super("ARM64 Interpreter", 30, listener, Architecture.ARM64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xint ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..2204ba8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64OptimizingBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64OptimizingBackendExecutor extends Executor {
+
+  public Arm64OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("ARM64 Optimizing Backend", 5, listener, Architecture.ARM64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
new file mode 100644
index 0000000..55c9c7a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Arm64QuickBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Arm64QuickBackendExecutor extends Executor {
+
+  public Arm64QuickBackendExecutor(BaseListener listener, Device device) {
+    super("ARM64 Quick Backend", 5, listener, Architecture.ARM64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
new file mode 100644
index 0000000..68ce2e0
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmInterpreterExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmInterpreterExecutor extends Executor {
+
+  public ArmInterpreterExecutor(BaseListener listener, Device device) {
+    super("ARM Interpreter", 30, listener, Architecture.ARM, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xint ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
new file mode 100644
index 0000000..78cf652
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmOptimizingBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmOptimizingBackendExecutor extends Executor {
+
+  public ArmOptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("ARM Optimizing Backend", 5, listener, Architecture.ARM, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
new file mode 100644
index 0000000..8f026b2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/ArmQuickBackendExecutor.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class ArmQuickBackendExecutor extends Executor {
+
+  public ArmQuickBackendExecutor(BaseListener listener, Device device) {
+    super("ARM Quick Backend", 5, listener, Architecture.ARM, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 ");
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("-Ximage:/data/art-test/core.art -Xnorelocate ");
+    }
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/arm/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Device.java b/tools/dexfuzz/src/dexfuzz/executors/Device.java
new file mode 100644
index 0000000..8c03103
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Device.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+/**
+ * Handles execution either on a remote device, or locally.
+ * Currently only remote execution, on an ADB-connected device, is supported.
+ */
+public class Device {
+  private boolean isLocal;
+  private String deviceName;
+  private boolean usingSpecificDevice;
+  private boolean noBootImage;
+
+  /**
+   * The constructor for a local "device". Not yet supported.
+   */
+  public Device() {
+    this.isLocal = true;
+    throw new UnsupportedOperationException("Currently local execution is not supported.");
+  }
+
+  /**
+   * The constructor for an ADB connected device.
+   */
+  public Device(String deviceName, boolean noBootImage) {
+    if (!deviceName.isEmpty()) {
+      this.deviceName = deviceName;
+      this.usingSpecificDevice = true;
+    }
+    this.noBootImage = noBootImage;
+  }
+
+  /**
+   * Get the name that would be provided to adb -s to communicate specifically with this device.
+   */
+  public String getName() {
+    if (isLocal) {
+      return "LOCAL DEVICE";
+    }
+    return deviceName;
+  }
+
+  public boolean isLocal() {
+    return isLocal;
+  }
+
+  /**
+   * Certain AOSP builds of Android may not have a full boot.art built. This will be set if
+   * we use --no-boot-image, and is used by Executors when deciding the arguments for dalvikvm
+   * and dex2oat when performing host-side verification.
+   */
+  public boolean noBootImageAvailable() {
+    return noBootImage;
+  }
+
+  /**
+   * Get the command prefix for this device if we want to use adb shell.
+   */
+  public String getExecutionShellPrefix() {
+    if (isLocal) {
+      return "";
+    }
+    return getExecutionPrefixWithAdb("shell");
+  }
+
+  /**
+   * Get the command prefix for this device if we want to use adb push.
+   */
+  public String getExecutionPushPrefix() {
+    if (isLocal) {
+      return "";
+    }
+    return getExecutionPrefixWithAdb("push");
+  }
+
+  private String getExecutionPrefixWithAdb(String command) {
+    if (usingSpecificDevice) {
+      return String.format("adb -s %s %s ", deviceName, command);
+    } else {
+      return String.format("adb %s ", command);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Executor.java b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
new file mode 100644
index 0000000..7cc584d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Executor.java
@@ -0,0 +1,303 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.StreamConsumer;
+import dexfuzz.listeners.BaseListener;
+
+import java.io.IOException;
+import java.util.Map;
+
+/**
+ * Base class containing the common methods for executing a particular backend of ART.
+ */
+public abstract class Executor {
+  private String androidHostOut;
+  private String androidProductOut;
+
+  private StreamConsumer outputConsumer;
+  private StreamConsumer errorConsumer;
+
+  protected ExecutionResult executionResult;
+  protected String executeClass;
+
+  // Set by subclasses.
+  protected String name;
+  protected int timeout;
+  protected BaseListener listener;
+  protected String testLocation;
+  protected Architecture architecture;
+  protected Device device;
+
+  protected Executor(String name, int timeout, BaseListener listener, Architecture architecture,
+      Device device) {
+    executeClass = Options.executeClass;
+
+    if (Options.shortTimeouts) {
+      this.timeout = 2;
+    } else {
+      this.timeout = timeout;
+    }
+
+    this.name = name;
+    this.listener = listener;
+    this.architecture = architecture;
+    this.device = device;
+
+    this.testLocation = Options.executeDirectory;
+
+    Map<String, String> envVars = System.getenv();
+    androidProductOut = checkForEnvVar(envVars, "ANDROID_PRODUCT_OUT");
+    androidHostOut = checkForEnvVar(envVars, "ANDROID_HOST_OUT");
+
+    outputConsumer = new StreamConsumer();
+    outputConsumer.start();
+    errorConsumer = new StreamConsumer();
+    errorConsumer.start();
+
+    if (!device.isLocal()) {
+      // Check for ADB.
+      try {
+        ProcessBuilder pb = new ProcessBuilder();
+        pb.command("adb", "devices");
+        Process process = pb.start();
+        int exitValue = process.waitFor();
+        if (exitValue != 0) {
+          Log.errorAndQuit("Problem executing ADB - is it in your $PATH?");
+        }
+      } catch (IOException e) {
+        Log.errorAndQuit("IOException when executing ADB, is it working?");
+      } catch (InterruptedException e) {
+        Log.errorAndQuit("InterruptedException when executing ADB, is it working?");
+      }
+
+      // Check we can run something on ADB.
+      ExecutionResult result = executeOnDevice("true", true);
+      if (result.getFlattenedAll().contains("device not found")) {
+        Log.errorAndQuit("Couldn't connect to specified ADB device: " + device.getName());
+      }
+    }
+  }
+
+  private String checkForEnvVar(Map<String, String> envVars, String key) {
+    if (!envVars.containsKey(key)) {
+      Log.errorAndQuit("Cannot run a fuzzed program if $" + key + " is not set!");
+    }
+    return envVars.get(key);
+  }
+
+  private ExecutionResult executeCommand(String command, boolean captureOutput) {
+    ExecutionResult result = new ExecutionResult();
+
+    Log.info("Executing: " + command);
+
+    try {
+      ProcessBuilder processBuilder = new ProcessBuilder(command.split(" "));
+      processBuilder.environment().put("ANDROID_ROOT", androidHostOut);
+      Process process = processBuilder.start();
+
+      if (captureOutput) {
+        // Give the streams to the StreamConsumers.
+        outputConsumer.giveStreamAndStartConsuming(process.getInputStream());
+        errorConsumer.giveStreamAndStartConsuming(process.getErrorStream());
+      }
+
+      // Wait until the process is done - the StreamConsumers will keep the
+      // buffers drained, so this shouldn't block indefinitely.
+      // Get the return value as well.
+      result.returnValue = process.waitFor();
+
+      Log.info("Return value: " + result.returnValue);
+
+      if (captureOutput) {
+        // Tell the StreamConsumers to stop consuming, and wait for them to finish
+        // so we know we have all of the output.
+        outputConsumer.processFinished();
+        errorConsumer.processFinished();
+        result.output = outputConsumer.getOutput();
+        result.error = errorConsumer.getOutput();
+
+        // Always explicitly indicate the return code in the text output now.
+        // NB: adb shell doesn't actually return exit codes currently, but this will
+        // be useful if/when it does.
+        result.output.add("RETURN CODE: " + result.returnValue);
+      }
+
+    } catch (IOException e) {
+      Log.errorAndQuit("ExecutionResult.execute() caught an IOException");
+    } catch (InterruptedException e) {
+      Log.errorAndQuit("ExecutionResult.execute() caught an InterruptedException");
+    }
+
+    return result;
+  }
+
+  /**
+   * Called by subclass Executors in their execute() implementations.
+   */
+  protected ExecutionResult executeOnDevice(String command, boolean captureOutput) {
+    String timeoutString = "timeout " + timeout + " ";
+    return executeCommand(timeoutString + device.getExecutionShellPrefix() + command,
+        captureOutput);
+  }
+
+  private ExecutionResult pushToDevice(String command) {
+    return executeCommand(device.getExecutionPushPrefix() + command, false);
+  }
+
+  /**
+   * Call this to make sure the StreamConsumer threads are stopped.
+   */
+  public void shutdown() {
+    outputConsumer.shutdown();
+    errorConsumer.shutdown();
+  }
+
+  /**
+   * Called by the Fuzzer after each execution has finished, to clear the results.
+   */
+  public void reset() {
+    executionResult = null;
+  }
+
+  /**
+   * Called by the Fuzzer to verify the mutated program using the host-side dex2oat.
+   */
+  public boolean verifyOnHost(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dex2oat ");
+
+    // This assumes that the Architecture enum's name, when reduced to lower-case,
+    // matches what dex2oat would expect.
+    commandBuilder.append("--instruction-set=").append(architecture.toString().toLowerCase());
+    commandBuilder.append(" --instruction-set-features=default ");
+
+    // Select the correct boot image.
+    commandBuilder.append("--boot-image=").append(androidProductOut);
+    if (device.noBootImageAvailable()) {
+      commandBuilder.append("/data/art-test/core.art ");
+    } else {
+      commandBuilder.append("/system/framework/boot.art ");
+    }
+
+    commandBuilder.append("--oat-file=output.oat ");
+    commandBuilder.append("--android-root=").append(androidHostOut).append(" ");
+    commandBuilder.append("--runtime-arg -classpath ");
+    commandBuilder.append("--runtime-arg ").append(programName).append(" ");
+    commandBuilder.append("--dex-file=").append(programName).append(" ");
+    commandBuilder.append("--compiler-filter=interpret-only --runtime-arg -Xnorelocate ");
+
+    ExecutionResult verificationResult = executeCommand(commandBuilder.toString(), true);
+
+    boolean success = true;
+
+    if (verificationResult.isSigabort()) {
+      listener.handleHostVerificationSigabort(verificationResult);
+      success = false;
+    }
+
+    if (success) {
+      // Search for a keyword that indicates verification was not successful.
+      // TODO: Determine if dex2oat crashed?
+      for (String line : verificationResult.error) {
+        if (line.contains("Verification error")
+            || line.contains("Failure to verify dex file")) {
+          success = false;
+        }
+        if (Options.dumpVerify) {
+          // Strip out the start of the log lines.
+          listener.handleDumpVerify(line.replaceFirst(".*(cc|h):\\d+] ",  ""));
+        }
+      }
+    }
+
+    if (!success) {
+      listener.handleFailedHostVerification(verificationResult);
+    }
+
+    executeCommand("rm output.oat", false);
+
+    return success;
+  }
+
+  /**
+   * Called by the Fuzzer to upload the program to the target device.
+   * TODO: Check if we're executing on a local device, and don't do this?
+   */
+  public void uploadToTarget(String programName) {
+    pushToDevice(programName + " " + testLocation);
+  }
+
+  /**
+   * Executor subclasses need to override this, to construct their arguments for dalvikvm
+   * invocation correctly.
+   */
+  public abstract void execute(String programName);
+
+  /**
+   * Executor subclasses need to override this, to delete their generated OAT file correctly.
+   */
+  public abstract void deleteGeneratedOatFile(String programName);
+
+  /**
+   * Executor subclasses need to override this, to report if they need a cleaned code cache.
+   */
+  public abstract boolean needsCleanCodeCache();
+
+  /**
+   * Fuzzer.checkForArchitectureSplit() will use this determine the architecture of the Executor.
+   */
+  public Architecture getArchitecture() {
+    return architecture;
+  }
+
+  /**
+   * Used in each subclass of Executor's deleteGeneratedOatFile() method, to know what to delete.
+   */
+  protected String getOatFileName(String programName) {
+    // Converts e.g. /data/art-test/file.dex to data@art-test@file.dex
+    return (testLocation.replace("/", "@").substring(1) + "@" + programName);
+  }
+
+  /**
+   * Used by the Fuzzer to get result of execution.
+   */
+  public ExecutionResult getResult() {
+    return executionResult;
+  }
+
+  /**
+   * Because dex2oat can accept a program with soft errors on the host, and then fail after
+   * performing hard verification on the target, we need to check if the Executor detected
+   * a target verification failure, before doing anything else with the resulting output.
+   * Used by the Fuzzer.
+   */
+  public boolean verifyOnTarget() {
+    // TODO: Remove this once host-verification can be forced to always fail?
+    if (executionResult.getFlattenedOutput().contains("VerifyError")) {
+      return false;
+    }
+    return true;
+  }
+
+  public String getName() {
+    return name;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
new file mode 100644
index 0000000..9f27b5e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64InterpreterExecutor.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64InterpreterExecutor extends Executor {
+
+  public Mips64InterpreterExecutor(BaseListener listener, Device device) {
+    super("MIPS64 Interpreter", 30, listener, Architecture.MIPS64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..b30240d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64OptimizingBackendExecutor extends Executor {
+
+  public Mips64OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS64 Optimizing Backend", 5, listener, Architecture.MIPS64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
new file mode 100644
index 0000000..42ccd1e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/Mips64QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class Mips64QuickBackendExecutor extends Executor {
+
+  public Mips64QuickBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS64 Quick Backend", 5, listener, Architecture.MIPS64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
new file mode 100644
index 0000000..524eaa9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsInterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsInterpreterExecutor extends Executor {
+
+  public MipsInterpreterExecutor(BaseListener listener, Device device) {
+    super("MIPS Interpreter", 30, listener, Architecture.MIPS, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
new file mode 100644
index 0000000..fcc92c8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsOptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsOptimizingBackendExecutor extends Executor {
+
+  public MipsOptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS Optimizing Backend", 5, listener, Architecture.MIPS, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
new file mode 100644
index 0000000..cb442f9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/MipsQuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class MipsQuickBackendExecutor extends Executor {
+
+  public MipsQuickBackendExecutor(BaseListener listener, Device device) {
+    super("MIPS Quick Backend", 5, listener, Architecture.MIPS, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/mips/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
new file mode 100644
index 0000000..93c14e9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86InterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86InterpreterExecutor extends Executor {
+
+  public X86InterpreterExecutor(BaseListener listener, Device device) {
+    super("x86 Interpreter", 30, listener, Architecture.X86, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
new file mode 100644
index 0000000..b27d5ca
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86OptimizingBackendExecutor extends Executor {
+
+  public X86OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("x86 Optimizing Backend", 5, listener, Architecture.X86, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
new file mode 100644
index 0000000..d8ec217
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86QuickBackendExecutor extends Executor {
+
+  public X86QuickBackendExecutor(BaseListener listener, Device device) {
+    super("x86 Quick Backend", 5, listener, Architecture.X86, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm32 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
new file mode 100644
index 0000000..7497322
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64InterpreterExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64InterpreterExecutor extends Executor {
+
+  public X86_64InterpreterExecutor(BaseListener listener, Device device) {
+    super("x86_64 Interpreter", 30, listener, Architecture.X86_64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xint ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
new file mode 100644
index 0000000..a978f73
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64OptimizingBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64OptimizingBackendExecutor extends Executor {
+
+  public X86_64OptimizingBackendExecutor(BaseListener listener, Device device) {
+    super("x86_64 Optimizing Backend", 5, listener, Architecture.X86_64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 -Xcompiler-option --compiler-backend=Optimizing ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
new file mode 100644
index 0000000..85532d8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/executors/X86_64QuickBackendExecutor.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.executors;
+
+import dexfuzz.listeners.BaseListener;
+
+public class X86_64QuickBackendExecutor extends Executor {
+
+  public X86_64QuickBackendExecutor(BaseListener listener, Device device) {
+    super("x86_64 Quick Backend", 5, listener, Architecture.X86_64, device);
+  }
+
+  @Override
+  public void execute(String programName) {
+    StringBuilder commandBuilder = new StringBuilder();
+    commandBuilder.append("dalvikvm64 ");
+    commandBuilder.append("-cp ").append(testLocation).append("/").append(programName).append(" ");
+    commandBuilder.append(executeClass);
+    executionResult = executeOnDevice(commandBuilder.toString(), true);
+  }
+
+  @Override
+  public void deleteGeneratedOatFile(String programName) {
+    String command = "rm -f /data/dalvik-cache/x86_64/" + getOatFileName(programName);
+    executeOnDevice(command, false);
+  }
+
+  @Override
+  public boolean needsCleanCodeCache() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
new file mode 100644
index 0000000..4c1acdb
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/Fuzzer.java
@@ -0,0 +1,449 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.Timer;
+import dexfuzz.executors.Architecture;
+import dexfuzz.executors.Arm64InterpreterExecutor;
+import dexfuzz.executors.Arm64OptimizingBackendExecutor;
+import dexfuzz.executors.Arm64QuickBackendExecutor;
+import dexfuzz.executors.ArmInterpreterExecutor;
+import dexfuzz.executors.ArmOptimizingBackendExecutor;
+import dexfuzz.executors.ArmQuickBackendExecutor;
+import dexfuzz.executors.Device;
+import dexfuzz.executors.Executor;
+import dexfuzz.executors.Mips64InterpreterExecutor;
+import dexfuzz.executors.Mips64OptimizingBackendExecutor;
+import dexfuzz.executors.Mips64QuickBackendExecutor;
+import dexfuzz.executors.MipsInterpreterExecutor;
+import dexfuzz.executors.MipsOptimizingBackendExecutor;
+import dexfuzz.executors.MipsQuickBackendExecutor;
+import dexfuzz.executors.X86InterpreterExecutor;
+import dexfuzz.executors.X86OptimizingBackendExecutor;
+import dexfuzz.executors.X86QuickBackendExecutor;
+import dexfuzz.executors.X86_64InterpreterExecutor;
+import dexfuzz.executors.X86_64OptimizingBackendExecutor;
+import dexfuzz.executors.X86_64QuickBackendExecutor;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Mutation;
+import dexfuzz.program.Program;
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.OffsetTracker;
+import dexfuzz.rawdex.RawDexFile;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * A particular fuzzing strategy, this class provides the common methods
+ * most fuzzing will involve, and subclasses override the run() method, to
+ * employ a particular strategy.
+ */
+public abstract class Fuzzer {
+  private List<Executor> executors;
+  private OffsetTracker offsetTracker;
+
+  /**
+   * This is the executor that we use to test for self-divergent programs.
+   */
+  private Executor goldenExecutor;
+
+  /*
+   * These two flags are set during fuzz(), and then cleared at the end of execute().
+   */
+  private boolean mutatedSuccessfully;
+  private boolean savedSuccessfully;
+
+  private Timer totalTimer = new Timer("Total Time");
+  private Timer timerDexInput = new Timer("DEX Input");
+  private Timer timerProgGen = new Timer("Program Generation");
+  private Timer timerMutation = new Timer("Mutation Time");
+  private Timer timerDexOutput = new Timer("DEX Output");
+  private Timer timerChecksumCalc = new Timer("Checksum Calculation");
+
+  protected BaseListener listener;
+
+  protected Fuzzer(BaseListener listener) {
+    totalTimer.start();
+    executors = new ArrayList<Executor>();
+    this.listener = listener;
+  }
+
+  public abstract void run();
+
+  protected abstract String getNextInputFilename();
+
+  protected abstract String getNextOutputFilename();
+
+  /**
+   * Call this after fuzzer execution to print out timing results.
+   */
+  public void printTimingInfo() {
+    totalTimer.stop();
+    timerDexInput.printTime(listener);
+    timerProgGen.printTime(listener);
+    timerMutation.printTime(listener);
+    timerDexOutput.printTime(listener);
+    timerChecksumCalc.printTime(listener);
+    totalTimer.printTime(listener);
+  }
+
+  /**
+   * Make sure this is called to correctly shutdown each Executor's StreamConsumers.
+   */
+  public void shutdown() {
+    if (executors != null) {
+      for (Executor executor : executors) {
+        executor.shutdown();
+      }
+    }
+  }
+
+  private void addExecutorsForArchitecture(Device device, Class<? extends Executor> quick,
+      Class<? extends Executor> optimizing, Class<? extends Executor> interpreter) {
+    // NB: Currently QuickBackend MUST come immediately before same arch's Interpreter.
+    // This is because intepreter execution relies on there being an OAT file already
+    // created to produce correct debug information. Otherwise we will see
+    // false-positive divergences.
+    try {
+      if (Options.useQuick) {
+        Constructor<? extends Executor> constructor =
+            quick.getConstructor(BaseListener.class, Device.class);
+        executors.add(constructor.newInstance(listener, device));
+      }
+      if (Options.useOptimizing) {
+        Constructor<? extends Executor> constructor =
+            optimizing.getConstructor(BaseListener.class, Device.class);
+        executors.add(constructor.newInstance(listener, device));
+      }
+      if (Options.useInterpreter) {
+        Constructor<? extends Executor> constructor =
+            interpreter.getConstructor(BaseListener.class, Device.class);
+        executors.add(constructor.newInstance(listener, device));
+      }
+    } catch (NoSuchMethodException e) {
+      Log.errorAndQuit("Executor doesn't have correct constructor.");
+    } catch (InstantiationException e) {
+      Log.errorAndQuit("Executor couldn't be instantiated.");
+    } catch (IllegalAccessException e) {
+      Log.errorAndQuit("Executor couldn't be accessed.");
+    } catch (IllegalArgumentException e) {
+      Log.errorAndQuit("Invalid arguments to instantiation of Executor.");
+    } catch (InvocationTargetException e) {
+      Log.errorAndQuit("Instantiation of Executor threw an Exception!");
+    }
+  }
+
+  protected void addExecutors() {
+    Device device = null;
+    if (Options.local) {
+      device = new Device();
+    } else {
+      device = new Device(Options.deviceName, Options.noBootImage);
+    }
+
+    if (Options.useArchArm64) {
+      addExecutorsForArchitecture(device, Arm64QuickBackendExecutor.class,
+          Arm64OptimizingBackendExecutor.class, Arm64InterpreterExecutor.class);
+    }
+
+    if (Options.useArchArm) {
+      addExecutorsForArchitecture(device, ArmQuickBackendExecutor.class,
+          ArmOptimizingBackendExecutor.class, ArmInterpreterExecutor.class);
+    }
+
+    if (Options.useArchX86_64) {
+      addExecutorsForArchitecture(device, X86_64QuickBackendExecutor.class,
+          X86_64OptimizingBackendExecutor.class, X86_64InterpreterExecutor.class);
+    }
+
+    if (Options.useArchX86) {
+      addExecutorsForArchitecture(device, X86QuickBackendExecutor.class,
+          X86OptimizingBackendExecutor.class, X86InterpreterExecutor.class);
+    }
+
+    if (Options.useArchMips64) {
+      addExecutorsForArchitecture(device, Mips64QuickBackendExecutor.class,
+          Mips64OptimizingBackendExecutor.class, Mips64InterpreterExecutor.class);
+    }
+
+    if (Options.useArchMips) {
+      addExecutorsForArchitecture(device, MipsQuickBackendExecutor.class,
+          MipsOptimizingBackendExecutor.class, MipsInterpreterExecutor.class);
+    }
+
+    // Add the first backend as the golden executor for self-divergence tests.
+    goldenExecutor = executors.get(0);
+  }
+
+  /**
+   * Called from each Fuzzer subclass that we can instantiate. Parses the program, fuzzes it,
+   * and then saves it, if mutation was successful. We can use --skip-mutation to bypass
+   * the mutation phase, if we wanted to verify that a test program itself works.
+   */
+  protected Program fuzz() {
+    String inputFile = getNextInputFilename();
+    Program program = loadProgram(inputFile, null);
+    if (program == null) {
+      Log.errorAndQuit("Problem loading seed file.");
+    }
+    // Mutate the program.
+    if (!Options.skipMutation) {
+      timerMutation.start();
+      program.mutateTheProgram();
+
+      mutatedSuccessfully = program.updateRawDexFile();
+      timerMutation.stop();
+      if (!mutatedSuccessfully) {
+        listener.handleMutationFail();
+      }
+    } else {
+      Log.info("Skipping mutation stage as requested.");
+      mutatedSuccessfully = true;
+    }
+    if (mutatedSuccessfully) {
+      savedSuccessfully = saveProgram(program, getNextOutputFilename());
+    }
+    return program;
+  }
+
+  protected boolean safeToExecute() {
+    return mutatedSuccessfully && savedSuccessfully;
+  }
+
+  protected void execute(Program program) {
+    if (!safeToExecute()) {
+      Log.errorAndQuit("Your Fuzzer subclass called execute() "
+          + "without checking safeToExecute()!");
+    }
+
+    String programName = getNextOutputFilename();
+    boolean verified = true;
+    if (!Options.skipHostVerify) {
+      verified = goldenExecutor.verifyOnHost(programName);
+    }
+    if (verified) {
+      boolean skipAnalysis = false;
+      boolean uploadedToTarget = false;
+      if (!Options.skipHostVerify) {
+        listener.handleSuccessfulHostVerification();
+      }
+      for (Executor executor : executors) {
+        executor.reset();
+        if (!uploadedToTarget) {
+          executor.uploadToTarget(programName);
+        } else {
+          uploadedToTarget = true;
+        }
+        if (executor.needsCleanCodeCache()) {
+          executor.deleteGeneratedOatFile(programName);
+        }
+        executor.execute(programName);
+        if (!executor.verifyOnTarget()) {
+          listener.handleFailedTargetVerification();
+          skipAnalysis = true;
+          break;
+        }
+        // Results are saved in the executors until they reset, usually at the
+        // next iteration.
+      }
+
+      if (!skipAnalysis) {
+        listener.handleSuccessfullyFuzzedFile(programName);
+        analyseResults(program, programName);
+      }
+    }
+    mutatedSuccessfully = false;
+    savedSuccessfully = false;
+  }
+
+  /**
+   * Checks if the different outputs we observed align with different architectures.
+   */
+  private boolean checkForArchitectureSplit(Map<String, List<Executor>> outputMap) {
+    if (outputMap.size() != 2) {
+      // Cannot have a two-way split if we don't have 2 kinds of output.
+      return false;
+    }
+
+    Architecture[] architectures = new Architecture[2];
+    int archIdx = 0;
+
+    // For each kind of output we saw, make sure they all
+    // came from the same architecture.
+    for (List<Executor> executorList : outputMap.values()) {
+      architectures[archIdx] = executorList.get(0).getArchitecture();
+      for (int execIdx = 1; execIdx < executorList.size(); execIdx++) {
+        if (executorList.get(execIdx).getArchitecture() != architectures[archIdx]) {
+          // Not every executor with this output shared the same architecture.
+          return false;
+        }
+      }
+      archIdx++;
+    }
+
+    // Now make sure that the two outputs we saw were different architectures.
+    if (architectures[0] == architectures[1]) {
+      return false;
+    }
+    return true;
+  }
+
+  private boolean checkGoldenExecutorForSelfDivergence(String programName) {
+    // Run golden executor 5 times, make sure it always produces
+    // the same output, otherwise report that it is self-divergent.
+
+    // TODO: Instead, produce a list of acceptable outputs, and see if the divergent
+    // outputs of the backends fall within this set of outputs.
+    String seenOutput = null;
+    for (int i = 0; i < 5; i++) {
+      goldenExecutor.reset();
+      goldenExecutor.execute(programName);
+      String output = goldenExecutor.getResult().getFlattenedOutput();
+      if (seenOutput == null) {
+        seenOutput = output;
+      } else if (!seenOutput.equals(output)) {
+        return true;
+      }
+    }
+    return false;
+  }
+
+  private void analyseResults(Program program, String programName) {
+    // Check timeouts.
+    // Construct two lists of executors, those who timed out, and those who did not.
+    // Report if we had some timeouts.
+    List<Executor> timedOut = new ArrayList<Executor>();
+    List<Executor> didNotTimeOut = new ArrayList<Executor>();
+    for (Executor executor : executors) {
+      if (executor.getResult().isTimeout()) {
+        timedOut.add(executor);
+      } else {
+        didNotTimeOut.add(executor);
+      }
+    }
+    if (!timedOut.isEmpty()) {
+      listener.handleTimeouts(timedOut, didNotTimeOut);
+      // Do not bother reporting divergence information.
+      return;
+    }
+
+    // Check divergences.
+    // Construct a map {output1: [executor that produced output1, ...], output2: [...]}
+    // If the map has more than one output, we had divergence, report it.
+    Map<String, List<Executor>> outputMap = new HashMap<String, List<Executor>>();
+    for (Executor executor : executors) {
+      String output = executor.getResult().getFlattenedOutput();
+      if (Options.dumpOutput) {
+        listener.handleDumpOutput(
+            executor.getResult().getFlattenedOutputWithNewlines(), executor);
+      }
+      if (outputMap.containsKey(output)) {
+        outputMap.get(output).add(executor);
+      } else {
+        List<Executor> newList = new ArrayList<Executor>();
+        newList.add(executor);
+        outputMap.put(output, newList);
+      }
+    }
+
+    if (outputMap.size() > 1) {
+      // Report that we had divergence.
+      listener.handleDivergences(outputMap);
+      listener.handleMutations(program.getMutations());
+      // If we found divergences, try running the "golden executor"
+      // a few times in succession, to see if the output it produces is different
+      // from run to run. If so, then we're probably executing something with either:
+      //  a) randomness
+      //  b) timing-dependent code
+      //  c) threads
+      // So we will not consider it a "true" divergence, but still useful?
+      if (checkGoldenExecutorForSelfDivergence(programName)) {
+        listener.handleSelfDivergence();
+        return;
+      }
+      // If we found divergences, try checking if the differences
+      // in outputs align with differences in architectures.
+      // For example, if we have: {Output1: [ARM, ARM], Output2: [ARM64, ARM64]}
+      if (checkForArchitectureSplit(outputMap)) {
+        listener.handleArchitectureSplit();
+      }
+    } else {
+      // No problems with execution.
+      listener.handleSuccess(outputMap);
+    }
+  }
+
+  private Program loadProgram(String inputName, List<Mutation> mutations) {
+    Program program = null;
+    try {
+      DexRandomAccessFile input = new DexRandomAccessFile(inputName, "r");
+      offsetTracker = new OffsetTracker();
+      input.setOffsetTracker(offsetTracker);
+      // Read the raw DexFile
+      RawDexFile rawDexFile = new RawDexFile();
+      timerDexInput.start();
+      rawDexFile.read(input);
+      timerDexInput.stop();
+      input.close();
+      // Create the program view.
+      timerProgGen.start();
+      program = new Program(rawDexFile, mutations, listener);
+      timerProgGen.stop();
+    } catch (FileNotFoundException e) {
+      Log.errorAndQuit("Couldn't open a file called " + inputName);
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException when trying to load a DEX test file!");
+    }
+    return program;
+  }
+
+  private boolean saveProgram(Program program, String outputName) {
+    boolean success = false;
+
+    try {
+      // Write out the results of mutation.
+      DexRandomAccessFile output = new DexRandomAccessFile(outputName, "rw");
+      output.setOffsetTracker(offsetTracker);
+      // Delete the contents of the file, in case it already existed.
+      output.setLength(0);
+      // Write out the file.
+      timerDexOutput.start();
+      program.writeRawDexFile(output);
+      timerDexOutput.stop();
+      // Recalculate the header info.
+      timerChecksumCalc.start();
+      program.updateRawDexFileHeader(output);
+      timerChecksumCalc.stop();
+      output.close();
+      success = true;
+    } catch (FileNotFoundException e) {
+      Log.errorAndQuit("Couldn't open a file called " + outputName);
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException when trying to save a DEX test file!");
+    }
+    return success;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java
new file mode 100644
index 0000000..8abaeb0
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultiple.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Superclass for fuzzing strategies that perform multiple fuzzes, and want
+ * their inputs to come from the input list in a round-robin fashion.
+ */
+public abstract class FuzzerMultiple extends Fuzzer {
+  protected int iterations;
+
+  protected FuzzerMultiple(BaseListener listener) {
+    super(listener);
+  }
+
+  @Override
+  protected String getNextInputFilename() {
+    String inputFile = Options.inputFileList.get(0);
+    if (Options.inputFileList.size() > 1) {
+      int nextIndex = iterations % Options.inputFileList.size();
+      inputFile = Options.inputFileList.get(nextIndex);
+    }
+    listener.handleFuzzingFile(inputFile);
+    return inputFile;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java
new file mode 100644
index 0000000..0cf6df7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleExecute.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Program;
+
+/**
+ * Fuzz programs multiple times, testing each.
+ */
+public class FuzzerMultipleExecute extends FuzzerMultiple {
+  public FuzzerMultipleExecute(BaseListener listener) {
+    super(listener);
+    addExecutors();
+  }
+
+  @Override
+  protected String getNextOutputFilename() {
+    // In MultipleExecute, always use the same output.
+    return Options.outputFile;
+  }
+
+  @Override
+  public void run() {
+    // TODO: Test that all seed files execute correctly before they are mutated!
+    for (iterations = 0; iterations < Options.repeat; iterations++) {
+      listener.handleIterationStarted(iterations);
+      Program program = fuzz();
+      if (safeToExecute()) {
+        execute(program);
+      }
+      listener.handleIterationFinished(iterations);
+    }
+    listener.handleSummary();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java
new file mode 100644
index 0000000..fea8788
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerMultipleNoExecute.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Fuzz programs multiple times, writing each one to a new DEX file.
+ */
+public class FuzzerMultipleNoExecute extends FuzzerMultiple {
+  public FuzzerMultipleNoExecute(BaseListener listener) {
+    super(listener);
+  }
+
+  @Override
+  protected String getNextOutputFilename() {
+    // In MultipleNoExecute, produce multiple files, each prefixed
+    // with the iteration value.
+    return String.format("%09d_%s", iterations, Options.outputFile);
+  }
+
+  @Override
+  public void run() {
+    for (iterations = 0; iterations < Options.repeat; iterations++) {
+      listener.handleIterationStarted(iterations);
+      fuzz();
+      listener.handleIterationFinished(iterations);
+    }
+    listener.handleSummary();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java
new file mode 100644
index 0000000..68b47c2
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingle.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Superclass for fuzzers that fuzz once.
+ */
+public abstract class FuzzerSingle extends Fuzzer {
+  protected FuzzerSingle(BaseListener listener) {
+    super(listener);
+  }
+
+  @Override
+  protected String getNextInputFilename() {
+    return Options.inputFileList.get(0);
+  }
+
+  protected String getNextOutputFilename() {
+    return Options.outputFile;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java
new file mode 100644
index 0000000..de0c7db
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleExecute.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.Program;
+
+/**
+ * Fuzz a DEX file once, and test it.
+ */
+public class FuzzerSingleExecute extends FuzzerSingle {
+  public FuzzerSingleExecute(BaseListener listener) {
+    super(listener);
+    addExecutors();
+  }
+
+  @Override
+  public void run() {
+    Program program = fuzz();
+    if (safeToExecute()) {
+      execute(program);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
new file mode 100644
index 0000000..6015284
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/fuzzers/FuzzerSingleNoExecute.java
@@ -0,0 +1,33 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.fuzzers;
+
+import dexfuzz.listeners.BaseListener;
+
+/**
+ * Fuzz a DEX file once, but don't test it.
+ */
+public class FuzzerSingleNoExecute extends FuzzerSingle {
+  public FuzzerSingleNoExecute(BaseListener listener) {
+    super(listener);
+  }
+
+  @Override
+  public void run() {
+    fuzz();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java b/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java
new file mode 100644
index 0000000..e33fb09
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/BaseListener.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Base class for Listeners, who are notified about certain events in dexfuzz's execution.
+ */
+public abstract class BaseListener {
+  public void setup() { }
+
+  public void shutdown() { }
+
+  public void handleSuccessfulHostVerification() { }
+
+  public void handleFailedHostVerification(ExecutionResult verificationResult) { }
+
+  public void handleFailedTargetVerification() { }
+
+  public void handleIterationStarted(int iteration) { }
+
+  public void handleIterationFinished(int iteration) { }
+
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) { }
+
+  public void handleDivergences(Map<String, List<Executor>> outputMap) { }
+
+  public void handleFuzzingFile(String inputFile) { }
+
+  public void handleSeed(long seed) { }
+
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) { }
+
+  public void handleSuccess(Map<String, List<Executor>> outputMap) { }
+
+  public void handleDumpOutput(String outputLine, Executor executor) { }
+
+  public void handleDumpVerify(String verifyLine) { }
+
+  public void handleMutationStats(String statsString) { }
+
+  public void handleTiming(String name, float elapsedTime) { }
+
+  public void handleMutationFail() { }
+
+  public void handleSummary() { }
+
+  public void handleSuccessfullyFuzzedFile(String programName) { }
+
+  public void handleSelfDivergence() { }
+
+  public void handleMessage(String msg) { }
+
+  public void handleMutations(List<Mutation> mutations) { }
+
+  public void handleArchitectureSplit() { }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java
new file mode 100644
index 0000000..1ea74d9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/ConsoleLoggerListener.java
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Logs output to the console, when not using --repeat.
+ */
+public class ConsoleLoggerListener extends BaseListener {
+  @Override
+  public void setup() {
+
+  }
+
+  @Override
+  public void shutdown() {
+
+  }
+
+  private void logToConsole(String msg) {
+    System.out.println("CONSOLE: " + msg);
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    logToConsole("Successful host verification");
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    logToConsole("Failed host verification");
+  }
+
+  @Override
+  public void handleMutations(List<Mutation> mutations) {
+    for (Mutation mutation : mutations) {
+      logToConsole("Applied mutation: " + mutation.toString());
+    }
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    logToConsole("Detected architectural split.");
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    logToConsole("Failed target verification");
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    logToConsole("Starting iteration " + iteration);
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    logToConsole("Finished iteration " + iteration);
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    logToConsole("Timed out: " + timedOut.size() + " Did not time out: " + didNotTimeOut.size());
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    logToConsole("Got divergences!");
+    int outputCount = 1;
+    for (List<Executor> executors : outputMap.values()) {
+      logToConsole("Output " + outputCount + ":");
+      for (Executor executor : executors) {
+        logToConsole("  " + executor.getName());
+      }
+      outputCount++;
+    }
+  }
+
+  @Override
+  public void handleFuzzingFile(String inputFile) {
+    logToConsole("Fuzzing: " + inputFile);
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    logToConsole("Seed: " + seed);
+  }
+
+  @Override
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+    logToConsole("Sigaborted host verification");
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    logToConsole("Program " + programName + " successfully fuzzed.");
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    logToConsole("Execution was successful");
+  }
+
+  @Override
+  public void handleDumpOutput(String outputLine, Executor executor) {
+    logToConsole(executor.getName() + " OUTPUT: " + outputLine);
+  }
+
+  @Override
+  public void handleDumpVerify(String verifyLine) {
+    logToConsole("VERIFY: " + verifyLine);
+  }
+
+  @Override
+  public void handleMutationFail() {
+    logToConsole("DEX file was not mutated");
+  }
+
+  @Override
+  public void handleMutationStats(String statsString) {
+    logToConsole("Mutations performed: " + statsString);
+  }
+
+  @Override
+  public void handleTiming(String name, float elapsedTime) {
+    logToConsole(String.format("'%s': %.3fs", name, elapsedTime));
+  }
+
+  @Override
+  public void handleSummary() {
+    logToConsole("--- SUMMARY ---");
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    logToConsole("Seen self divergence");
+  }
+
+  @Override
+  public void handleMessage(String msg) {
+    logToConsole(msg);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java b/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java
new file mode 100644
index 0000000..09ee756
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/LogFileListener.java
@@ -0,0 +1,277 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.Log;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+import dexfuzz.program.MutationSerializer;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Logs events to a file.
+ */
+public class LogFileListener extends BaseListener {
+  private BufferedWriter writer;
+  boolean ready = false;
+
+  long successfulVerification;
+  long failedVerification;
+  long failedMutation;
+  long success;
+  long timedOut;
+  long divergence;
+  long selfDivergent;
+  long architectureSplit;
+  long iterations;
+
+  private String logFile;
+
+  public LogFileListener(String logFile) {
+    this.logFile = logFile;
+  }
+
+  @Override
+  public void setup() {
+    try {
+      writer = new BufferedWriter(new FileWriter(logFile));
+      ready = true;
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void shutdown() {
+    try {
+      writer.close();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    Log.always("Full log in " + logFile);
+  }
+
+  private void write(String msg) {
+    if (!ready) {
+      return;
+    }
+    try {
+      writer.write(msg + "\n");
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    write("Host verification: SUCCESS");
+    successfulVerification++;
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    write("Host verification: FAILED");
+    failedVerification++;
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    write("Target verification: FAILED");
+    failedVerification++;
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    write("--> FUZZ " + (iteration + 1));
+    Date now = new Date(System.currentTimeMillis());
+    write("Time started: " + now.toString());
+    iterations++;
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    write("Some executors timed out.");
+    write("Timed out:");
+    for (Executor executor : timedOut) {
+      write("  " + executor.getName());
+    }
+    if (!didNotTimeOut.isEmpty()) {
+      write("Did not time out:");
+      for (Executor executor : didNotTimeOut) {
+        write("  " + executor.getName());
+      }
+    }
+    this.timedOut++;
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    write("DIVERGENCE between some executors!");
+    int outputCount = 1;
+    for (List<Executor> executors : outputMap.values()) {
+      write("Output " + outputCount + ":");
+      for (Executor executor : executors) {
+        write("  " + executor.getName());
+      }
+      outputCount++;
+    }
+    divergence++;
+
+    // You are probably interested in reading about these divergences while fuzzing
+    // is taking place, so flush the writer now.
+    try {
+      writer.flush();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleFuzzingFile(String inputFile) {
+    write("Fuzzing file '" + inputFile + "'");
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    write("Using " + seed + " for seed.");
+    // Flush the seed as well, so if anything goes wrong we can see what seed lead
+    // to the issue.
+    try {
+      writer.flush();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+    write("Host verification: SIGABORTED");
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    write("All executors agreed on result.");
+    success++;
+  }
+
+  @Override
+  public void handleDumpOutput(String outputLine, Executor executor) {
+    write(executor.getName() + " OUTPUT:");
+    write(outputLine);
+  }
+
+  @Override
+  public void handleDumpVerify(String verifyLine) {
+    write("VERIFY: " + verifyLine);
+  }
+
+  @Override
+  public void handleMutationStats(String statsString) {
+    write("Mutation Stats: " + statsString);
+  }
+
+  @Override
+  public void handleTiming(String name, float elapsedTime) {
+    write(String.format("'%s': %.3fs", name, elapsedTime));
+  }
+
+  @Override
+  public void handleMutationFail() {
+    write("Mutation process: FAILED");
+    failedMutation++;
+  }
+
+  @Override
+  public void handleSummary() {
+    write("");
+    write("---+++--- SUMMARY ---+++---");
+    write("Fuzzing attempts: " + iterations);
+    write("  Failed verification: " + failedVerification);
+    write("  Failed mutation: " + failedMutation);
+    write("  Timed out: " + timedOut);
+    write("Successful: " + success);
+    write("  Self divergent: " + selfDivergent);
+    write("  Architecture split: " + architectureSplit);
+    write("Produced divergence: " + divergence);
+
+    long truelyDivergent = divergence - (selfDivergent + architectureSplit);
+    long verified = success + timedOut + truelyDivergent;
+
+    write("");
+
+    float verifiedTotalRatio =
+        (((float) (verified)) / iterations) * 100.0f;
+    write(String.format("Percentage Verified/Total: %.3f%%", verifiedTotalRatio));
+
+    float timedOutVerifiedRatio =
+        (((float) timedOut) / (verified)) * 100.0f;
+    write(String.format("Percentage Timed Out/Verified: %.3f%%", timedOutVerifiedRatio));
+
+    float successfulVerifiedRatio =
+        (((float) success) / (verified)) * 100.0f;
+    write(String.format("Percentage Successful/Verified: %.3f%%", successfulVerifiedRatio));
+
+    float divergentVerifiedRatio =
+        (((float) truelyDivergent) / (verified)) * 100.0f;
+    write(String.format("Percentage Divergent/Verified: %.3f%%", divergentVerifiedRatio));
+
+    write("---+++--- SUMMARY ---+++---");
+    write("");
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    write("");
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    write("Successfully fuzzed file '" + programName + "'");
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    write("Golden Executor was self-divergent!");
+    selfDivergent++;
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    write("Divergent outputs align with difference in architectures.");
+    architectureSplit++;
+  }
+
+  @Override
+  public void handleMessage(String msg) {
+    write(msg);
+  }
+
+  @Override
+  public void handleMutations(List<Mutation> mutations) {
+    write("Mutations Report");
+    for (Mutation mutation : mutations) {
+      write(MutationSerializer.getMutationString(mutation));
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java
new file mode 100644
index 0000000..28ebce7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/MultiplexerListener.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Handles situation where multiple Listeners are wanted, passes notifications
+ * onto each Listener it is responsible for.
+ */
+public class MultiplexerListener extends BaseListener {
+
+  private List<BaseListener> listeners;
+
+  @Override
+  public void setup() {
+    listeners = new ArrayList<BaseListener>();
+  }
+
+  public void addListener(BaseListener listener) {
+    listeners.add(listener);
+    listener.setup();
+  }
+
+  @Override
+  public void shutdown() {
+    for (BaseListener listener : listeners) {
+      listener.shutdown();
+    }
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    for (BaseListener listener : listeners) {
+      listener.handleSuccessfulHostVerification();
+    }
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    for (BaseListener listener : listeners) {
+      listener.handleFailedHostVerification(verificationResult);
+    }
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    for (BaseListener listener : listeners) {
+      listener.handleFailedTargetVerification();
+    }
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    for (BaseListener listener : listeners) {
+      listener.handleIterationStarted(iteration);
+    }
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    for (BaseListener listener : listeners) {
+      listener.handleIterationFinished(iteration);
+    }
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    for (BaseListener listener : listeners) {
+      listener.handleTimeouts(timedOut, didNotTimeOut);
+    }
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    for (BaseListener listener : listeners) {
+      listener.handleDivergences(outputMap);
+    }
+  }
+
+  @Override
+  public void handleFuzzingFile(String inputFile) {
+    for (BaseListener listener : listeners) {
+      listener.handleFuzzingFile(inputFile);
+    }
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    for (BaseListener listener : listeners) {
+      listener.handleSeed(seed);
+    }
+  }
+
+  @Override
+  public void handleHostVerificationSigabort(ExecutionResult verificationResult) {
+    for (BaseListener listener : listeners) {
+      listener.handleHostVerificationSigabort(verificationResult);
+    }
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    for (BaseListener listener : listeners) {
+      listener.handleSuccess(outputMap);
+    }
+  }
+
+  @Override
+  public void handleDumpOutput(String outputLine, Executor executor) {
+    for (BaseListener listener : listeners) {
+      listener.handleDumpOutput(outputLine, executor);
+    }
+  }
+
+  @Override
+  public void handleDumpVerify(String verifyLine) {
+    for (BaseListener listener : listeners) {
+      listener.handleDumpVerify(verifyLine);
+    }
+  }
+
+  @Override
+  public void handleMutationStats(String statsString) {
+    for (BaseListener listener : listeners) {
+      listener.handleMutationStats(statsString);
+    }
+  }
+
+  @Override
+  public void handleTiming(String name, float elapsedTime) {
+    for (BaseListener listener : listeners) {
+      listener.handleTiming(name, elapsedTime);
+    }
+  }
+
+  @Override
+  public void handleMutationFail() {
+    for (BaseListener listener : listeners) {
+      listener.handleMutationFail();
+    }
+  }
+
+  @Override
+  public void handleSummary() {
+    for (BaseListener listener : listeners) {
+      listener.handleSummary();
+    }
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    for (BaseListener listener : listeners) {
+      listener.handleSuccessfullyFuzzedFile(programName);
+    }
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    for (BaseListener listener : listeners) {
+      listener.handleSelfDivergence();
+    }
+  }
+
+  @Override
+  public void handleMessage(String msg) {
+    for (BaseListener listener : listeners) {
+      listener.handleMessage(msg);
+    }
+  }
+
+  @Override
+  public void handleMutations(List<Mutation> mutations) {
+    for (BaseListener listener : listeners) {
+      listener.handleMutations(mutations);
+    }
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    for (BaseListener listener : listeners) {
+      listener.handleArchitectureSplit();
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java b/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java
new file mode 100644
index 0000000..affaffc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/UniqueProgramTrackerListener.java
@@ -0,0 +1,259 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.Log;
+import dexfuzz.Options;
+import dexfuzz.executors.Executor;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Tracks unique programs and outputs. Also saves divergent programs!
+ */
+public class UniqueProgramTrackerListener extends BaseListener {
+  /**
+   * Map of unique program MD5 sums, mapped to times seen.
+   */
+  private Map<String, Integer> uniquePrograms;
+
+  /**
+   * Map of unique program outputs (MD5'd), mapped to times seen.
+   */
+  private Map<String, Integer> uniqueOutputs;
+
+  /**
+   * Used to remember the seed used to fuzz the fuzzed file, so we can save it with this
+   * seed as a name, if we find a divergence.
+   */
+  private long currentSeed;
+
+  /**
+   * Used to remember the name of the file we've fuzzed, so we can save it if we
+   * find a divergence.
+   */
+  private String fuzzedFile;
+
+  private MessageDigest digest;
+  private String databaseFile;
+
+  /**
+   * Save the database every X number of iterations.
+   */
+  private static final int saveDatabasePeriod = 20;
+
+  public UniqueProgramTrackerListener(String databaseFile) {
+    this.databaseFile = databaseFile;
+  }
+
+  @Override
+  public void handleSeed(long seed) {
+    currentSeed = seed;
+  }
+
+  /**
+   * Given a program filename, calculate the MD5sum of
+   * this program.
+   */
+  private String getMD5SumOfProgram(String programName) {
+    byte[] buf = new byte[256];
+    try {
+      FileInputStream stream = new FileInputStream(programName);
+      boolean done = false;
+      while (!done) {
+        int bytesRead = stream.read(buf);
+        if (bytesRead == -1) {
+          done = true;
+        } else {
+          digest.update(buf);
+        }
+      }
+      stream.close();
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+    return new String(digest.digest());
+  }
+
+  private String getMD5SumOfOutput(String output) {
+    digest.update(output.getBytes());
+    return new String(digest.digest());
+  }
+
+  @SuppressWarnings("unchecked")
+  private void loadUniqueProgsData() {
+    File file = new File(databaseFile);
+    if (!file.exists()) {
+      uniquePrograms = new HashMap<String, Integer>();
+      uniqueOutputs = new HashMap<String, Integer>();
+      return;
+    }
+
+    try {
+      ObjectInputStream objectStream =
+          new ObjectInputStream(new FileInputStream(databaseFile));
+      uniquePrograms = (Map<String, Integer>) objectStream.readObject();
+      uniqueOutputs = (Map<String, Integer>) objectStream.readObject();
+      objectStream.close();
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    } catch (ClassNotFoundException e) {
+      e.printStackTrace();
+    }
+
+  }
+
+  private void saveUniqueProgsData() {
+    // Since we could potentially stop the program while writing out this DB,
+    // copy the old file beforehand, and then delete it if we successfully wrote out the DB.
+    boolean oldWasSaved = false;
+    File file = new File(databaseFile);
+    if (file.exists()) {
+      try {
+        Process process =
+            Runtime.getRuntime().exec(String.format("cp %1$s %1$s.old", databaseFile));
+        // Shouldn't block, cp shouldn't produce output.
+        process.waitFor();
+        oldWasSaved = true;
+      } catch (IOException exception) {
+        exception.printStackTrace();
+      } catch (InterruptedException exception) {
+        exception.printStackTrace();
+      }
+    }
+
+    // Now write out the DB.
+    boolean success = false;
+    try {
+      ObjectOutputStream objectStream =
+          new ObjectOutputStream(new FileOutputStream(databaseFile));
+      objectStream.writeObject(uniquePrograms);
+      objectStream.writeObject(uniqueOutputs);
+      objectStream.close();
+      success = true;
+    } catch (FileNotFoundException e) {
+      e.printStackTrace();
+    } catch (IOException e) {
+      e.printStackTrace();
+    }
+
+    // If we get here, and we successfully wrote out the DB, delete the saved one.
+    if (oldWasSaved && success) {
+      try {
+        Process process =
+            Runtime.getRuntime().exec(String.format("rm %s.old", databaseFile));
+        // Shouldn't block, rm shouldn't produce output.
+        process.waitFor();
+      } catch (IOException exception) {
+        exception.printStackTrace();
+      } catch (InterruptedException exception) {
+        exception.printStackTrace();
+      }
+    } else if (oldWasSaved && !success) {
+      Log.error("Failed to successfully write out the unique programs DB!");
+      Log.error("Old DB should be saved in " + databaseFile + ".old");
+    }
+  }
+
+  private void addToMap(String md5sum, Map<String, Integer> map) {
+    if (map.containsKey(md5sum)) {
+      map.put(md5sum, map.get(md5sum) + 1);
+    } else {
+      map.put(md5sum, 1);
+    }
+  }
+
+  private void saveDivergentProgram() {
+    File before = new File(fuzzedFile);
+    File after = new File(String.format("divergent_programs/%d.dex", currentSeed));
+    boolean success = before.renameTo(after);
+    if (!success) {
+      Log.error("Failed to save divergent program! Does divergent_programs/ exist?");
+    }
+  }
+
+  @Override
+  public void setup() {
+    try {
+      digest = MessageDigest.getInstance("MD5");
+      loadUniqueProgsData();
+    } catch (NoSuchAlgorithmException e) {
+      e.printStackTrace();
+    }
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    if ((iteration % saveDatabasePeriod) == (saveDatabasePeriod - 1)) {
+      saveUniqueProgsData();
+    }
+  }
+
+  @Override
+  public void handleSuccessfullyFuzzedFile(String programName) {
+    String md5sum = getMD5SumOfProgram(programName);
+    addToMap(md5sum, uniquePrograms);
+
+    fuzzedFile = programName;
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    // Just use the first one.
+    String output = (String) outputMap.keySet().toArray()[0];
+    String md5sum = getMD5SumOfOutput(output);
+    addToMap(md5sum, uniqueOutputs);
+
+    saveDivergentProgram();
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    // There's only one, use it.
+    String output = (String) outputMap.keySet().toArray()[0];
+    String md5sum = getMD5SumOfOutput(output);
+    addToMap(md5sum, uniqueOutputs);
+  }
+
+  @Override
+  public void handleSummary() {
+    if (Options.reportUnique) {
+      Log.always("-- UNIQUE PROGRAM REPORT --");
+      Log.always("Unique Programs Seen: " + uniquePrograms.size());
+      Log.always("Unique Outputs Seen: " + uniqueOutputs.size());
+      Log.always("---------------------------");
+    }
+
+    saveUniqueProgsData();
+  }
+
+}
diff --git a/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java b/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java
new file mode 100644
index 0000000..39d1d2f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/listeners/UpdatingConsoleListener.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.listeners;
+
+import dexfuzz.ExecutionResult;
+import dexfuzz.executors.Executor;
+
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implements the live updating table of results when --repeat is being used.
+ */
+public class UpdatingConsoleListener extends BaseListener {
+  long successfulVerification;
+  long failedVerification;
+  long failedMutation;
+  long success;
+  long timedOut;
+  long divergence;
+  long selfDivergent;
+  long architectureSplit;
+  long iterations;
+
+  @Override
+  public void setup() {
+    System.out.println("|-----------------------------------------------------------------|");
+    System.out.println("|Iterations|VerifyFail|MutateFail|Timed Out |Successful|Divergence|");
+    System.out.println("|-----------------------------------------------------------------|");
+  }
+
+  @Override
+  public void handleSuccessfulHostVerification() {
+    successfulVerification++;
+  }
+
+  @Override
+  public void handleFailedHostVerification(ExecutionResult verificationResult) {
+    failedVerification++;
+  }
+
+  @Override
+  public void handleFailedTargetVerification() {
+    failedVerification++;
+  }
+
+  @Override
+  public void handleIterationStarted(int iteration) {
+    iterations++;
+  }
+
+  @Override
+  public void handleIterationFinished(int iteration) {
+    String output = String.format("| %-9d| %-9d| %-9d| %-9d| %-9d| %-9d|",
+        iterations, failedVerification, failedMutation, timedOut, success,
+        divergence - (selfDivergent + architectureSplit));
+    System.out.print("\r" + output);
+  }
+
+  @Override
+  public void handleTimeouts(List<Executor> timedOut, List<Executor> didNotTimeOut) {
+    this.timedOut++;
+  }
+
+  @Override
+  public void handleDivergences(Map<String, List<Executor>> outputMap) {
+    divergence++;
+  }
+
+  @Override
+  public void handleSelfDivergence() {
+    selfDivergent++;
+  }
+
+  @Override
+  public void handleArchitectureSplit() {
+    architectureSplit++;
+  }
+
+  @Override
+  public void handleSuccess(Map<String, List<Executor>> outputMap) {
+    success++;
+  }
+
+  @Override
+  public void handleMutationFail() {
+    failedMutation++;
+  }
+
+  @Override
+  public void handleSummary() {
+    System.out.println("\n|-----------------------------------------------------------------|");
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
new file mode 100644
index 0000000..650501b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/CodeTranslator.java
@@ -0,0 +1,592 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.CodeItem;
+import dexfuzz.rawdex.EncodedCatchHandler;
+import dexfuzz.rawdex.EncodedTypeAddrPair;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.TryItem;
+import dexfuzz.rawdex.formats.ContainsTarget;
+import dexfuzz.rawdex.formats.RawInsnHelper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Translates from a CodeItem (the raw list of Instructions) to MutatableCode
+ * (graph of Instructions, using MInsns and subclasses) and vice-versa.
+ */
+public class CodeTranslator {
+
+  /**
+   * Given a raw DEX file's CodeItem, produce a MutatableCode object, that CodeMutators
+   * are designed to operate on.
+   * @param codeItemIdx Used to make sure the correct CodeItem is updated later after mutation.
+   * @return A new MutatableCode object, which contains all relevant information
+   *         obtained from the CodeItem.
+   */
+  public MutatableCode codeItemToMutatableCode(Program program, CodeItem codeItem,
+      int codeItemIdx, int mutatableCodeIdx) {
+    Log.debug("Translating CodeItem " + codeItemIdx
+        + " (" + codeItem.meta.methodName + ") to MutatableCode");
+
+    MutatableCode mutatableCode = new MutatableCode(program);
+
+    codeItem.registerMutatableCode(mutatableCode);
+
+    mutatableCode.name = codeItem.meta.methodName;
+    mutatableCode.shorty = codeItem.meta.shorty;
+    mutatableCode.isStatic = codeItem.meta.isStatic;
+
+    mutatableCode.codeItemIdx = codeItemIdx;
+
+    mutatableCode.mutatableCodeIdx = mutatableCodeIdx;
+
+    mutatableCode.registersSize = codeItem.registersSize;
+    mutatableCode.insSize = codeItem.insSize;
+    mutatableCode.outsSize = codeItem.outsSize;
+    mutatableCode.triesSize = codeItem.triesSize;
+
+    // Temporary map from bytecode offset -> instruction.
+    Map<Integer,MInsn> insnLocationMap = new HashMap<Integer,MInsn>();
+
+    List<Instruction> inputInsns = codeItem.insns;
+
+    // Create the MInsns.
+    int loc = 0;
+    for (Instruction insn : inputInsns) {
+      MInsn mInsn = null;
+
+      if (isInstructionSwitch(insn)) {
+        mInsn = new MSwitchInsn();
+      } else if (isInstructionBranch(insn)) {
+        mInsn = new MBranchInsn();
+      } else if (isInstructionFillArrayData(insn)) {
+        mInsn = new MInsnWithData();
+      } else {
+        mInsn = new MInsn();
+      }
+
+      mInsn.insn = insn;
+
+      // Populate the temporary map.
+      insnLocationMap.put(loc, mInsn);
+
+      // Populate the proper list of mutatable instructions.
+      mutatableCode.addInstructionToEnd(mInsn);
+
+      // Calculate the offsets for each instruction.
+      mInsn.location = loc;
+      mInsn.locationUpdated = false;
+
+      loc += mInsn.insn.getSize();
+    }
+
+    // Now make branch/switch instructions point at the right target instructions.
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        readSwitchInstruction((MSwitchInsn) mInsn, insnLocationMap);
+      } else if (mInsn instanceof MInsnWithData) {
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn);
+        ((MInsnWithData)mInsn).dataTarget = insnLocationMap.get(targetLoc);
+        if (((MInsnWithData)mInsn).dataTarget == null) {
+          Log.errorAndQuit("Bad offset calculation in data-target insn");
+        }
+      } else if (mInsn instanceof MBranchInsn) {
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        int targetLoc = mInsn.location + (int) containsTarget.getTarget(mInsn.insn);
+        ((MBranchInsn)mInsn).target = insnLocationMap.get(targetLoc);
+        if (((MBranchInsn)mInsn).target == null) {
+          Log.errorAndQuit("Bad offset calculation in branch insn");
+        }
+      }
+    }
+
+    // Now create try blocks.
+    if (mutatableCode.triesSize > 0) {
+      readTryBlocks(codeItem, mutatableCode, insnLocationMap);
+    }
+
+    return mutatableCode;
+  }
+
+  /**
+   * Given a MutatableCode item that may have been mutated, update the original CodeItem
+   * correctly, to allow valid DEX to be written back to the output file.
+   */
+  public void mutatableCodeToCodeItem(CodeItem codeItem, MutatableCode mutatableCode) {
+    Log.debug("Translating MutatableCode " + mutatableCode.name
+        + " to CodeItem " + mutatableCode.codeItemIdx);
+
+    // We must first align any data instructions at the end of the code
+    // before we recalculate any offsets.
+    // This also updates their sizes...
+    alignDataInstructions(mutatableCode);
+
+    // Validate that the tracked locations for instructions are valid.
+    // Also mark locations as no longer being updated.
+    int loc = 0;
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.justRaw) {
+        // All just_raw instructions need alignment!
+        if ((loc % 2) != 0) {
+          loc++;
+        }
+      }
+      if (mInsn.location != loc) {
+        Log.errorAndQuit(String.format("%s does not have expected location 0x%x",
+            mInsn, loc));
+      }
+      mInsn.locationUpdated = false;
+      loc += mInsn.insn.getSize();
+    }
+
+    // This new list will be attached to the CodeItem at the end...
+    List<Instruction> outputInsns = new LinkedList<Instruction>();
+
+    // Go through our new list of MInsns, adding them to the new
+    // list of instructions that will be attached to the CodeItem.
+    // Also recalculate offsets for branches.
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        updateSwitchInstruction((MSwitchInsn)mInsn, mutatableCode);
+      } else if (mInsn instanceof MInsnWithData) {
+        MInsn target = ((MInsnWithData) mInsn).dataTarget;
+        int dataOffset = target.location - mInsn.location;
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        containsTarget.setTarget(mInsn.insn, dataOffset);
+      } else if (mInsn instanceof MBranchInsn) {
+        MInsn target = ((MBranchInsn) mInsn).target;
+        int branchOffset = target.location - mInsn.location;
+        ContainsTarget containsTarget = (ContainsTarget) mInsn.insn.info.format;
+        containsTarget.setTarget(mInsn.insn, branchOffset);
+      }
+      outputInsns.add(mInsn.insn);
+    }
+
+    // Calculate the new insns_size.
+    int newInsnsSize = 0;
+    for (Instruction insn : outputInsns) {
+      newInsnsSize += insn.getSize();
+    }
+
+    if (mutatableCode.triesSize > 0) {
+      updateTryBlocks(codeItem, mutatableCode);
+    }
+
+    codeItem.insnsSize = newInsnsSize;
+    codeItem.insns = outputInsns;
+    codeItem.registersSize = mutatableCode.registersSize;
+    codeItem.insSize = mutatableCode.insSize;
+    codeItem.outsSize = mutatableCode.outsSize;
+    codeItem.triesSize = mutatableCode.triesSize;
+  }
+
+  /**
+   * The TryItem specifies an offset into the EncodedCatchHandlerList for a given CodeItem,
+   * but we only have an array of the EncodedCatchHandlers that the List contains.
+   * This function produces a map that offers a way to find out the index into our array,
+   * from the try handler's offset.
+   */
+  private Map<Short,Integer> createTryHandlerOffsetToIndexMap(CodeItem codeItem) {
+    // Create a sorted set of offsets.
+    List<Short> uniqueOffsets = new ArrayList<Short>();
+    for (TryItem tryItem : codeItem.tries) {
+      int index = 0;
+      while (true) {
+        if ((index == uniqueOffsets.size())
+            || (uniqueOffsets.get(index) > tryItem.handlerOff)) {
+          // First condition means we're at the end of the set (or we're inserting
+          //   into an empty set)
+          // Second condition means that the offset belongs here
+          // ...so insert it here, pushing the element currently in this position to the
+          //    right, if it exists
+          uniqueOffsets.add(index, tryItem.handlerOff);
+          break;
+        } else if (uniqueOffsets.get(index) == tryItem.handlerOff) {
+          // We've already seen it, and we're making a set, not a list.
+          break;
+        } else {
+          // Keep searching.
+          index++;
+        }
+      }
+    }
+    // Now we have an (implicit) index -> offset mapping!
+
+    // Now create the reverse mapping.
+    Map<Short,Integer> offsetIndexMap = new HashMap<Short,Integer>();
+    for (int i = 0; i < uniqueOffsets.size(); i++) {
+      offsetIndexMap.put(uniqueOffsets.get(i), i);
+    }
+
+    return offsetIndexMap;
+  }
+
+  private void readTryBlocks(CodeItem codeItem, MutatableCode mutatableCode,
+      Map<Integer,MInsn> insnLocationMap) {
+    mutatableCode.mutatableTries = new LinkedList<MTryBlock>();
+
+    Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem);
+
+    // Read each TryItem into a MutatableTryBlock.
+    for (TryItem tryItem : codeItem.tries) {
+      MTryBlock mTryBlock = new MTryBlock();
+
+      // Get the MInsns that form the start and end of the try block.
+      int startLocation = tryItem.startAddr;
+      mTryBlock.startInsn = insnLocationMap.get(startLocation);
+      int endLocation = tryItem.startAddr + tryItem.insnCount;
+      mTryBlock.endInsn = insnLocationMap.get(endLocation);
+
+      // Sanity checks.
+      if (mTryBlock.startInsn == null) {
+        Log.errorAndQuit(String.format(
+            "Couldn't find a mutatable insn at start offset 0x%x",
+            startLocation));
+      }
+      if (mTryBlock.endInsn == null) {
+        Log.errorAndQuit(String.format(
+            "Couldn't find a mutatable insn at end offset 0x%x",
+            endLocation));
+      }
+
+      // Get the EncodedCatchHandler.
+      int handlerIdx = offsetIndexMap.get(tryItem.handlerOff);
+      EncodedCatchHandler encodedCatchHandler = codeItem.handlers.list[handlerIdx];
+
+      // Do we have a catch all? If so, associate the MInsn that's there.
+      if (encodedCatchHandler.size <= 0) {
+        mTryBlock.catchAllHandler =
+            insnLocationMap.get(encodedCatchHandler.catchAllAddr);
+        // Sanity check.
+        if (mTryBlock.catchAllHandler == null) {
+          Log.errorAndQuit(
+              String.format("Couldn't find a mutatable insn at catch-all offset 0x%x",
+                  encodedCatchHandler.catchAllAddr));
+        }
+      }
+      // Do we have explicitly-typed handlers? This will remain empty if not.
+      mTryBlock.handlers = new LinkedList<MInsn>();
+
+      // Associate all the explicitly-typed handlers.
+      for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
+        EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
+        MInsn handlerInsn = insnLocationMap.get(handler.addr);
+        // Sanity check.
+        if (handlerInsn == null) {
+          Log.errorAndQuit(String.format(
+              "Couldn't find a mutatable instruction at handler offset 0x%x",
+              handler.addr));
+        }
+        mTryBlock.handlers.add(handlerInsn);
+      }
+
+      // Now finally add the new MutatableTryBlock into this MutatableCode's list!
+      mutatableCode.mutatableTries.add(mTryBlock);
+    }
+  }
+
+  private void updateTryBlocks(CodeItem codeItem, MutatableCode mutatableCode) {
+
+    // TODO: Support ability to add extra try blocks/handlers?
+
+    for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
+      if (mTryBlock.startInsn.location > mTryBlock.endInsn.location) {
+        // Mutation has put this try block's end insn before its start insn. Fix this.
+        MInsn tempInsn = mTryBlock.startInsn;
+        mTryBlock.startInsn = mTryBlock.endInsn;
+        mTryBlock.endInsn = tempInsn;
+      }
+    }
+
+    // First, manipulate the try blocks if they overlap.
+    for (int i = 0; i < mutatableCode.mutatableTries.size() - 1; i++) {
+      MTryBlock first = mutatableCode.mutatableTries.get(i);
+      MTryBlock second = mutatableCode.mutatableTries.get(i + 1);
+
+      // Do they overlap?
+      if (first.endInsn.location > second.startInsn.location) {
+
+        Log.debug("Found overlap in TryBlocks, moving 2nd TryBlock...");
+        Log.debug("1st TryBlock goes from " + first.startInsn + " to "  + first.endInsn);
+        Log.debug("2nd TryBlock goes from " + second.startInsn + " to "  + second.endInsn);
+
+        // Find the first instruction that comes after that does not overlap
+        // with the first try block.
+        MInsn newInsn = second.startInsn;
+        int ptr = mutatableCode.getInstructionIndex(newInsn);
+        while (first.endInsn.location > newInsn.location) {
+          ptr++;
+          newInsn = mutatableCode.getInstructionAt(ptr);
+        }
+        second.startInsn = newInsn;
+
+        Log.debug("Now 2nd TryBlock goes from " + second.startInsn + " to "  + second.endInsn);
+      }
+    }
+
+    Map<Short,Integer> offsetIndexMap = createTryHandlerOffsetToIndexMap(codeItem);
+
+    int tryItemIdx = 0;
+    for (MTryBlock mTryBlock : mutatableCode.mutatableTries) {
+      TryItem tryItem = codeItem.tries[tryItemIdx];
+
+      tryItem.startAddr = mTryBlock.startInsn.location;
+      tryItem.insnCount =
+          (short) (mTryBlock.endInsn.location - mTryBlock.startInsn.location);
+
+      // Get the EncodedCatchHandler.
+      EncodedCatchHandler encodedCatchHandler =
+          codeItem.handlers.list[offsetIndexMap.get(tryItem.handlerOff)];
+
+      if (encodedCatchHandler.size <= 0) {
+        encodedCatchHandler.catchAllAddr = mTryBlock.catchAllHandler.location;
+      }
+      for (int i = 0; i < Math.abs(encodedCatchHandler.size); i++) {
+        MInsn handlerInsn = mTryBlock.handlers.get(i);
+        EncodedTypeAddrPair handler = encodedCatchHandler.handlers[i];
+        handler.addr = handlerInsn.location;
+      }
+      tryItemIdx++;
+    }
+  }
+
+  /**
+   * Given a switch instruction, find the associated data's raw[] form, and update
+   * the targets of the switch instruction to point to the correct instructions.
+   */
+  private void readSwitchInstruction(MSwitchInsn switchInsn,
+      Map<Integer,MInsn> insnLocationMap) {
+    // Find the data.
+    ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
+    int dataLocation = switchInsn.location + (int) containsTarget.getTarget(switchInsn.insn);
+    switchInsn.dataTarget = insnLocationMap.get(dataLocation);
+    if (switchInsn.dataTarget == null) {
+      Log.errorAndQuit("Bad offset calculation for data target in switch insn");
+    }
+
+    // Now read the data.
+    Instruction dataInsn = switchInsn.dataTarget.insn;
+
+    int rawPtr = 2;
+
+    int targetsSize = (int) RawInsnHelper.getUnsignedShortFromTwoBytes(dataInsn.rawBytes, rawPtr);
+    rawPtr += 2;
+
+    int[] keys = new int[targetsSize];
+    int[] targets = new int[targetsSize];
+
+    if (dataInsn.rawType == 1) {
+      switchInsn.packed = true;
+      // Dealing with a packed-switch.
+      // Read the first key.
+      keys[0] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes, rawPtr);
+      rawPtr += 4;
+      // Calculate the rest of the keys.
+      for (int i = 1; i < targetsSize; i++) {
+        keys[i] = keys[i - 1] + 1;
+      }
+    } else if (dataInsn.rawType == 2) {
+      // Dealing with a sparse-switch.
+      // Read all of the keys.
+      for (int i = 0; i < targetsSize; i++) {
+        keys[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
+            rawPtr);
+        rawPtr += 4;
+      }
+    }
+
+    // Now read the targets.
+    for (int i = 0; i < targetsSize; i++) {
+      targets[i] = (int) RawInsnHelper.getUnsignedIntFromFourBytes(dataInsn.rawBytes,
+          rawPtr);
+      rawPtr += 4;
+    }
+
+    // Store the keys.
+    switchInsn.keys = keys;
+
+    // Convert our targets[] offsets into pointers to MInsns.
+    for (int target : targets) {
+      int targetLocation = switchInsn.location + target;
+      MInsn targetInsn = insnLocationMap.get(targetLocation);
+      switchInsn.targets.add(targetInsn);
+      if (targetInsn == null) {
+        Log.errorAndQuit("Bad offset calculation for target in switch insn");
+      }
+    }
+  }
+
+  /**
+   * Given a mutatable switch instruction, which may have had some of its branch
+   * targets moved, update all the target offsets in the raw[] form of the instruction.
+   */
+  private void updateSwitchInstruction(MSwitchInsn switchInsn, MutatableCode mutatableCode) {
+    // Update the offset to the data instruction
+    MInsn dataTarget = switchInsn.dataTarget;
+    int dataOffset = dataTarget.location - switchInsn.location;
+    ContainsTarget containsTarget = (ContainsTarget) switchInsn.insn.info.format;
+    containsTarget.setTarget(switchInsn.insn, dataOffset);
+
+    int targetsSize = switchInsn.targets.size();
+
+    int[] keys = switchInsn.keys;
+    int[] targets = new int[targetsSize];
+
+    // Calculate the new offsets.
+    int targetIdx = 0;
+    for (MInsn target : switchInsn.targets) {
+      targets[targetIdx] = target.location - switchInsn.location;
+      targetIdx++;
+    }
+
+    // Now write the data back to the raw bytes.
+    Instruction dataInsn = switchInsn.dataTarget.insn;
+
+    int rawPtr = 2;
+
+    // Write out the size.
+    RawInsnHelper.writeUnsignedShortToTwoBytes(dataInsn.rawBytes, rawPtr, targetsSize);
+    rawPtr += 2;
+
+    // Write out the keys.
+    if (switchInsn.packed) {
+      // Only write out one key - the first.
+      RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[0]);
+      rawPtr += 4;
+    } else {
+      // Write out all the keys.
+      for (int i = 0; i < targetsSize; i++) {
+        RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, keys[i]);
+        rawPtr += 4;
+      }
+    }
+
+    // Write out all the targets.
+    for (int i = 0; i < targetsSize; i++) {
+      RawInsnHelper.writeUnsignedIntToFourBytes(dataInsn.rawBytes, rawPtr, targets[i]);
+      rawPtr += 4;
+    }
+  }
+
+  /**
+   * After mutation, data instructions may no longer be 4-byte aligned.
+   * If this is the case, insert nops to align them all.
+   * This makes a number of assumptions about data currently:
+   * - data is always at the end of method insns
+   * - all data instructions are stored contiguously
+   */
+  private void alignDataInstructions(MutatableCode mutatableCode) {
+    // Find all the switch data instructions.
+    List<MInsn> dataInsns = new ArrayList<MInsn>();
+
+    // Update raw sizes of the data instructions as well.
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        // Update the raw size of the instruction.
+        MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
+        int targetsSize = switchInsn.targets.size();
+        Instruction dataInsn = switchInsn.dataTarget.insn;
+        if (switchInsn.packed) {
+          dataInsn.rawSize = (targetsSize * 2) + 4;
+        } else {
+          dataInsn.rawSize = (targetsSize * 4) + 2;
+        }
+        dataInsns.add(switchInsn.dataTarget);
+      } else if (mInsn instanceof MInsnWithData) {
+        MInsnWithData insnWithData =
+            (MInsnWithData) mInsn;
+        dataInsns.add(insnWithData.dataTarget);
+      }
+    }
+
+    // Only need to align switch data instructions if there are any!
+    if (!dataInsns.isEmpty()) {
+
+      Log.debug("Found data instructions, checking alignment...");
+
+      // Sort data_insns by location.
+      Collections.sort(dataInsns, new Comparator<MInsn>() {
+        @Override
+        public int compare(MInsn first, MInsn second) {
+          if (first.location < second.location) {
+            return -1;
+          } else if (first.location > second.location) {
+            return 1;
+          }
+          return 0;
+        }
+      });
+
+      boolean performedAlignment = false;
+
+      // Go through all the data insns, and insert an alignment nop if they're unaligned.
+      for (MInsn dataInsn : dataInsns) {
+        if (dataInsn.location % 2 != 0) {
+          Log.debug("Aligning data instruction with a nop.");
+          int alignmentNopIdx = mutatableCode.getInstructionIndex(dataInsn);
+          MInsn nop = new MInsn();
+          nop.insn = new Instruction();
+          nop.insn.info = Instruction.getOpcodeInfo(Opcode.NOP);
+          mutatableCode.insertInstructionAt(nop, alignmentNopIdx);
+          performedAlignment = true;
+        }
+      }
+
+      if (!performedAlignment) {
+        Log.debug("Alignment okay.");
+      }
+    }
+  }
+
+  /**
+   * Determine if a particular instruction is a branch instruction, based on opcode.
+   */
+  private boolean isInstructionBranch(Instruction insn) {
+    Opcode opcode = insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+        || Opcode.isBetween(opcode, Opcode.GOTO, Opcode.GOTO_32)) {
+      return true;
+    }
+    return false;
+  }
+
+  /**
+   * Determine if a particular instruction is a switch instruction, based on opcode.
+   */
+  private boolean isInstructionSwitch(Instruction insn) {
+    Opcode opcode = insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)) {
+      return true;
+    }
+    return false;
+  }
+
+  private boolean isInstructionFillArrayData(Instruction insn) {
+    return (insn.info.opcode == Opcode.FILL_ARRAY_DATA);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/IdCreator.java b/tools/dexfuzz/src/dexfuzz/program/IdCreator.java
new file mode 100644
index 0000000..c506fa6
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/IdCreator.java
@@ -0,0 +1,804 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.FieldIdItem;
+import dexfuzz.rawdex.MethodIdItem;
+import dexfuzz.rawdex.Offset;
+import dexfuzz.rawdex.Offsettable;
+import dexfuzz.rawdex.ProtoIdItem;
+import dexfuzz.rawdex.RawDexFile;
+import dexfuzz.rawdex.RawDexObject.IndexUpdateKind;
+import dexfuzz.rawdex.StringDataItem;
+import dexfuzz.rawdex.StringIdItem;
+import dexfuzz.rawdex.TypeIdItem;
+import dexfuzz.rawdex.TypeItem;
+import dexfuzz.rawdex.TypeList;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Responsible for the finding and creation of TypeIds, MethodIds, FieldIds, and StringIds,
+ * during mutation.
+ */
+public class IdCreator {
+  private RawDexFile rawDexFile;
+
+  public IdCreator(RawDexFile rawDexFile) {
+    this.rawDexFile = rawDexFile;
+  }
+
+  private int findProtoIdInsertionPoint(String signature) {
+    int returnTypeIdx = findTypeId(convertSignatureToReturnType(signature));
+    String[] parameterListStrings = convertSignatureToParameterList(signature);
+    TypeList parameterList = null;
+    if (parameterListStrings.length > 0) {
+      parameterList = findTypeList(parameterListStrings);
+    }
+
+    if (returnTypeIdx < 0) {
+      Log.errorAndQuit("Did not create necessary return type before finding insertion "
+          + "point for new proto!");
+    }
+
+    if (parameterListStrings.length > 0 && parameterList == null) {
+      Log.errorAndQuit("Did not create necessary parameter list before finding insertion "
+          + "point for new proto!");
+    }
+
+    int protoIdIdx = 0;
+    for (ProtoIdItem protoId : rawDexFile.protoIds) {
+      if (returnTypeIdx < protoId.returnTypeIdx) {
+        break;
+      }
+      if (returnTypeIdx == protoId.returnTypeIdx
+          && parameterListStrings.length == 0) {
+        break;
+      }
+      if (returnTypeIdx == protoId.returnTypeIdx
+          && parameterListStrings.length > 0
+          && protoId.parametersOff.pointsToSomething()
+          && parameterList.comesBefore(
+              (TypeList) protoId.parametersOff.getPointedToItem())) {
+        break;
+      }
+      protoIdIdx++;
+    }
+    return protoIdIdx;
+  }
+
+  private int findMethodIdInsertionPoint(String className, String methodName, String signature) {
+    int classIdx = findTypeId(className);
+    int nameIdx = findString(methodName);
+    int protoIdx = findProtoId(signature);
+
+    if (classIdx < 0 || nameIdx < 0 || protoIdx < 0) {
+      Log.errorAndQuit("Did not create necessary class, name or proto strings before finding "
+          + " insertion point for new method!");
+    }
+
+    int methodIdIdx = 0;
+    for (MethodIdItem methodId : rawDexFile.methodIds) {
+      if (classIdx < methodId.classIdx) {
+        break;
+      }
+      if (classIdx == methodId.classIdx && nameIdx < methodId.nameIdx) {
+        break;
+      }
+      if (classIdx == methodId.classIdx && nameIdx == methodId.nameIdx
+          && protoIdx < methodId.protoIdx) {
+        break;
+      }
+      methodIdIdx++;
+    }
+    return methodIdIdx;
+  }
+
+  private int findTypeIdInsertionPoint(String className) {
+    int descriptorIdx = findString(className);
+
+    if (descriptorIdx < 0) {
+      Log.errorAndQuit("Did not create necessary descriptor string before finding "
+          + " insertion point for new type!");
+    }
+
+    int typeIdIdx = 0;
+    for (TypeIdItem typeId : rawDexFile.typeIds) {
+      if (descriptorIdx < typeId.descriptorIdx) {
+        break;
+      }
+      typeIdIdx++;
+    }
+    return typeIdIdx;
+  }
+
+  private int findStringDataInsertionPoint(String string) {
+    int stringDataIdx = 0;
+    for (StringDataItem stringData : rawDexFile.stringDatas) {
+      if (stringData.getSize() > 0 && stringData.getString().compareTo(string) >= 0) {
+        break;
+      }
+      stringDataIdx++;
+    }
+    return stringDataIdx;
+  }
+
+  private int findFieldIdInsertionPoint(String className, String typeName, String fieldName) {
+    int classIdx = findTypeId(className);
+    int typeIdx = findTypeId(typeName);
+    int nameIdx = findString(fieldName);
+
+    if (classIdx < 0 || typeIdx < 0 || nameIdx < 0) {
+      Log.errorAndQuit("Did not create necessary class, type or name strings before finding "
+          + " insertion point for new field!");
+    }
+
+    int fieldIdIdx = 0;
+    for (FieldIdItem fieldId : rawDexFile.fieldIds) {
+      if (classIdx < fieldId.classIdx) {
+        break;
+      }
+      if (classIdx == fieldId.classIdx && nameIdx < fieldId.nameIdx) {
+        break;
+      }
+      if (classIdx == fieldId.classIdx && nameIdx == fieldId.nameIdx
+          && typeIdx < fieldId.typeIdx) {
+        break;
+      }
+      fieldIdIdx++;
+    }
+    return fieldIdIdx;
+  }
+
+  private int createMethodId(String className, String methodName, String signature) {
+    if (rawDexFile.methodIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many methods for the DEX file.");
+    }
+
+    // Search for (or create) the prototype.
+    int protoIdx = findOrCreateProtoId(signature);
+
+    // Search for (or create) the owning class.
+    // NB: findOrCreateProtoId could create new types, so this must come
+    //     after it!
+    int typeIdIdx = findOrCreateTypeId(className);
+
+    // Search for (or create) the string representing the method name.
+    // NB: findOrCreateProtoId/TypeId could create new strings, so this must come
+    //     after them!
+    int methodNameStringIdx = findOrCreateString(methodName);
+
+    // Create MethodIdItem.
+    MethodIdItem newMethodId = new MethodIdItem();
+    newMethodId.classIdx = (short) typeIdIdx;
+    newMethodId.protoIdx = (short) protoIdx;
+    newMethodId.nameIdx = methodNameStringIdx;
+
+    // MethodIds must be ordered.
+    int newMethodIdIdx = findMethodIdInsertionPoint(className, methodName, signature);
+
+    rawDexFile.methodIds.add(newMethodIdIdx, newMethodId);
+
+    // Insert into OffsetTracker.
+    if (newMethodIdIdx == 0) {
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstOfType(newMethodId, rawDexFile);
+    } else {
+      MethodIdItem prevMethodId = rawDexFile.methodIds.get(newMethodIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newMethodId, prevMethodId);
+    }
+
+    Log.info(String.format("Created new MethodIdItem for %s %s %s, index: 0x%04x",
+        className, methodName, signature, newMethodIdIdx));
+
+    // Now that we've potentially moved a lot of method IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.METHOD_ID, newMethodIdIdx);
+
+    // All done, return the index for the new method.
+    return newMethodIdIdx;
+  }
+
+  private int findMethodId(String className, String methodName, String signature) {
+    int classIdx = findTypeId(className);
+    if (classIdx == -1) {
+      return -1;
+    }
+    int nameIdx = findString(methodName);
+    if (nameIdx == -1) {
+      return -1;
+    }
+    int protoIdx = findProtoId(signature);
+    if (nameIdx == -1) {
+      return -1;
+    }
+
+    int methodIdIdx = 0;
+    for (MethodIdItem methodId : rawDexFile.methodIds) {
+      if (classIdx == methodId.classIdx
+          && nameIdx == methodId.nameIdx
+          && protoIdx == methodId.protoIdx) {
+        return methodIdIdx;
+      }
+      methodIdIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a fully qualified class name (Ljava/lang/System;), method name (gc) and
+   * and signature (()V), either find the MethodId in our DEX file's table, or create it.
+   */
+  public int findOrCreateMethodId(String className, String methodName, String shorty) {
+    int methodIdIdx = findMethodId(className, methodName, shorty);
+    if (methodIdIdx != -1) {
+      return methodIdIdx;
+    }
+    return createMethodId(className, methodName, shorty);
+  }
+
+  private int createTypeId(String className) {
+    if (rawDexFile.typeIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many classes for the DEX file.");
+    }
+
+    // Search for (or create) the string representing the class descriptor.
+    int descriptorStringIdx = findOrCreateString(className);
+
+    // Create TypeIdItem.
+    TypeIdItem newTypeId = new TypeIdItem();
+    newTypeId.descriptorIdx = descriptorStringIdx;
+
+    // TypeIds must be ordered.
+    int newTypeIdIdx = findTypeIdInsertionPoint(className);
+
+    rawDexFile.typeIds.add(newTypeIdIdx, newTypeId);
+
+    // Insert into OffsetTracker.
+    if (newTypeIdIdx == 0) {
+      rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newTypeId, rawDexFile);
+    } else {
+      TypeIdItem prevTypeId = rawDexFile.typeIds.get(newTypeIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newTypeId, prevTypeId);
+    }
+
+    Log.info(String.format("Created new ClassIdItem for %s, index: 0x%04x",
+        className, newTypeIdIdx));
+
+    // Now that we've potentially moved a lot of type IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.TYPE_ID, newTypeIdIdx);
+
+    // All done, return the index for the new class.
+    return newTypeIdIdx;
+  }
+
+  private int findTypeId(String className) {
+    int descriptorIdx = findString(className);
+    if (descriptorIdx == -1) {
+      return -1;
+    }
+
+    // Search for class.
+    int typeIdIdx = 0;
+    for (TypeIdItem typeId : rawDexFile.typeIds) {
+      if (descriptorIdx == typeId.descriptorIdx) {
+        return typeIdIdx;
+      }
+      typeIdIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a fully qualified class name (Ljava/lang/System;)
+   * either find the TypeId in our DEX file's table, or create it.
+   */
+  public int findOrCreateTypeId(String className) {
+    int typeIdIdx = findTypeId(className);
+    if (typeIdIdx != -1) {
+      return typeIdIdx;
+    }
+    return createTypeId(className);
+  }
+
+  private int createString(String string) {
+    // Didn't find it, create one...
+    int stringsCount = rawDexFile.stringIds.size();
+    if (stringsCount != rawDexFile.stringDatas.size()) {
+      Log.errorAndQuit("Corrupted DEX file, len(StringIDs) != len(StringDatas)");
+    }
+
+    // StringData must be ordered.
+    int newStringIdx = findStringDataInsertionPoint(string);
+
+    // Create StringDataItem.
+    StringDataItem newStringData = new StringDataItem();
+    newStringData.setSize(string.length());
+    newStringData.setString(string);
+
+    rawDexFile.stringDatas.add(newStringIdx, newStringData);
+
+    // Insert into OffsetTracker.
+    // (Need to save the Offsettable, because the StringIdItem will point to it.)
+    Offsettable offsettableStringData = null;
+    if (newStringIdx == 0) {
+      offsettableStringData =
+          rawDexFile.getOffsetTracker()
+          .insertNewOffsettableAsFirstOfType(newStringData, rawDexFile);
+    } else {
+      StringDataItem prevStringData = rawDexFile.stringDatas.get(newStringIdx - 1);
+      offsettableStringData = rawDexFile.getOffsetTracker()
+          .insertNewOffsettableAfter(newStringData, prevStringData);
+    }
+
+    // Create StringIdItem.
+    StringIdItem newStringId = new StringIdItem();
+    newStringId.stringDataOff = new Offset(false);
+    newStringId.stringDataOff.pointToNew(offsettableStringData);
+
+    rawDexFile.stringIds.add(newStringIdx, newStringId);
+
+    // Insert into OffsetTracker.
+    if (newStringIdx == 0) {
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstOfType(newStringId, rawDexFile);
+    } else {
+      StringIdItem prevStringId = rawDexFile.stringIds.get(newStringIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newStringId, prevStringId);
+    }
+
+
+    Log.info(String.format("Created new StringIdItem and StringDataItem for %s, index: 0x%04x",
+        string, newStringIdx));
+
+    // Now that we've potentially moved a lot of string IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.STRING_ID, newStringIdx);
+
+    // All done, return the index for the new string.
+    return newStringIdx;
+  }
+
+  private int findString(String string) {
+    // Search for string.
+    int stringIdx = 0;
+    for (StringDataItem stringDataItem : rawDexFile.stringDatas) {
+      if (stringDataItem.getSize() == 0 && string.isEmpty()) {
+        return stringIdx;
+      } else if (stringDataItem.getSize() > 0 && stringDataItem.getString().equals(string)) {
+        return stringIdx;
+      }
+      stringIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a string, either find the StringId in our DEX file's table, or create it.
+   */
+  public int findOrCreateString(String string) {
+    int stringIdx = findString(string);
+    if (stringIdx != -1) {
+      return stringIdx;
+    }
+    return createString(string);
+  }
+
+  private int createFieldId(String className, String typeName, String fieldName) {
+    if (rawDexFile.fieldIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many fields for the DEX file.");
+    }
+
+    // Search for (or create) the owning class.
+    int classIdx = findOrCreateTypeId(className);
+
+    // Search for (or create) the field's type.
+    int typeIdx = findOrCreateTypeId(typeName);
+
+    // The creation of the typeIdx may have changed the classIdx, search again!
+    classIdx = findOrCreateTypeId(className);
+
+    // Search for (or create) the string representing the field name.
+    int fieldNameStringIdx = findOrCreateString(fieldName);
+
+    // Create FieldIdItem.
+    FieldIdItem newFieldId = new FieldIdItem();
+    newFieldId.classIdx = (short) classIdx;
+    newFieldId.typeIdx = (short) typeIdx;
+    newFieldId.nameIdx = fieldNameStringIdx;
+
+    // FieldIds must be ordered.
+    int newFieldIdIdx = findFieldIdInsertionPoint(className, typeName, fieldName);
+
+    rawDexFile.fieldIds.add(newFieldIdIdx, newFieldId);
+
+    // Insert into OffsetTracker.
+    if (newFieldIdIdx == 0 && rawDexFile.fieldIds.size() == 1) {
+      // Special case: we didn't have any fields before!
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstEverField(newFieldId, rawDexFile);
+    } else if (newFieldIdIdx == 0) {
+      rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newFieldId, rawDexFile);
+    } else {
+      FieldIdItem prevFieldId = rawDexFile.fieldIds.get(newFieldIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newFieldId, prevFieldId);
+    }
+
+    Log.info(String.format("Created new FieldIdItem for %s %s %s, index: 0x%04x",
+        className, typeName, fieldName, newFieldIdIdx));
+
+    // Now that we've potentially moved a lot of field IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.FIELD_ID, newFieldIdIdx);
+
+    // All done, return the index for the new field.
+    return newFieldIdIdx;
+  }
+
+  private int findFieldId(String className, String typeName, String fieldName) {
+    int classIdx = findTypeId(className);
+    if (classIdx == -1) {
+      return -1;
+    }
+    int typeIdx = findTypeId(typeName);
+    if (typeIdx == -1) {
+      return -1;
+    }
+    int nameIdx = findString(fieldName);
+    if (nameIdx == -1) {
+      return -1;
+    }
+
+    int fieldIdIdx = 0;
+    for (FieldIdItem fieldId : rawDexFile.fieldIds) {
+      if (classIdx == fieldId.classIdx
+          && typeIdx == fieldId.typeIdx
+          && nameIdx == fieldId.nameIdx) {
+        return fieldIdIdx;
+      }
+      fieldIdIdx++;
+    }
+    return -1;
+  }
+
+  /**
+   * Given a field's fully qualified class name, type name, and name,
+   * either find the FieldId in our DEX file's table, or create it.
+   */
+  public int findOrCreateFieldId(String className, String typeName, String fieldName) {
+    int fieldIdx = findFieldId(className, typeName, fieldName);
+    if (fieldIdx != -1) {
+      return fieldIdx;
+    }
+    return createFieldId(className, typeName, fieldName);
+  }
+
+  /**
+   * Returns a 1 or 2 element String[]. If 1 element, the only element is the return type
+   * part of the signature. If 2 elements, the first is the parameters, the second is
+   * the return type.
+   */
+  private String[] convertSignatureToParametersAndReturnType(String signature) {
+    if (signature.charAt(0) != '(' || !signature.contains(")")) {
+      Log.errorAndQuit("Invalid signature: " + signature);
+    }
+    String[] elems = signature.substring(1).split("\\)");
+    return elems;
+  }
+
+  private String[] convertSignatureToParameterList(String signature) {
+    String[] elems = convertSignatureToParametersAndReturnType(signature);
+    String parameters = "";
+    if (elems.length == 2) {
+      parameters = elems[0];
+    }
+
+    List<String> parameterList = new ArrayList<String>();
+
+    int typePointer = 0;
+    while (typePointer != parameters.length()) {
+      if (elems[0].charAt(typePointer) == 'L') {
+        int start = typePointer;
+        // Read up to the next ;
+        while (elems[0].charAt(typePointer) != ';') {
+          typePointer++;
+        }
+        parameterList.add(parameters.substring(start, typePointer + 1));
+      } else {
+        parameterList.add(Character.toString(parameters.charAt(typePointer)));
+      }
+      typePointer++;
+    }
+
+    return parameterList.toArray(new String[]{});
+  }
+
+  private String convertSignatureToReturnType(String signature) {
+    String[] elems = convertSignatureToParametersAndReturnType(signature);
+    String returnType = "";
+    if (elems.length == 1) {
+      returnType = elems[0];
+    } else {
+      returnType = elems[1];
+    }
+
+    return returnType;
+  }
+
+  private String convertSignatureToShorty(String signature) {
+    String[] elems = convertSignatureToParametersAndReturnType(signature);
+
+    StringBuilder shortyBuilder = new StringBuilder();
+
+    String parameters = "";
+    String returnType = "";
+
+    if (elems.length == 1) {
+      shortyBuilder.append("V");
+    } else {
+      parameters = elems[0];
+      returnType = elems[1];
+      char returnChar = returnType.charAt(0);
+      // Arrays are references in shorties.
+      if (returnChar == '[') {
+        returnChar = 'L';
+      }
+      shortyBuilder.append(returnChar);
+    }
+
+    int typePointer = 0;
+    while (typePointer != parameters.length()) {
+      if (parameters.charAt(typePointer) == 'L') {
+        shortyBuilder.append('L');
+        // Read up to the next ;
+        while (parameters.charAt(typePointer) != ';') {
+          typePointer++;
+          if (typePointer == parameters.length()) {
+            Log.errorAndQuit("Illegal type specified in signature - L with no ;!");
+          }
+        }
+      } else if (parameters.charAt(typePointer) == '[') {
+        // Arrays are references in shorties.
+        shortyBuilder.append('L');
+        // Read past all the [s
+        while (parameters.charAt(typePointer) == '[') {
+          typePointer++;
+        }
+        if (parameters.charAt(typePointer) == 'L') {
+          // Read up to the next ;
+          while (parameters.charAt(typePointer) != ';') {
+            typePointer++;
+            if (typePointer == parameters.length()) {
+              Log.errorAndQuit("Illegal type specified in signature - L with no ;!");
+            }
+          }
+        }
+      } else {
+        shortyBuilder.append(parameters.charAt(typePointer));
+      }
+
+      typePointer++;
+    }
+
+    return shortyBuilder.toString();
+  }
+
+  private Integer[] convertParameterListToTypeIdList(String[] parameterList) {
+    List<Integer> typeIdList = new ArrayList<Integer>();
+    for (String parameter : parameterList) {
+      int typeIdx = findTypeId(parameter);
+      if (typeIdx == -1) {
+        return null;
+      }
+      typeIdList.add(typeIdx);
+    }
+    return typeIdList.toArray(new Integer[]{});
+  }
+
+  private TypeList createTypeList(String[] parameterList) {
+    TypeList typeList = new TypeList();
+    List<TypeItem> typeItemList = new ArrayList<TypeItem>();
+
+    // This must be done as two passes, one to create all the types,
+    // and then one to put them in the type list.
+    for (String parameter : parameterList) {
+      findOrCreateTypeId(parameter);
+    }
+
+    // Now actually put them in the list.
+    for (String parameter : parameterList) {
+      TypeItem typeItem = new TypeItem();
+      typeItem.typeIdx = (short) findOrCreateTypeId(parameter);
+      typeItemList.add(typeItem);
+    }
+    typeList.list = typeItemList.toArray(new TypeItem[]{});
+    typeList.size = typeItemList.size();
+
+    // Insert into OffsetTracker.
+    if (rawDexFile.typeLists == null) {
+      // Special case: we didn't have any fields before!
+      Log.info("Need to create first type list.");
+      rawDexFile.typeLists = new ArrayList<TypeList>();
+      rawDexFile.getOffsetTracker()
+        .insertNewOffsettableAsFirstEverTypeList(typeList, rawDexFile);
+    } else {
+      TypeList prevTypeList =
+          rawDexFile.typeLists.get(rawDexFile.typeLists.size() - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(typeList, prevTypeList);
+    }
+
+    // Finally, add this new TypeList to the list of them.
+    rawDexFile.typeLists.add(typeList);
+
+    return typeList;
+  }
+
+  private TypeList findTypeList(String[] parameterList) {
+    Integer[] typeIdList = convertParameterListToTypeIdList(parameterList);
+    if (typeIdList == null) {
+      return null;
+    }
+
+    if (rawDexFile.typeLists == null) {
+      // There's no type lists yet!
+      return null;
+    }
+
+    for (TypeList typeList : rawDexFile.typeLists) {
+      if (typeList.size != typeIdList.length) {
+        continue;
+      }
+
+      boolean found = true;
+      int idx = 0;
+      for (TypeItem typeItem : typeList.list) {
+        if (typeItem.typeIdx != typeIdList[idx]) {
+          found = false;
+          break;
+        }
+        idx++;
+      }
+      if (found && idx == parameterList.length) {
+        return typeList;
+      }
+    }
+
+    return null;
+  }
+
+  private TypeList findOrCreateTypeList(String[] parameterList) {
+    TypeList typeList = findTypeList(parameterList);
+    if (typeList != null) {
+      return typeList;
+    }
+    return createTypeList(parameterList);
+  }
+
+  private int createProtoId(String signature) {
+    String shorty = convertSignatureToShorty(signature);
+    String returnType = convertSignatureToReturnType(signature);
+    String[] parameterList = convertSignatureToParameterList(signature);
+
+    if (rawDexFile.protoIds.size() >= 65536) {
+      Log.errorAndQuit("Referenced too many protos for the DEX file.");
+    }
+
+    TypeList typeList = null;
+    Offsettable typeListOffsettable = null;
+
+    if (parameterList.length > 0) {
+      // Search for (or create) the parameter list.
+      typeList = findOrCreateTypeList(parameterList);
+
+      typeListOffsettable =
+          rawDexFile.getOffsetTracker().getOffsettableForItem(typeList);
+    }
+
+    // Search for (or create) the return type.
+    int returnTypeIdx = findOrCreateTypeId(returnType);
+
+    // Search for (or create) the shorty string.
+    int shortyIdx = findOrCreateString(shorty);
+
+    // Create ProtoIdItem.
+    ProtoIdItem newProtoId = new ProtoIdItem();
+    newProtoId.shortyIdx = shortyIdx;
+    newProtoId.returnTypeIdx = returnTypeIdx;
+    newProtoId.parametersOff = new Offset(false);
+    if (parameterList.length > 0) {
+      newProtoId.parametersOff.pointToNew(typeListOffsettable);
+    }
+
+    // ProtoIds must be ordered.
+    int newProtoIdIdx = findProtoIdInsertionPoint(signature);
+
+    rawDexFile.protoIds.add(newProtoIdIdx, newProtoId);
+
+    // Insert into OffsetTracker.
+    if (newProtoIdIdx == 0) {
+      rawDexFile.getOffsetTracker().insertNewOffsettableAsFirstOfType(newProtoId, rawDexFile);
+    } else {
+      ProtoIdItem prevProtoId = rawDexFile.protoIds.get(newProtoIdIdx - 1);
+      rawDexFile.getOffsetTracker().insertNewOffsettableAfter(newProtoId, prevProtoId);
+    }
+
+    Log.info(String.format("Created new ProtoIdItem for %s, index: 0x%04x",
+        signature, newProtoIdIdx));
+
+    // Now that we've potentially moved a lot of proto IDs along, all references
+    // to them need to be updated.
+    rawDexFile.incrementIndex(IndexUpdateKind.PROTO_ID, newProtoIdIdx);
+
+    // All done, return the index for the new proto.
+    return newProtoIdIdx;
+  }
+
+  private int findProtoId(String signature) {
+    String shorty = convertSignatureToShorty(signature);
+    String returnType = convertSignatureToReturnType(signature);
+    String[] parameterList = convertSignatureToParameterList(signature);
+
+    int shortyIdx = findString(shorty);
+    if (shortyIdx == -1) {
+      return -1;
+    }
+    int returnTypeIdx = findTypeId(returnType);
+    if (returnTypeIdx == -1) {
+      return -1;
+    }
+
+    // Only look for a TypeList if there's a parameter list.
+    TypeList typeList = null;
+    if (parameterList.length > 0) {
+      typeList = findTypeList(parameterList);
+      if (typeList == null) {
+        return -1;
+      }
+    }
+
+    int protoIdIdx = 0;
+    for (ProtoIdItem protoId : rawDexFile.protoIds) {
+      if (parameterList.length > 0) {
+        // With parameters.
+        if (shortyIdx == protoId.shortyIdx
+            && returnTypeIdx == protoId.returnTypeIdx
+            && typeList.equals(protoId.parametersOff.getPointedToItem())) {
+          return protoIdIdx;
+        }
+      } else {
+        // Without parameters.
+        if (shortyIdx == protoId.shortyIdx
+            && returnTypeIdx == protoId.returnTypeIdx) {
+          return protoIdIdx;
+        }
+      }
+      protoIdIdx++;
+    }
+    return -1;
+  }
+
+  private int findOrCreateProtoId(String signature) {
+    int protoIdx = findProtoId(signature);
+    if (protoIdx != -1) {
+      return protoIdx;
+    }
+    return createProtoId(signature);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java b/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java
new file mode 100644
index 0000000..ea66844
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MBranchInsn.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+/**
+ * A subclass of the MInsn, that tracks its target instruction.
+ */
+public class MBranchInsn extends MInsn {
+  /**
+   * The MInsn this branch instruction branches to.
+   */
+  public MInsn target;
+
+  /**
+   * Clone this MBranchInsn, and clone the wrapped Instruction.
+   */
+  public MBranchInsn clone() {
+    MBranchInsn newInsn = new MBranchInsn();
+    newInsn.insn = insn.clone();
+    newInsn.target = target;
+    return newInsn;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MInsn.java b/tools/dexfuzz/src/dexfuzz/program/MInsn.java
new file mode 100644
index 0000000..10f7755
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MInsn.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.rawdex.Instruction;
+
+/**
+ * Base class that is a thin wrapper for Instructions currently, also tracking location
+ * as the instruction is moved around.
+ */
+public class MInsn {
+  /**
+   * The raw DEX instruction that this instruction represents.
+   */
+  public Instruction insn;
+
+
+  /**
+   * The location of this instruction, as an offset in code words from the beginning.
+   * May become invalid if instructions around it are mutated.
+   */
+  public int location;
+
+  /**
+   * Denotes if the currently associated location can be trusted.
+   */
+  public boolean locationUpdated;
+
+  /**
+   * Clone this MInsn, and clone the wrapped Instruction.
+   */
+  public MInsn clone() {
+    MInsn newInsn = new MInsn();
+    newInsn.insn = insn.clone();
+    // It is the responsibility of the cloner to update these values.
+    newInsn.location = location;
+    newInsn.locationUpdated = locationUpdated;
+    return newInsn;
+  }
+
+  /**
+   * Get the String representation of an instruction.
+   */
+  public String toString() {
+    return String.format("{0x%04x%s: %s}",
+        location,
+        (locationUpdated) ? "!" : "",
+            insn.toString());
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java b/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java
new file mode 100644
index 0000000..ffed883
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MInsnWithData.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+/**
+ * A subclass of the MInsn, that tracks the data instruction.
+ */
+public class MInsnWithData extends MInsn {
+  /**
+   * The MInsn that represents the data this instruction uses.
+   */
+  public MInsn dataTarget;
+
+  /**
+   * Clone this MInsnWithData, and clone the wrapped Instruction.
+   */
+  public MInsnWithData clone() {
+    MInsnWithData newInsn = new MInsnWithData();
+    newInsn.insn = insn.clone();
+    newInsn.dataTarget = dataTarget;
+    return newInsn;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java b/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java
new file mode 100644
index 0000000..d8693fe
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MSwitchInsn.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A subclass of the MInsnWithData, that also has multiple jump targets.
+ */
+public class MSwitchInsn extends MInsnWithData {
+  /**
+   * The MInsns this switch instruction branches to.
+   */
+  public List<MInsn> targets = new LinkedList<MInsn>();
+
+  public boolean packed;
+
+  public int[] keys;
+
+  /**
+   * Clone this MSwitchInsn, and clone the wrapped Instruction.
+   */
+  public MSwitchInsn clone() {
+    MSwitchInsn newInsn = new MSwitchInsn();
+    newInsn.insn = insn.clone();
+    newInsn.dataTarget = dataTarget;
+    newInsn.packed = packed;
+    for (MInsn target : targets) {
+      newInsn.targets.add(target);
+    }
+    newInsn.keys = new int[keys.length];
+    System.arraycopy(keys, 0, newInsn.keys, 0, keys.length);
+    return newInsn;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/MTryBlock.java b/tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
new file mode 100644
index 0000000..a1dc029
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MTryBlock.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import java.util.List;
+
+/**
+ * Tracks where try blocks start and end.
+ */
+public class MTryBlock {
+  public MInsn startInsn;
+  public MInsn endInsn;
+  public List<MInsn> handlers;
+  public MInsn catchAllHandler;
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java b/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java
new file mode 100644
index 0000000..c56b1bc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MutatableCode.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * A class that represents a CodeItem in a way that is more amenable to mutation.
+ */
+public class MutatableCode {
+  /**
+   * To ensure we update the correct CodeItem in the raw DEX file.
+   */
+  public int codeItemIdx;
+
+  /**
+   * This is an index into the Program's list of MutatableCodes.
+   */
+  public int mutatableCodeIdx;
+
+  /**
+   * Number of registers this code uses.
+   */
+  public short registersSize;
+
+  /**
+   * Number of ins this code has.
+   */
+  public short insSize;
+
+  /**
+   * Number of outs this code has.
+   */
+  public short outsSize;
+
+  /**
+   * Number of tries this code has.
+   */
+  public short triesSize;
+
+  /**
+   * CodeTranslator is responsible for creating this, and
+   * converting it back to a list of Instructions.
+   */
+  private List<MInsn> mutatableInsns;
+
+  /**
+   * CodeTranslator is responsible for creating this, and
+   * converting it back to the correct form for CodeItems.
+   */
+  public List<MTryBlock> mutatableTries;
+
+  /**
+   * The name of the method this code represents.
+   */
+  public String name;
+  public String shorty;
+  public boolean isStatic;
+
+  /**
+   * The Program that owns this MutatableCode.
+   * Currently used to get the size of constant pools for
+   * PoolIndexChanger/RandomInstructionGenerator
+   */
+  public Program program;
+
+  private short originalInVReg;
+  private short tempVRegsAllocated;
+  private short initialTempVReg;
+  private boolean vregsNeedCopying;
+  private int numMoveInsnsGenerated;
+
+  public MutatableCode(Program program) {
+    this.program = program;
+    this.mutatableInsns = new LinkedList<MInsn>();
+  }
+
+  /**
+   * Call this to update all instructions after the provided mInsn, to have their
+   * locations adjusted by the provided offset. It will also mark that they have been updated.
+   */
+  public void updateInstructionLocationsAfter(MInsn mInsn, int offset) {
+    boolean updating = false;
+    for (MInsn mInsnChecking : mutatableInsns) {
+      if (updating) {
+        mInsnChecking.locationUpdated = true;
+        mInsnChecking.location += offset;
+      } else {
+        if (mInsnChecking == mInsn) {
+          updating = true;
+        }
+      }
+
+    }
+  }
+
+  private void recalculateLocations() {
+    int loc = 0;
+    for (MInsn mInsn : mutatableInsns) {
+      mInsn.location = loc;
+      loc += mInsn.insn.getSize();
+    }
+  }
+
+  public List<MInsn> getInstructions() {
+    return Collections.unmodifiableList(mutatableInsns);
+  }
+
+  public int getInstructionCount() {
+    return mutatableInsns.size();
+  }
+
+  public int getInstructionIndex(MInsn mInsn) {
+    return mutatableInsns.indexOf(mInsn);
+  }
+
+  public MInsn getInstructionAt(int idx) {
+    return mutatableInsns.get(idx);
+  }
+
+  public void addInstructionToEnd(MInsn mInsn) {
+    mutatableInsns.add(mInsn);
+  }
+
+  public void insertInstructionAfter(MInsn toBeInserted, int insertionIdx) {
+    if ((insertionIdx + 1) < mutatableInsns.size()) {
+      insertInstructionAt(toBeInserted, insertionIdx + 1);
+    } else {
+      // Appending to end.
+      MInsn finalInsn = mutatableInsns.get(mutatableInsns.size() - 1);
+      toBeInserted.location = finalInsn.location + finalInsn.insn.getSize();
+      mutatableInsns.add(toBeInserted);
+    }
+  }
+
+  /**
+   * Has same semantics as List.add()
+   */
+  public void insertInstructionAt(MInsn toBeInserted, int insertionIdx) {
+    MInsn currentInsn = mutatableInsns.get(insertionIdx);
+    toBeInserted.location = currentInsn.location;
+    mutatableInsns.add(insertionIdx , toBeInserted);
+    updateInstructionLocationsAfter(toBeInserted, toBeInserted.insn.getSize());
+  }
+
+  /**
+   * Checks if any MTryBlock's instruction refs pointed at the 'before' MInsn,
+   * and points them to the 'after' MInsn, if so. 'twoWay' will check if 'after'
+   * was pointed to, and point refs to the 'before' insn.
+   * (one-way is used when deleting instructions,
+   * two-way is used when swapping instructions.)
+   */
+  private void updateTryBlocksWithReplacementInsn(MInsn before, MInsn after,
+      boolean twoWay) {
+    if (triesSize > 0) {
+      for (MTryBlock mTryBlock : mutatableTries) {
+        if (mTryBlock.startInsn == before) {
+          Log.debug("Try block's first instruction was updated");
+          mTryBlock.startInsn = after;
+        } else if (twoWay && mTryBlock.startInsn == after) {
+          Log.debug("Try block's first instruction was updated");
+          mTryBlock.startInsn = before;
+        }
+        if (mTryBlock.endInsn == before) {
+          Log.debug("Try block's last instruction was updated");
+          mTryBlock.endInsn = after;
+        } else if (twoWay && mTryBlock.endInsn == after) {
+          Log.debug("Try block's last instruction was updated");
+          mTryBlock.endInsn = before;
+        }
+        if (mTryBlock.catchAllHandler == before) {
+          Log.debug("Try block's catch-all instruction was updated");
+          mTryBlock.catchAllHandler = after;
+        } else if (twoWay && mTryBlock.catchAllHandler == after) {
+          Log.debug("Try block's catch-all instruction was updated");
+          mTryBlock.catchAllHandler = before;
+        }
+
+        List<Integer> matchesIndicesToChange = new ArrayList<Integer>();
+        List<Integer> replacementIndicesToChange = null;
+        if (twoWay) {
+          replacementIndicesToChange = new ArrayList<Integer>();
+        }
+
+        int idx = 0;
+        for (MInsn handler : mTryBlock.handlers) {
+          if (handler == before) {
+            matchesIndicesToChange.add(idx);
+            Log.debug("Try block's handler instruction was updated");
+          } else if (twoWay && handler == after) {
+            replacementIndicesToChange.add(idx);
+            Log.debug("Try block's handler instruction was updated");
+          }
+          idx++;
+        }
+
+        for (int idxToChange : matchesIndicesToChange) {
+          mTryBlock.handlers.set(idxToChange, after);
+        }
+
+        if (twoWay) {
+          for (int idxToChange : replacementIndicesToChange) {
+            mTryBlock.handlers.set(idxToChange, before);
+          }
+        }
+      }
+    }
+  }
+
+  /**
+   * The actual implementation of deleteInstruction called by
+   * the single-argument deleteInstructions.
+   */
+  private void deleteInstructionFull(MInsn toBeDeleted, int toBeDeletedIdx) {
+    // Make sure we update all locations afterwards first.
+    updateInstructionLocationsAfter(toBeDeleted, -(toBeDeleted.insn.getSize()));
+
+    // Remove it.
+    mutatableInsns.remove(toBeDeletedIdx);
+
+    // Update any branch instructions that branched to the instruction we just deleted!
+
+    // First, pick the replacement target.
+    int replacementTargetIdx = toBeDeletedIdx;
+    if (replacementTargetIdx == mutatableInsns.size()) {
+      replacementTargetIdx--;
+    }
+    MInsn replacementTarget = mutatableInsns.get(replacementTargetIdx);
+
+    for (MInsn mInsn : mutatableInsns) {
+      if (mInsn instanceof MBranchInsn) {
+        // Check if this branch insn points at the insn we just deleted.
+        MBranchInsn branchInsn = (MBranchInsn) mInsn;
+        MInsn target = branchInsn.target;
+        if (target == toBeDeleted) {
+          Log.debug(branchInsn + " was pointing at the deleted instruction, updated.");
+          branchInsn.target = replacementTarget;
+        }
+      } else if (mInsn instanceof MSwitchInsn) {
+        // Check if any of this switch insn's targets points at the insn we just deleted.
+        MSwitchInsn switchInsn = (MSwitchInsn) mInsn;
+        List<Integer> indicesToChange = new ArrayList<Integer>();
+        int idx = 0;
+        for (MInsn target : switchInsn.targets) {
+          if (target == toBeDeleted) {
+            indicesToChange.add(idx);
+            Log.debug(switchInsn + "[" + idx
+                + "] was pointing at the deleted instruction, updated.");
+          }
+          idx++;
+        }
+        for (int idxToChange : indicesToChange) {
+          switchInsn.targets.remove(idxToChange);
+          switchInsn.targets.add(idxToChange, replacementTarget);
+        }
+      }
+    }
+
+    // Now update the try blocks.
+    updateTryBlocksWithReplacementInsn(toBeDeleted, replacementTarget, false);
+  }
+
+  /**
+   * Delete the provided MInsn.
+   */
+  public void deleteInstruction(MInsn toBeDeleted) {
+    deleteInstructionFull(toBeDeleted, mutatableInsns.indexOf(toBeDeleted));
+  }
+
+  /**
+   * Delete the MInsn at the provided index.
+   */
+  public void deleteInstruction(int toBeDeletedIdx) {
+    deleteInstructionFull(mutatableInsns.get(toBeDeletedIdx), toBeDeletedIdx);
+  }
+
+  public void swapInstructionsByIndex(int aIdx, int bIdx) {
+    MInsn aInsn = mutatableInsns.get(aIdx);
+    MInsn bInsn = mutatableInsns.get(bIdx);
+
+    mutatableInsns.set(aIdx, bInsn);
+    mutatableInsns.set(bIdx, aInsn);
+
+    updateTryBlocksWithReplacementInsn(aInsn, bInsn, true);
+
+    recalculateLocations();
+  }
+
+  /**
+   * Some mutators may require the use of temporary registers. For instance,
+   * to easily add in printing of values without having to look for registers
+   * that aren't currently live.
+   * The idea is to allocate these registers at the top of the set of registers.
+   * Because this will then shift where the arguments to the method are, we then
+   * change the start of the method to copy the arguments to the method
+   * into the place where the rest of the method's code expects them to be.
+   * Call allocateTemporaryVRegs(n), then use getTemporaryVReg(n),
+   * and then make sure finishedUsingTemporaryVRegs() is called!
+   */
+  public void allocateTemporaryVRegs(int count) {
+    if (count > tempVRegsAllocated) {
+      if (tempVRegsAllocated == 0) {
+        Log.info("Allocating temporary vregs for method...");
+        initialTempVReg = registersSize;
+        originalInVReg = (short) (registersSize - insSize);
+      } else {
+        Log.info("Extending allocation of temporary vregs for method...");
+      }
+      registersSize = (short) (initialTempVReg + count);
+      if (outsSize < count) {
+        outsSize = (short) count;
+      }
+      vregsNeedCopying = true;
+      tempVRegsAllocated = (short) count;
+    }
+  }
+
+  public int getTemporaryVReg(int number) {
+    if (number >= tempVRegsAllocated) {
+      Log.errorAndQuit("Not allocated enough temporary vregs!");
+    }
+    return initialTempVReg + number;
+  }
+
+  public void finishedUsingTemporaryVRegs() {
+    if (tempVRegsAllocated > 0 && vregsNeedCopying) {
+      // Just delete all the move instructions and generate again, if we already have some.
+      while (numMoveInsnsGenerated > 0) {
+        deleteInstruction(0);
+        numMoveInsnsGenerated--;
+      }
+
+      Log.info("Moving 'in' vregs to correct locations after allocating temporary vregs");
+
+      int shortyIdx = 0;
+      if (isStatic) {
+        shortyIdx = 1;
+      }
+
+      int insertionCounter = 0;
+
+      // Insert copy insns that move all the in VRs down.
+      for (int i = 0; i < insSize; i++) {
+        MInsn moveInsn = new MInsn();
+        moveInsn.insn = new Instruction();
+        moveInsn.insn.vregA = originalInVReg + i;
+        moveInsn.insn.vregB = originalInVReg + i + tempVRegsAllocated;
+
+        char type = 'L';
+        if (shortyIdx > 0) {
+          type = shorty.charAt(shortyIdx);
+        }
+        shortyIdx++;
+
+        if (type == 'L') {
+          moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
+        } else if (type == 'D' || type == 'J') {
+          moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
+          i++;
+        } else {
+          moveInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
+        }
+
+        insertInstructionAt(moveInsn, insertionCounter);
+        insertionCounter++;
+        Log.info("Temp vregs creation, Added instruction " + moveInsn);
+        numMoveInsnsGenerated++;
+      }
+
+      vregsNeedCopying = false;
+    }
+  }
+
+  /**
+   * When we insert new Field/Type/MethodIds into the DEX file, this may shunt some Ids
+   * into a new position in the table. If this happens, every reference to the Ids must
+   * be updated! Because CodeItems have their Instructions wrapped into a graph of MInsns
+   * during mutation, they don't have a view of all their instructions during mutation,
+   * and so if they are asked to update their instructions' indices into the tables, they
+   * must call this method to get the actual list of instructions they currently own.
+   */
+  public List<Instruction> requestLatestInstructions() {
+    List<Instruction> latestInsns = new ArrayList<Instruction>();
+    for (MInsn mInsn : mutatableInsns) {
+      latestInsns.add(mInsn.insn);
+    }
+    return latestInsns;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/Mutation.java b/tools/dexfuzz/src/dexfuzz/program/Mutation.java
new file mode 100644
index 0000000..2eba718
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/Mutation.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.program.mutators.CodeMutator;
+
+/**
+ * Mutation should be subclassed by an AssociatedMutation in each CodeMutator,
+ * which will describe the parameters of the mutation, and override the getString()
+ * and parseString() methods here to allow serialization of the mutations.
+ */
+public abstract class Mutation {
+
+  public MutatableCode mutatableCode;
+
+  // The first field of any serialized mutation - the mutator that uses it.
+  public Class<? extends CodeMutator> mutatorClass;
+  // The second field of any serialized mutation...
+  // This is an index into the Program's list of MutatableCodes
+  // i.e., it is NOT an index into the DEX file's CodeItems!
+  public int mutatableCodeIdx;
+
+  public void setup(Class<? extends CodeMutator> mutatorClass, MutatableCode mutatableCode) {
+    this.mutatorClass = mutatorClass;
+    this.mutatableCode = mutatableCode;
+    this.mutatableCodeIdx = mutatableCode.mutatableCodeIdx;
+  }
+
+  public abstract String getString();
+
+  public abstract void parseString(String[] elements);
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java b/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java
new file mode 100644
index 0000000..7f79517
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/MutationSerializer.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.program.mutators.CodeMutator;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.IOException;
+
+/**
+ * Responsible for serializing mutations, allowing replay of mutations, and searching
+ * for a minimal set of mutations.
+ */
+public class MutationSerializer {
+  public static String getMutationString(Mutation mutation) {
+    StringBuilder builder = new StringBuilder();
+    builder.append(mutation.mutatorClass.getCanonicalName()).append(" ");
+    builder.append(mutation.mutatableCodeIdx).append(" ");
+    builder.append(mutation.getString());
+    return builder.toString();
+  }
+
+  public static void writeMutation(BufferedWriter writer, Mutation mutation) throws IOException {
+    // Write out the common fields.
+    writer.write(mutation.mutatorClass.getCanonicalName() + " "
+        + mutation.mutatableCodeIdx + " ");
+
+    // Use the mutation's own function to write out the rest of the fields.
+    writer.write(mutation.getString() + "\n");
+  }
+
+  @SuppressWarnings("unchecked")
+  public static Mutation readMutation(BufferedReader reader) throws IOException {
+    String line = reader.readLine();
+    String[] fields = null;
+    if (line != null) {
+      fields = line.split(" ");
+    } else {
+      Log.errorAndQuit("Could not read line during mutation loading.");
+    }
+
+    // Read the mutator's class name
+    String mutatorClassName = fields[0];
+
+    // Get the class for that mutator
+    Class<? extends CodeMutator> mutatorClass = null;
+    try {
+      mutatorClass = (Class<? extends CodeMutator>) Class.forName(mutatorClassName);
+    } catch (ClassNotFoundException e) {
+      Log.errorAndQuit("Cannot find a mutator class called: " + mutatorClassName);
+    }
+
+    Mutation mutation = null;
+    try {
+      mutation = mutatorClass.newInstance().getNewMutation();
+    } catch (InstantiationException e) {
+      Log.errorAndQuit("Unable to instantiate " + mutatorClassName
+          + " using default constructor.");
+    } catch (IllegalAccessException e) {
+      Log.errorAndQuit("Unable to access methods in " + mutatorClassName + ".");
+    }
+
+    if (mutation == null) {
+      Log.errorAndQuit("Unable to get Mutation for Mutator: " + mutatorClassName);
+    }
+
+    // Populate the common fields of the mutation.
+    mutation.mutatorClass = mutatorClass;
+    // The Program must set this later, using the mutatable_code_idx
+    //   into its list of MutatableCodes.
+    mutation.mutatableCode = null;
+    mutation.mutatableCodeIdx = Integer.parseInt(fields[1]);
+
+    // Use the mutation's own method to read the rest of the fields.
+    mutation.parseString(fields);
+
+    return mutation;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/Program.java b/tools/dexfuzz/src/dexfuzz/program/Program.java
new file mode 100644
index 0000000..286fe52
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/Program.java
@@ -0,0 +1,602 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.Options;
+import dexfuzz.listeners.BaseListener;
+import dexfuzz.program.mutators.ArithOpChanger;
+import dexfuzz.program.mutators.BranchShifter;
+import dexfuzz.program.mutators.CmpBiasChanger;
+import dexfuzz.program.mutators.CodeMutator;
+import dexfuzz.program.mutators.ConstantValueChanger;
+import dexfuzz.program.mutators.ConversionRepeater;
+import dexfuzz.program.mutators.FieldFlagChanger;
+import dexfuzz.program.mutators.InstructionDeleter;
+import dexfuzz.program.mutators.InstructionDuplicator;
+import dexfuzz.program.mutators.InstructionSwapper;
+import dexfuzz.program.mutators.NewMethodCaller;
+import dexfuzz.program.mutators.NonsenseStringPrinter;
+import dexfuzz.program.mutators.PoolIndexChanger;
+import dexfuzz.program.mutators.RandomInstructionGenerator;
+import dexfuzz.program.mutators.SwitchBranchShifter;
+import dexfuzz.program.mutators.TryBlockShifter;
+import dexfuzz.program.mutators.ValuePrinter;
+import dexfuzz.program.mutators.VRegChanger;
+import dexfuzz.rawdex.ClassDataItem;
+import dexfuzz.rawdex.ClassDefItem;
+import dexfuzz.rawdex.CodeItem;
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.EncodedField;
+import dexfuzz.rawdex.EncodedMethod;
+import dexfuzz.rawdex.FieldIdItem;
+import dexfuzz.rawdex.MethodIdItem;
+import dexfuzz.rawdex.ProtoIdItem;
+import dexfuzz.rawdex.RawDexFile;
+import dexfuzz.rawdex.TypeIdItem;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+
+import java.io.BufferedReader;
+import java.io.BufferedWriter;
+import java.io.FileReader;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+/**
+ * After the raw DEX file has been parsed, it is passed into this class
+ * that represents the program in a mutatable form.
+ * The class uses a CodeTranslator to translate between the raw DEX form
+ * for a method, and the mutatable form. It also controls all CodeMutators,
+ * deciding which ones should be applied to each CodeItem.
+ */
+public class Program {
+  /**
+   * The RNG used during mutation.
+   */
+  private Random rng;
+
+  /**
+   * The seed that was given to the RNG.
+   */
+  public long rngSeed;
+
+  /**
+   * The parsed raw DEX file.
+   */
+  private RawDexFile rawDexFile;
+
+  /**
+   * The system responsible for translating from CodeItems to MutatableCode and vice-versa.
+   */
+  private CodeTranslator translator;
+
+  /**
+   * Responsible for adding new class ID items, method ID items, etc.
+   */
+  private IdCreator idCreator;
+
+  /**
+   * A list of all the MutatableCode that the CodeTranslator produced from
+   * CodeItems that are acceptable to mutate.
+   */
+  private List<MutatableCode> mutatableCodes;
+
+  /**
+   * A list of all MutatableCode items that were mutated when mutateTheProgram()
+   * was called. updateRawDexFile() will update the relevant CodeItems when called,
+   * and then clear this list.
+   */
+  private List<MutatableCode> mutatedCodes;
+
+  /**
+   * A list of all registered CodeMutators that this Program can use to mutate methods.
+   */
+  private List<CodeMutator> mutators;
+
+  /**
+   * Used if we're loading mutations from a file, so we can find the correct mutator.
+   */
+  private Map<Class<? extends CodeMutator>, CodeMutator> mutatorsLookupByClass;
+
+  /**
+   * Tracks mutation stats.
+   */
+  private MutationStats mutationStats;
+
+  /**
+   * A list of all mutations used for loading/dumping mutations from/to a file.
+   */
+  private List<Mutation> mutations;
+
+  /**
+   * The listener who is interested in events.
+   * May be a listener that is responsible for multiple listeners.
+   */
+  private BaseListener listener;
+
+  /**
+   * Given a maximum number of mutations that can be performed on a method, n,
+   * give up after attempting (n * this value) mutations for any method.
+   */
+  private static final int MAXIMUM_MUTATION_ATTEMPT_FACTOR = 10;
+
+  /**
+   * Construct the mutatable Program based on the raw DEX file that was parsed initially.
+   */
+  public Program(RawDexFile rawDexFile, List<Mutation> previousMutations,
+      BaseListener listener) {
+    this.listener = listener;
+
+    idCreator = new IdCreator(rawDexFile);
+
+    // Set up the RNG.
+    rng = new Random();
+    if (Options.usingProvidedSeed) {
+      rng.setSeed(Options.rngSeed);
+      rngSeed = Options.rngSeed;
+    } else {
+      long seed = System.currentTimeMillis();
+      listener.handleSeed(seed);
+      rng.setSeed(seed);
+      rngSeed = seed;
+    }
+
+    if (previousMutations != null) {
+      mutations = previousMutations;
+    } else {
+      // Allocate the mutations list.
+      mutations = new ArrayList<Mutation>();
+
+      // Read in the mutations if we need to.
+      if (Options.loadMutations) {
+        // Allocate the mutators lookup table.
+        mutatorsLookupByClass = new HashMap<Class<? extends CodeMutator>, CodeMutator>();
+        loadMutationsFromDisk(Options.loadMutationsFile);
+      }
+    }
+
+    // Allocate the mutators list.
+    mutators = new ArrayList<CodeMutator>();
+
+    this.rawDexFile = rawDexFile;
+
+    mutatableCodes = new ArrayList<MutatableCode>();
+    mutatedCodes = new ArrayList<MutatableCode>();
+
+    translator = new CodeTranslator();
+
+    mutationStats = new MutationStats();
+
+    // Register all the code mutators here.
+    registerMutator(new ArithOpChanger(rng, mutationStats, mutations));
+    registerMutator(new BranchShifter(rng, mutationStats, mutations));
+    registerMutator(new CmpBiasChanger(rng, mutationStats, mutations));
+    registerMutator(new ConstantValueChanger(rng, mutationStats, mutations));
+    registerMutator(new ConversionRepeater(rng, mutationStats, mutations));
+    registerMutator(new FieldFlagChanger(rng, mutationStats, mutations));
+    registerMutator(new InstructionDeleter(rng, mutationStats, mutations));
+    registerMutator(new InstructionDuplicator(rng, mutationStats, mutations));
+    registerMutator(new InstructionSwapper(rng, mutationStats, mutations));
+    registerMutator(new NewMethodCaller(rng, mutationStats, mutations));
+    registerMutator(new NonsenseStringPrinter(rng, mutationStats, mutations));
+    registerMutator(new PoolIndexChanger(rng, mutationStats, mutations));
+    registerMutator(new RandomInstructionGenerator(rng, mutationStats, mutations));
+    registerMutator(new SwitchBranchShifter(rng, mutationStats, mutations));
+    registerMutator(new TryBlockShifter(rng, mutationStats, mutations));
+    registerMutator(new ValuePrinter(rng, mutationStats, mutations));
+    registerMutator(new VRegChanger(rng, mutationStats, mutations));
+
+    associateClassDefsAndClassData();
+    associateCodeItemsWithMethodNames();
+
+    int codeItemIdx = 0;
+    for (CodeItem codeItem : rawDexFile.codeItems) {
+      if (legalToMutate(codeItem)) {
+        Log.debug("Legal to mutate code item " + codeItemIdx);
+        int mutatableCodeIdx = mutatableCodes.size();
+        mutatableCodes.add(translator.codeItemToMutatableCode(this, codeItem,
+            codeItemIdx, mutatableCodeIdx));
+      } else {
+        Log.debug("Not legal to mutate code item " + codeItemIdx);
+      }
+      codeItemIdx++;
+    }
+  }
+
+  private void registerMutator(CodeMutator mutator) {
+    if (mutator.canBeTriggered()) {
+      Log.debug("Registering mutator " + mutator.getClass().getSimpleName());
+      mutators.add(mutator);
+    }
+    if (Options.loadMutations) {
+      mutatorsLookupByClass.put(mutator.getClass(), mutator);
+    }
+  }
+
+  /**
+   * Associate ClassDefItem to a ClassDataItem and vice-versa.
+   * This is so when we're associating method names with code items,
+   * we can find the name of the class the method belongs to.
+   */
+  private void associateClassDefsAndClassData() {
+    for (ClassDefItem classDefItem : rawDexFile.classDefs) {
+      if (classDefItem.classDataOff.pointsToSomething()) {
+        ClassDataItem classDataItem = (ClassDataItem)
+            classDefItem.classDataOff.getPointedToItem();
+        classDataItem.meta.classDefItem = classDefItem;
+        classDefItem.meta.classDataItem = classDataItem;
+      }
+    }
+  }
+
+  /**
+   * For each CodeItem, find the name of the method the item represents.
+   * This is done to allow the filtering of mutating methods based on if
+   * they have the name *_MUTATE, but also for debugging info.
+   */
+  private void associateCodeItemsWithMethodNames() {
+    // Associate method names with codeItems.
+    for (ClassDataItem classDataItem : rawDexFile.classDatas) {
+
+      String className = "";
+      if (classDataItem.meta.classDefItem != null) {
+        int typeIdx = classDataItem.meta.classDefItem.classIdx;
+        TypeIdItem typeIdItem = rawDexFile.typeIds.get(typeIdx);
+        className = rawDexFile.stringDatas.get(typeIdItem.descriptorIdx).getString() + ".";
+      }
+
+      // Do direct methods...
+      // Track the current method index with this value, since the encoding in
+      // each EncodedMethod is the absolute index for the first EncodedMethod,
+      // and then relative index for the rest...
+      int methodIdx = 0;
+      for (EncodedMethod method : classDataItem.directMethods) {
+        methodIdx = associateMethod(method, methodIdx, className);
+      }
+      // Reset methodIdx for virtual methods...
+      methodIdx = 0;
+      for (EncodedMethod method : classDataItem.virtualMethods) {
+        methodIdx = associateMethod(method, methodIdx, className);
+      }
+    }
+  }
+
+  /**
+   * Associate the name of the provided method with its CodeItem, if it
+   * has one.
+   *
+   * @param methodIdx The method index of the last EncodedMethod that was handled in this class.
+   * @return The method index of the EncodedMethod that has just been handled in this class.
+   */
+  private int associateMethod(EncodedMethod method, int methodIdx, String className) {
+    if (!method.codeOff.pointsToSomething()) {
+      // This method doesn't have a code item, so we won't encounter it later.
+      return methodIdx;
+    }
+
+    // First method index is an absolute index.
+    // The rest are relative to the previous.
+    // (so if methodIdx is initialised to 0, this single line works)
+    methodIdx = methodIdx + method.methodIdxDiff;
+
+    // Get the name.
+    MethodIdItem methodIdItem = rawDexFile.methodIds.get(methodIdx);
+    ProtoIdItem protoIdItem = rawDexFile.protoIds.get(methodIdItem.protoIdx);
+    String shorty = rawDexFile.stringDatas.get(protoIdItem.shortyIdx).getString();
+    String methodName = className
+        + rawDexFile.stringDatas.get(methodIdItem.nameIdx).getString();
+
+    // Get the codeItem.
+    if (method.codeOff.getPointedToItem() instanceof CodeItem) {
+      CodeItem codeItem = (CodeItem) method.codeOff.getPointedToItem();
+      codeItem.meta.methodName = methodName;
+      codeItem.meta.shorty = shorty;
+      codeItem.meta.isStatic = method.isStatic();
+    } else {
+      Log.errorAndQuit("You've got an EncodedMethod that points to an Offsettable"
+          + " that does not contain a CodeItem");
+    }
+
+    return methodIdx;
+  }
+
+  /**
+   * Determine, based on the current options supplied to dexfuzz, as well as
+   * its capabilities, if the provided CodeItem can be mutated.
+   * @param codeItem The CodeItem we may wish to mutate.
+   * @return If the CodeItem can be mutated.
+   */
+  private boolean legalToMutate(CodeItem codeItem) {
+    if (!Options.mutateLimit) {
+      Log.debug("Mutating everything.");
+      return true;
+    }
+    if (codeItem.meta.methodName.endsWith("_MUTATE")) {
+      Log.debug("Code item marked with _MUTATE.");
+      return true;
+    }
+    Log.debug("Code item not marked with _MUTATE, but not mutating all code items.");
+    return false;
+  }
+
+  private int getNumberOfMutationsToPerform() {
+    // We want n mutations to be twice as likely as n+1 mutations.
+    //
+    // So if we have max 3,
+    // then 0 has 8 chances ("tickets"),
+    //      1 has 4 chances
+    //      2 has 2 chances
+    //  and 3 has 1 chance
+
+    // Allocate the tickets
+    // n mutations need (2^(n+1) - 1) tickets
+    // e.g.
+    // 3 mutations => 15 tickets
+    // 4 mutations => 31 tickets
+    int tickets = (2 << Options.methodMutations) - 1;
+
+    // Pick the lucky ticket
+    int luckyTicket = rng.nextInt(tickets);
+
+    // The tickets are put into buckets with accordance with log-base-2.
+    // have to make sure it's luckyTicket + 1, because log(0) is undefined
+    // so:
+    // log_2(1) => 0
+    // log_2(2) => 1
+    // log_2(3) => 1
+    // log_2(4) => 2
+    // log_2(5) => 2
+    // log_2(6) => 2
+    // log_2(7) => 2
+    // log_2(8) => 3
+    // ...
+    // so to make the highest mutation value the rarest,
+    //   subtract log_2(luckyTicket+1) from the maximum number
+    // log2(x) <=> 31 - Integer.numberOfLeadingZeros(x)
+    int luckyMutation = Options.methodMutations
+        - (31 - Integer.numberOfLeadingZeros(luckyTicket + 1));
+
+    return luckyMutation;
+  }
+
+  /**
+   * Returns true if we completely failed to mutate this method's mutatable code after
+   * attempting to.
+   */
+  private boolean mutateAMutatableCode(MutatableCode mutatableCode) {
+    int mutations = getNumberOfMutationsToPerform();
+
+    Log.info("Attempting " + mutations + " mutations for method " + mutatableCode.name);
+
+    int mutationsApplied = 0;
+
+    int maximumMutationAttempts = Options.methodMutations * MAXIMUM_MUTATION_ATTEMPT_FACTOR;
+    int mutationAttempts = 0;
+    boolean hadToBail = false;
+
+    while (mutationsApplied < mutations) {
+      int mutatorIdx = rng.nextInt(mutators.size());
+      CodeMutator mutator = mutators.get(mutatorIdx);
+      Log.info("Running mutator " + mutator.getClass().getSimpleName());
+      if (mutator.attemptToMutate(mutatableCode)) {
+        mutationsApplied++;
+      }
+      mutationAttempts++;
+      if (mutationAttempts > maximumMutationAttempts) {
+        Log.info("Bailing out on mutation for this method, tried too many times...");
+        hadToBail = true;
+        break;
+      }
+    }
+
+    // If any of them actually mutated it, excellent!
+    if (mutationsApplied > 0) {
+      Log.info("Method was mutated.");
+      mutatedCodes.add(mutatableCode);
+    } else {
+      Log.info("Method was not mutated.");
+    }
+
+    return ((mutationsApplied == 0) && hadToBail);
+  }
+
+  /**
+   * Go through each mutatable method in turn, and attempt to mutate it.
+   * Afterwards, call updateRawDexFile() to apply the results of mutation to the
+   * original code.
+   */
+  public void mutateTheProgram() {
+    if (Options.loadMutations) {
+      applyMutationsFromList();
+      return;
+    }
+
+    // Typically, this is 2 to 10...
+    int methodsToMutate = Options.minMethods
+        + rng.nextInt((Options.maxMethods - Options.minMethods) + 1);
+
+    // Check we aren't trying to mutate more methods than we have.
+    if (methodsToMutate > mutatableCodes.size()) {
+      methodsToMutate = mutatableCodes.size();
+    }
+
+    // Check if we're going to end up mutating all the methods.
+    if (methodsToMutate == mutatableCodes.size()) {
+      // Just do them all in order.
+      Log.info("Mutating all possible methods.");
+      for (MutatableCode mutatableCode : mutatableCodes) {
+        if (mutatableCode == null) {
+          Log.errorAndQuit("Why do you have a null MutatableCode?");
+        }
+        mutateAMutatableCode(mutatableCode);
+      }
+      Log.info("Finished mutating all possible methods.");
+    } else {
+      // Pick them at random.
+      Log.info("Randomly selecting " + methodsToMutate + " methods to mutate.");
+      while (mutatedCodes.size() < methodsToMutate) {
+        int randomMethodIdx = rng.nextInt(mutatableCodes.size());
+        MutatableCode mutatableCode = mutatableCodes.get(randomMethodIdx);
+        if (mutatableCode == null) {
+          Log.errorAndQuit("Why do you have a null MutatableCode?");
+        }
+        if (!mutatedCodes.contains(mutatableCode)) {
+          boolean completelyFailedToMutate = mutateAMutatableCode(mutatableCode);
+          if (completelyFailedToMutate) {
+            methodsToMutate--;
+          }
+        }
+      }
+      Log.info("Finished mutating the methods.");
+    }
+
+    listener.handleMutationStats(mutationStats.getStatsString());
+
+    if (Options.dumpMutations) {
+      writeMutationsToDisk(Options.dumpMutationsFile);
+    }
+  }
+
+  private void writeMutationsToDisk(String fileName) {
+    Log.debug("Writing mutations to disk.");
+    try {
+      BufferedWriter writer = new BufferedWriter(new FileWriter(fileName));
+      for (Mutation mutation : mutations) {
+        MutationSerializer.writeMutation(writer, mutation);
+      }
+      writer.close();
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException while writing mutations to disk...");
+    }
+  }
+
+  private void loadMutationsFromDisk(String fileName) {
+    Log.debug("Loading mutations from disk.");
+    try {
+      BufferedReader reader = new BufferedReader(new FileReader(fileName));
+      while (reader.ready()) {
+        Mutation mutation = MutationSerializer.readMutation(reader);
+        mutations.add(mutation);
+      }
+      reader.close();
+    } catch (IOException e) {
+      Log.errorAndQuit("IOException while loading mutations from disk...");
+    }
+  }
+
+  private void applyMutationsFromList() {
+    Log.info("Applying preloaded list of mutations...");
+    for (Mutation mutation : mutations) {
+      // Repopulate the MutatableCode field from the recorded index into the Program's list.
+      mutation.mutatableCode = mutatableCodes.get(mutation.mutatableCodeIdx);
+
+      // Get the right mutator.
+      CodeMutator mutator = mutatorsLookupByClass.get(mutation.mutatorClass);
+
+      // Apply the mutation.
+      mutator.forceMutate(mutation);
+
+      // Add this mutatable code to the list of mutated codes, if we haven't already.
+      if (!mutatedCodes.contains(mutation.mutatableCode)) {
+        mutatedCodes.add(mutation.mutatableCode);
+      }
+    }
+    Log.info("...finished applying preloaded list of mutations.");
+  }
+
+  public List<Mutation> getMutations() {
+    return mutations;
+  }
+
+  /**
+   * Updates any CodeItems that need to be updated after mutation.
+   */
+  public boolean updateRawDexFile() {
+    boolean anythingMutated = !(mutatedCodes.isEmpty());
+    for (MutatableCode mutatedCode : mutatedCodes) {
+      translator.mutatableCodeToCodeItem(rawDexFile.codeItems
+          .get(mutatedCode.codeItemIdx), mutatedCode);
+    }
+    mutatedCodes.clear();
+    return anythingMutated;
+  }
+
+  public void writeRawDexFile(DexRandomAccessFile file) throws IOException {
+    rawDexFile.write(file);
+  }
+
+  public void updateRawDexFileHeader(DexRandomAccessFile file) throws IOException {
+    rawDexFile.updateHeader(file);
+  }
+
+  /**
+   * Used by the CodeMutators to determine legal index values.
+   */
+  public int getTotalPoolIndicesByKind(PoolIndexKind poolIndexKind) {
+    switch (poolIndexKind) {
+      case Type:
+        return rawDexFile.typeIds.size();
+      case Field:
+        return rawDexFile.fieldIds.size();
+      case String:
+        return rawDexFile.stringIds.size();
+      case Method:
+        return rawDexFile.methodIds.size();
+      case Invalid:
+        return 0;
+      default:
+    }
+    return 0;
+  }
+
+  /**
+   * Used by the CodeMutators to lookup and/or create Ids.
+   */
+  public IdCreator getNewItemCreator() {
+    return idCreator;
+  }
+
+  /**
+   * Used by FieldFlagChanger, to find an EncodedField for a specified field in an insn,
+   * if that field is actually defined in this DEX file. If not, null is returned.
+   */
+  public EncodedField getEncodedField(int fieldIdx) {
+    if (fieldIdx >= rawDexFile.fieldIds.size()) {
+      Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
+          fieldIdx));
+      return null;
+    }
+    FieldIdItem fieldId = rawDexFile.fieldIds.get(fieldIdx);
+
+    for (ClassDefItem classDef : rawDexFile.classDefs) {
+      if (classDef.classIdx == fieldId.classIdx) {
+        ClassDataItem classData = classDef.meta.classDataItem;
+        return classData.getEncodedFieldWithIndex(fieldIdx);
+      }
+    }
+
+    Log.debug(String.format("Field idx 0x%x specified is not defined in this DEX file.",
+        fieldIdx));
+    return null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java
new file mode 100644
index 0000000..4c69694
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ArithOpChanger.java
@@ -0,0 +1,290 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ArithOpChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int arithmeticInsnIdx;
+    public int newOpcode;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(arithmeticInsnIdx).append(" ");
+      builder.append(newOpcode);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      arithmeticInsnIdx = Integer.parseInt(elements[2]);
+      newOpcode = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ArithOpChanger() { }
+
+  public ArithOpChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 75;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> arithmeticInsns = null;
+
+  private void generateCachedArithmeticInsns(MutatableCode mutatableCode) {
+    if (arithmeticInsns != null) {
+      return;
+    }
+
+    arithmeticInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isArithmeticOperation(mInsn)) {
+        arithmeticInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isArithmeticOperation(mInsn)) {
+        return true;
+      }
+    }
+
+    Log.debug("No arithmetic operations in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedArithmeticInsns(mutatableCode);
+
+    int arithmeticInsnIdx = rng.nextInt(arithmeticInsns.size());
+
+    MInsn randomInsn = arithmeticInsns.get(arithmeticInsnIdx);
+
+    OpcodeInfo oldOpcodeInfo = randomInsn.insn.info;
+
+    OpcodeInfo newOpcodeInfo = oldOpcodeInfo;
+
+    while (newOpcodeInfo.value == oldOpcodeInfo.value) {
+      newOpcodeInfo = Instruction.getOpcodeInfo(getLegalDifferentOpcode(randomInsn));
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.arithmeticInsnIdx = arithmeticInsnIdx;
+    mutation.newOpcode = newOpcodeInfo.value;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedArithmeticInsns(mutatableCode);
+
+    MInsn randomInsn = arithmeticInsns.get(mutation.arithmeticInsnIdx);
+
+    String oldInsnString = randomInsn.toString();
+
+    OpcodeInfo newOpcodeInfo = Instruction.getOpcodeInfo(mutation.newOpcode);
+
+    randomInsn.insn.info = newOpcodeInfo;
+
+    Log.info("Changed " + oldInsnString + " to " + randomInsn);
+
+    stats.incrementStat("Changed arithmetic opcode");
+
+    // Clear the cache.
+    arithmeticInsns = null;
+  }
+
+  private boolean isArithmeticOperation(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT_LIT8)) {
+      return true;
+    }
+    return false;
+  }
+
+  private Opcode getLegalDifferentOpcode(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+
+    for (List<Opcode> opcodeList : opcodeLists) {
+      Opcode first = opcodeList.get(0);
+      Opcode last = opcodeList.get(opcodeList.size() - 1);
+      if (Opcode.isBetween(opcode, first, last)) {
+        int newOpcodeIdx = rng.nextInt(opcodeList.size());
+        return opcodeList.get(newOpcodeIdx);
+      }
+    }
+
+    return opcode;
+  }
+
+  private static List<Opcode> intOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> int2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> longOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> long2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> floatOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> float2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> doubleOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> double2addrOpcodes = new ArrayList<Opcode>();
+  private static List<Opcode> intLit8Opcodes = new ArrayList<Opcode>();
+  private static List<Opcode> intLit16Opcodes = new ArrayList<Opcode>();
+  private static List<List<Opcode>> opcodeLists = new ArrayList<List<Opcode>>();
+
+  static {
+    intOpcodes.add(Opcode.ADD_INT);
+    intOpcodes.add(Opcode.SUB_INT);
+    intOpcodes.add(Opcode.MUL_INT);
+    intOpcodes.add(Opcode.DIV_INT);
+    intOpcodes.add(Opcode.REM_INT);
+    intOpcodes.add(Opcode.AND_INT);
+    intOpcodes.add(Opcode.OR_INT);
+    intOpcodes.add(Opcode.XOR_INT);
+    intOpcodes.add(Opcode.SHL_INT);
+    intOpcodes.add(Opcode.SHR_INT);
+    intOpcodes.add(Opcode.USHR_INT);
+
+    int2addrOpcodes.add(Opcode.ADD_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.SUB_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.MUL_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.DIV_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.REM_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.AND_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.OR_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.XOR_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.SHL_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.SHR_INT_2ADDR);
+    int2addrOpcodes.add(Opcode.USHR_INT_2ADDR);
+
+    longOpcodes.add(Opcode.ADD_LONG);
+    longOpcodes.add(Opcode.SUB_LONG);
+    longOpcodes.add(Opcode.MUL_LONG);
+    longOpcodes.add(Opcode.DIV_LONG);
+    longOpcodes.add(Opcode.REM_LONG);
+    longOpcodes.add(Opcode.AND_LONG);
+    longOpcodes.add(Opcode.OR_LONG);
+    longOpcodes.add(Opcode.XOR_LONG);
+    longOpcodes.add(Opcode.SHL_LONG);
+    longOpcodes.add(Opcode.SHR_LONG);
+    longOpcodes.add(Opcode.USHR_LONG);
+
+    long2addrOpcodes.add(Opcode.ADD_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.SUB_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.MUL_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.DIV_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.REM_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.AND_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.OR_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.XOR_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.SHL_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.SHR_LONG_2ADDR);
+    long2addrOpcodes.add(Opcode.USHR_LONG_2ADDR);
+
+    floatOpcodes.add(Opcode.ADD_FLOAT);
+    floatOpcodes.add(Opcode.SUB_FLOAT);
+    floatOpcodes.add(Opcode.MUL_FLOAT);
+    floatOpcodes.add(Opcode.DIV_FLOAT);
+    floatOpcodes.add(Opcode.REM_FLOAT);
+
+    float2addrOpcodes.add(Opcode.ADD_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.SUB_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.MUL_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.DIV_FLOAT_2ADDR);
+    float2addrOpcodes.add(Opcode.REM_FLOAT_2ADDR);
+
+    doubleOpcodes.add(Opcode.ADD_DOUBLE);
+    doubleOpcodes.add(Opcode.SUB_DOUBLE);
+    doubleOpcodes.add(Opcode.MUL_DOUBLE);
+    doubleOpcodes.add(Opcode.DIV_DOUBLE);
+    doubleOpcodes.add(Opcode.REM_DOUBLE);
+
+    double2addrOpcodes.add(Opcode.ADD_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.SUB_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.MUL_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.DIV_DOUBLE_2ADDR);
+    double2addrOpcodes.add(Opcode.REM_DOUBLE_2ADDR);
+
+    intLit8Opcodes.add(Opcode.ADD_INT_LIT8);
+    intLit8Opcodes.add(Opcode.RSUB_INT_LIT8);
+    intLit8Opcodes.add(Opcode.MUL_INT_LIT8);
+    intLit8Opcodes.add(Opcode.DIV_INT_LIT8);
+    intLit8Opcodes.add(Opcode.REM_INT_LIT8);
+    intLit8Opcodes.add(Opcode.AND_INT_LIT8);
+    intLit8Opcodes.add(Opcode.OR_INT_LIT8);
+    intLit8Opcodes.add(Opcode.XOR_INT_LIT8);
+    intLit8Opcodes.add(Opcode.SHL_INT_LIT8);
+    intLit8Opcodes.add(Opcode.SHR_INT_LIT8);
+    intLit8Opcodes.add(Opcode.USHR_INT_LIT8);
+
+    intLit16Opcodes.add(Opcode.ADD_INT_LIT16);
+    intLit16Opcodes.add(Opcode.RSUB_INT);
+    intLit16Opcodes.add(Opcode.MUL_INT_LIT16);
+    intLit16Opcodes.add(Opcode.DIV_INT_LIT16);
+    intLit16Opcodes.add(Opcode.REM_INT_LIT16);
+    intLit16Opcodes.add(Opcode.AND_INT_LIT16);
+    intLit16Opcodes.add(Opcode.OR_INT_LIT16);
+    intLit16Opcodes.add(Opcode.XOR_INT_LIT16);
+
+    opcodeLists.add(intOpcodes);
+    opcodeLists.add(longOpcodes);
+    opcodeLists.add(floatOpcodes);
+    opcodeLists.add(doubleOpcodes);
+    opcodeLists.add(int2addrOpcodes);
+    opcodeLists.add(long2addrOpcodes);
+    opcodeLists.add(float2addrOpcodes);
+    opcodeLists.add(double2addrOpcodes);
+    opcodeLists.add(intLit8Opcodes);
+    opcodeLists.add(intLit16Opcodes);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java
new file mode 100644
index 0000000..a28f5ba
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/BranchShifter.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MBranchInsn;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class BranchShifter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int branchInsnIdx;
+    public int newTargetIdx;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(branchInsnIdx).append(" ");
+      builder.append(newTargetIdx);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      branchInsnIdx = Integer.parseInt(elements[2]);
+      newTargetIdx = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public BranchShifter() { }
+
+  public BranchShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MBranchInsn> branchInsns;
+
+  private void generateCachedBranchInsns(MutatableCode mutatableCode) {
+    if (branchInsns != null) {
+      return;
+    }
+
+    branchInsns = new ArrayList<MBranchInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MBranchInsn) {
+        branchInsns.add((MBranchInsn) mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    // Can't shift a branch if there's only one instruction in the method.
+    if (mutatableCode.getInstructionCount() == 1) {
+      Log.debug("Method contains only one instruction, skipping.");
+      return false;
+    }
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MBranchInsn) {
+        return true;
+      }
+    }
+
+    Log.debug("Method contains no branch instructions.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedBranchInsns(mutatableCode);
+
+    // Pick a random branching instruction.
+    int branchInsnIdx = rng.nextInt(branchInsns.size());
+    MBranchInsn branchInsn = branchInsns.get(branchInsnIdx);
+
+    // Get its original target, find its index.
+    MInsn oldTargetInsn = branchInsn.target;
+    int oldTargetInsnIdx = mutatableCode.getInstructionIndex(oldTargetInsn);
+
+    int newTargetIdx = oldTargetInsnIdx;
+
+    int delta = 0;
+
+    // Keep searching for a new index.
+    while (newTargetIdx == oldTargetInsnIdx) {
+      // Vary by +/- 2 instructions.
+      delta = 0;
+      while (delta == 0) {
+        delta = (rng.nextInt(5) - 2);
+      }
+
+      newTargetIdx = oldTargetInsnIdx + delta;
+
+      // Check the new index is legal.
+      if (newTargetIdx < 0) {
+        newTargetIdx = 0;
+      } else if (newTargetIdx >= mutatableCode.getInstructionCount()) {
+        newTargetIdx = mutatableCode.getInstructionCount() - 1;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.branchInsnIdx = branchInsnIdx;
+    mutation.newTargetIdx = newTargetIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedBranchInsns(mutatableCode);
+
+    MBranchInsn branchInsn = branchInsns.get(mutation.branchInsnIdx);
+
+    // Get the new target.
+    MInsn newTargetInsn = mutatableCode.getInstructionAt(mutation.newTargetIdx);
+
+    // Set the new target.
+    branchInsn.target = newTargetInsn;
+
+    Log.info("Shifted the target of " + branchInsn + " to point to " + newTargetInsn);
+
+    stats.incrementStat("Shifted branch target");
+
+    // Clear cache.
+    branchInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java
new file mode 100644
index 0000000..dc60e79
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/CmpBiasChanger.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class CmpBiasChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int cmpBiasInsnIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(cmpBiasInsnIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      cmpBiasInsnIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public CmpBiasChanger() { }
+
+  public CmpBiasChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> cmpBiasInsns = null;
+
+  private void generateCachedCmpBiasInsns(MutatableCode mutatableCode) {
+    if (cmpBiasInsns != null) {
+      return;
+    }
+
+    cmpBiasInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isCmpBiasOperation(mInsn)) {
+        cmpBiasInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isCmpBiasOperation(mInsn)) {
+        return true;
+      }
+    }
+
+    Log.debug("No cmp-with-bias operations in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedCmpBiasInsns(mutatableCode);
+
+    int cmpBiasInsnIdx = rng.nextInt(cmpBiasInsns.size());
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.cmpBiasInsnIdx = cmpBiasInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedCmpBiasInsns(mutatableCode);
+
+    MInsn cmpBiasInsn = cmpBiasInsns.get(mutation.cmpBiasInsnIdx);
+
+    String oldInsnString = cmpBiasInsn.toString();
+
+    Opcode newOpcode = getLegalDifferentOpcode(cmpBiasInsn);
+
+    cmpBiasInsn.insn.info = Instruction.getOpcodeInfo(newOpcode);
+
+    Log.info("Changed " + oldInsnString + " to " + cmpBiasInsn);
+
+    stats.incrementStat("Changed comparison bias");
+
+    // Clear cache.
+    cmpBiasInsns = null;
+  }
+
+  private Opcode getLegalDifferentOpcode(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (opcode == Opcode.CMPG_DOUBLE) {
+      return Opcode.CMPL_DOUBLE;
+    }
+    if (opcode == Opcode.CMPL_DOUBLE) {
+      return Opcode.CMPG_DOUBLE;
+    }
+    if (opcode == Opcode.CMPG_FLOAT) {
+      return Opcode.CMPL_FLOAT;
+    }
+    return Opcode.CMPG_FLOAT;
+  }
+
+  private boolean isCmpBiasOperation(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.CMPL_FLOAT, Opcode.CMPG_DOUBLE)) {
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java
new file mode 100644
index 0000000..be566ad
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/CodeMutator.java
@@ -0,0 +1,136 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.Options;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+/**
+ * The base class for all classes that can mutate methods.
+ */
+public abstract class CodeMutator {
+  /**
+   * The RNG, passed in by the Program that initialised us.
+   */
+  protected Random rng;
+
+  /**
+   * Used to track which mutations happen.
+   */
+  protected MutationStats stats;
+
+  /**
+   * Used to track mutations that have been applied so far.
+   */
+  protected List<Mutation> mutations;
+
+  /**
+   * The chance, out of 100, that this mutator actually mutates the the program
+   * when asked to by the Program. The default is 50% chance, but each mutator that
+   * extends CodeMutator should its own default.
+   */
+  protected int likelihood = 50;
+
+  /**
+   * This constructor is only intended for use in MutationRecorder...
+   */
+  public CodeMutator() {
+
+  }
+
+  /**
+   * Constructor that all subclasses must call...
+   *
+   * @param rng The RNG that the Program created.
+   */
+  public CodeMutator(Random rng, MutationStats stats, List<Mutation> mutations) {
+    this.rng = rng;
+    this.stats = stats;
+    this.mutations = mutations;
+
+    String name = this.getClass().getSimpleName().toLowerCase();
+
+    if (Options.mutationLikelihoods.containsKey(name)) {
+      likelihood = Options.mutationLikelihoods.get(name);
+      Log.info("Set mutation likelihood to " + likelihood
+          + "% for " + this.getClass().getSimpleName());
+    }
+  }
+
+  /**
+   * When the Program picks a particular mutator to mutate the code, it calls
+   * this function, that determines if the mutator will actually mutate the code.
+   * If so, it then calls the mutationFunction() method, that every subclass CodeMutator
+   * is expected to implement to perform its mutation.
+   *
+   * @return If mutation took place.
+   */
+  public boolean attemptToMutate(MutatableCode mutatableCode) {
+    if (shouldMutate(mutatableCode)) {
+      generateAndApplyMutation(mutatableCode);
+      return true;
+    }
+    Log.info("Skipping mutation.");
+    return false;
+  }
+
+  public void forceMutate(Mutation mutation) {
+    Log.info("Forcing mutation.");
+    applyMutation(mutation);
+  }
+
+  public boolean canBeTriggered() {
+    return (likelihood > 0);
+  }
+
+  /**
+   * Randomly determine if the mutator will actually mutate a method, based on its
+   * provided likelihood of mutation.
+   *
+   * @return If the method should be mutated.
+   */
+  private boolean shouldMutate(MutatableCode mutatableCode) {
+    return ((rng.nextInt(100) < likelihood) && canMutate(mutatableCode));
+  }
+
+  private void generateAndApplyMutation(MutatableCode mutatableCode) {
+    Mutation mutation = generateMutation(mutatableCode);
+    // Always save the mutation.
+    mutations.add(mutation);
+    applyMutation(mutation);
+  }
+
+  /**
+   * A CodeMutator must override this method if there is any reason why could not mutate
+   * a particular method, and return false in that case.
+   */
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    return true;
+  }
+
+  protected abstract Mutation generateMutation(MutatableCode mutatableCode);
+
+  protected abstract void applyMutation(Mutation uncastMutation);
+
+  public abstract Mutation getNewMutation();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java
new file mode 100644
index 0000000..22f04e8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ConstantValueChanger.java
@@ -0,0 +1,149 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsConst;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ConstantValueChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int constInsnIdx;
+    public long newConstant;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(constInsnIdx).append(" ");
+      builder.append(newConstant);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      constInsnIdx = Integer.parseInt(elements[2]);
+      newConstant = Long.parseLong(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ConstantValueChanger() { }
+
+  public ConstantValueChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 70;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> constInsns = null;
+
+  private void generateCachedConstInsns(MutatableCode mutatableCode) {
+    if (constInsns != null) {
+      return;
+    }
+
+    constInsns = new ArrayList<MInsn>();
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsConst) {
+        constInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsConst) {
+        return true;
+      }
+    }
+
+    Log.debug("Method contains no const instructions.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedConstInsns(mutatableCode);
+
+    // Pick a random const instruction.
+    int constInsnIdx = rng.nextInt(constInsns.size());
+    MInsn constInsn = constInsns.get(constInsnIdx);
+
+    // Get the constant.
+    long oldConstant = ((ContainsConst)constInsn.insn.info.format).getConst(constInsn.insn);
+
+    long newConstant = oldConstant;
+
+    // Make a new constant.
+    while (newConstant == oldConstant) {
+      newConstant = rng.nextLong()
+          % ((ContainsConst)constInsn.insn.info.format).getConstRange();
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.constInsnIdx = constInsnIdx;
+    mutation.newConstant = newConstant;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedConstInsns(mutatableCode);
+
+    MInsn constInsn = constInsns.get(mutation.constInsnIdx);
+
+    long oldConstant = ((ContainsConst)constInsn.insn.info.format).getConst(constInsn.insn);
+
+    Log.info("Changed constant value #" + oldConstant + " to #" + mutation.newConstant
+        + " in " + constInsn);
+
+    stats.incrementStat("Changed constant value");
+
+    // Set the new constant.
+    ((ContainsConst)constInsn.insn.info.format).setConst(constInsn.insn, mutation.newConstant);
+
+    // Clear cache.
+    constInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java
new file mode 100644
index 0000000..7fdf304
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ConversionRepeater.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class ConversionRepeater extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int conversionInsnIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(conversionInsnIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      conversionInsnIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ConversionRepeater() { }
+
+  public ConversionRepeater(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 50;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> conversionInsns = null;
+
+  private void generateCachedConversionInsns(MutatableCode mutatableCode) {
+    if (conversionInsns != null) {
+      return;
+    }
+
+    conversionInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isConversionInstruction(mInsn)) {
+        conversionInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isConversionInstruction(mInsn)) {
+        return true;
+      }
+    }
+
+    Log.debug("No conversion operations in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedConversionInsns(mutatableCode);
+    int conversionInsnIdx = rng.nextInt(conversionInsns.size());
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.conversionInsnIdx = conversionInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedConversionInsns(mutatableCode);
+
+    MInsn originalInsn = conversionInsns.get(mutation.conversionInsnIdx);
+
+    // We want to create two new instructions:
+    // [original conversion] eg float-to-int
+    // NEW: [there] eg int-to-float (with vregs of first inst swapped)
+    // NEW: [back] eg float-to-int
+
+    // Create the "there" instruction.
+    MInsn newInsnThere = originalInsn.clone();
+
+    // Flip the opcode.
+    Opcode oppositeOpcode = null;
+    switch (newInsnThere.insn.info.opcode) {
+      case INT_TO_LONG:
+        oppositeOpcode = Opcode.LONG_TO_INT;
+        break;
+      case INT_TO_FLOAT:
+        oppositeOpcode = Opcode.FLOAT_TO_INT;
+        break;
+      case INT_TO_DOUBLE:
+        oppositeOpcode = Opcode.DOUBLE_TO_INT;
+        break;
+      case LONG_TO_INT:
+        oppositeOpcode = Opcode.INT_TO_LONG;
+        break;
+      case LONG_TO_FLOAT:
+        oppositeOpcode = Opcode.FLOAT_TO_LONG;
+        break;
+      case LONG_TO_DOUBLE:
+        oppositeOpcode = Opcode.DOUBLE_TO_LONG;
+        break;
+      case FLOAT_TO_INT:
+        oppositeOpcode = Opcode.INT_TO_FLOAT;
+        break;
+      case FLOAT_TO_LONG:
+        oppositeOpcode = Opcode.LONG_TO_FLOAT;
+        break;
+      case FLOAT_TO_DOUBLE:
+        oppositeOpcode = Opcode.DOUBLE_TO_FLOAT;
+        break;
+      case DOUBLE_TO_INT:
+        oppositeOpcode = Opcode.INT_TO_DOUBLE;
+        break;
+      case DOUBLE_TO_LONG:
+        oppositeOpcode = Opcode.LONG_TO_DOUBLE;
+        break;
+      case DOUBLE_TO_FLOAT:
+        oppositeOpcode = Opcode.FLOAT_TO_DOUBLE;
+        break;
+      default:
+        Log.errorAndQuit(
+            "Trying to repeat the conversion in an insn that is not a conversion insn.");
+    }
+    newInsnThere.insn.info = Instruction.getOpcodeInfo(oppositeOpcode);
+
+    // Swap the vregs.
+    long tempReg = newInsnThere.insn.vregA;
+    newInsnThere.insn.vregA = newInsnThere.insn.vregB;
+    newInsnThere.insn.vregB = tempReg;
+
+    // Create the "back" instruction.
+    MInsn newInsnBack = originalInsn.clone();
+
+    // Get the index into the MutatableCode's mInsns list for this insn.
+    int originalInsnIdx = mutatableCode.getInstructionIndex(originalInsn);
+
+    // Insert the new instructions.
+    mutatableCode.insertInstructionAfter(newInsnThere, originalInsnIdx);
+    mutatableCode.insertInstructionAfter(newInsnBack, originalInsnIdx + 1);
+
+    Log.info("Performing conversion repetition for " + originalInsn);
+
+    stats.incrementStat("Repeating conversion");
+
+    // Clear the cache.
+    conversionInsns = null;
+  }
+
+  private boolean isConversionInstruction(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.INT_TO_LONG, Opcode.DOUBLE_TO_FLOAT)) {
+      return true;
+    }
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java
new file mode 100644
index 0000000..0849d12
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/FieldFlagChanger.java
@@ -0,0 +1,167 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.EncodedField;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class FieldFlagChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int fieldInsnIdx;
+    public boolean setVolatile;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(fieldInsnIdx).append(" ");
+      builder.append(setVolatile);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      fieldInsnIdx = Integer.parseInt(elements[2]);
+      setVolatile = Boolean.parseBoolean(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public FieldFlagChanger() { }
+
+  public FieldFlagChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> fieldInsns = null;
+
+  private void generateCachedFieldInsns(MutatableCode mutatableCode) {
+    if (fieldInsns != null) {
+      return;
+    }
+
+    fieldInsns = new ArrayList<MInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) {
+        fieldInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (isFileDefinedFieldInstruction(mInsn, mutatableCode)) {
+        return true;
+      }
+    }
+
+    Log.debug("No field instructions in method, skipping...");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedFieldInsns(mutatableCode);
+
+    int fieldInsnIdx = rng.nextInt(fieldInsns.size());
+
+    Instruction insn = fieldInsns.get(fieldInsnIdx).insn;
+    ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+    int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+    EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx);
+
+    boolean setVolatile = false;
+    if (!encodedField.isVolatile()) {
+      setVolatile = true;
+    }
+    // TODO: Flip more flags?
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.fieldInsnIdx = fieldInsnIdx;
+    mutation.setVolatile = setVolatile;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedFieldInsns(mutatableCode);
+
+    Instruction insn = fieldInsns.get(mutation.fieldInsnIdx).insn;
+    ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+    int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+    EncodedField encodedField = mutatableCode.program.getEncodedField(fieldIdx);
+
+    if (mutation.setVolatile) {
+      encodedField.setVolatile(true);
+      Log.info("Set field idx " + fieldIdx + " as volatile");
+    } else {
+      encodedField.setVolatile(false);
+      Log.info("Set field idx " + fieldIdx + " as not volatile");
+    }
+
+    stats.incrementStat("Changed volatility of field");
+
+    // Clear cache.
+    fieldInsns = null;
+  }
+
+  private boolean isFileDefinedFieldInstruction(MInsn mInsn, MutatableCode mutatableCode) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.SPUT_SHORT)) {
+      Instruction insn = mInsn.insn;
+      ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) insn.info.format;
+      int fieldIdx = containsPoolIndex.getPoolIndex(insn);
+      if (mutatableCode.program.getEncodedField(fieldIdx) != null) {
+        return true;
+      }
+    }
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java
new file mode 100644
index 0000000..8ffa4c5
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDeleter.java
@@ -0,0 +1,138 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MInsnWithData;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionDeleter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insnToDeleteIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(insnToDeleteIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insnToDeleteIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public InstructionDeleter() { }
+
+  public InstructionDeleter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.getInstructionCount() < 4) {
+      // TODO: Make this more sophisticated - right now this is to avoid problems with
+      // a method that has 3 instructions: fill-array-data; return-void; <data for fill-array-data>
+      Log.debug("Cannot delete insns in a method with only a few.");
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Pick an instruction at random...
+    int insnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insnToDeleteIdx = insnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn toBeDeleted =
+        mutatableCode.getInstructionAt(mutation.insnToDeleteIdx);
+
+    Log.info("Deleting " + toBeDeleted);
+
+    stats.incrementStat("Deleted instruction");
+
+    // Delete the instruction.
+    mutatableCode.deleteInstruction(mutation.insnToDeleteIdx);
+
+    // If we delete a with-data insn, we should delete the associated data insn as well.
+    if (toBeDeleted instanceof MInsnWithData) {
+      Log.info(toBeDeleted + " had associated data, so the data insn was deleted.");
+      // Get the data instruction.
+      MInsn dataInsn =
+          ((MInsnWithData)toBeDeleted).dataTarget;
+      mutatableCode.deleteInstruction(dataInsn);
+      stats.incrementStat("Deleted a with-data insn's data");
+    }
+    // If we delete a data insn, we should delete the associated with-data insn as well.
+    if (toBeDeleted.insn.justRaw) {
+      // .justRaw implies that this is a data insn.
+      Log.info(toBeDeleted
+          + " was data, so the associated with-data insn was deleted.");
+
+      // First, find the with-data insn that is pointing to this insn.
+      MInsn withDataInsn = null;
+      for (MInsn mInsn : mutatableCode.getInstructions()) {
+        if (mInsn instanceof MInsnWithData) {
+          if (((MInsnWithData)mInsn).dataTarget == toBeDeleted) {
+            withDataInsn = mInsn;
+            break;
+          }
+        }
+      }
+
+      // Now delete the with-data insn.
+      if (withDataInsn != null) {
+        mutatableCode.deleteInstruction(withDataInsn);
+        stats.incrementStat("Deleted a data insn's with-data insn");
+      } else {
+        Log.errorAndQuit("Tried to delete a data insn, "
+            + "but it didn't have any with-data insn pointing at it?");
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java
new file mode 100644
index 0000000..4917056
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionDuplicator.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionDuplicator extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insnToDuplicateIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(insnToDuplicateIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insnToDuplicateIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public InstructionDuplicator() { }
+
+  public InstructionDuplicator(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 80;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    boolean foundInsn = false;
+    int insnIdx = 0;
+
+    while (!foundInsn) {
+      // Pick an instruction at random...
+      insnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn oldInsn = mutatableCode.getInstructionAt(insnIdx);
+      foundInsn = true;
+      Opcode opcode = oldInsn.insn.info.opcode;
+      // ...check it's a legal instruction to duplicate.
+      if (opcode == Opcode.SPARSE_SWITCH || opcode == Opcode.PACKED_SWITCH
+          || opcode == Opcode.FILL_ARRAY_DATA || oldInsn.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insnToDuplicateIdx = insnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn oldInsn = mutatableCode.getInstructionAt(mutation.insnToDuplicateIdx);
+
+    MInsn newInsn = oldInsn.clone();
+
+    Log.info("Duplicating " + oldInsn);
+
+    stats.incrementStat("Duplicated instruction");
+
+    mutatableCode.insertInstructionAt(newInsn, mutation.insnToDuplicateIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java
new file mode 100644
index 0000000..17ea939
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/InstructionSwapper.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class InstructionSwapper extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int swapInsnIdx;
+    public int swapWithInsnIdx;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(swapInsnIdx).append(" ");
+      builder.append(swapWithInsnIdx);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      swapInsnIdx = Integer.parseInt(elements[2]);
+      swapWithInsnIdx = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public InstructionSwapper() { }
+
+  public InstructionSwapper(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 80;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.getInstructionCount() == 1) {
+      // Cannot swap one instruction.
+      Log.debug("Cannot swap insns in a method with only one.");
+      return false;
+    }
+    return true;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    int swapInsnIdx = 0;
+    int swapWithInsnIdx = 0;
+
+    boolean foundFirstInsn = false;
+    boolean foundSecondInsn = false;
+
+    while (!foundFirstInsn || !foundSecondInsn) {
+      // Look for the first insn.
+      while (!foundFirstInsn) {
+        swapInsnIdx = rng.nextInt(mutatableCode.getInstructionCount());
+        MInsn toBeSwapped = mutatableCode.getInstructionAt(swapInsnIdx);
+        foundFirstInsn = true;
+        if (toBeSwapped.insn.justRaw) {
+          foundFirstInsn = false;
+        }
+      }
+
+      // Look for the second insn.
+      int secondInsnAttempts = 0;
+      while (!foundSecondInsn) {
+        int delta = rng.nextInt(5) - 1;
+
+        if (delta == 0) {
+          continue;
+        }
+
+        swapWithInsnIdx = swapInsnIdx + delta;
+        foundSecondInsn = true;
+
+        // Check insn is in valid range.
+        if (swapWithInsnIdx < 0) {
+          foundSecondInsn = false;
+        } else if (swapWithInsnIdx >= mutatableCode.getInstructionCount()) {
+          foundSecondInsn = false;
+        }
+
+        // Finally, check if we're swapping with a raw insn.
+        if (foundSecondInsn) {
+          if (mutatableCode.getInstructionAt(swapWithInsnIdx).insn.justRaw) {
+            foundSecondInsn = false;
+          }
+        }
+
+        // If we've checked 10 times for an insn to swap with,
+        // and still found nothing, then try a new first insn.
+        if (!foundSecondInsn) {
+          secondInsnAttempts++;
+          if (secondInsnAttempts == 10) {
+            foundFirstInsn = false;
+            break;
+          }
+        }
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.swapInsnIdx = swapInsnIdx;
+    mutation.swapWithInsnIdx = swapWithInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn toBeSwapped = mutatableCode.getInstructionAt(mutation.swapInsnIdx);
+    MInsn swappedWith = mutatableCode.getInstructionAt(mutation.swapWithInsnIdx);
+
+    Log.info("Swapping " + toBeSwapped + " with " + swappedWith);
+
+    stats.incrementStat("Swapped two instructions");
+
+    mutatableCode.swapInstructionsByIndex(mutation.swapInsnIdx, mutation.swapWithInsnIdx);
+
+    Log.info("Now " + swappedWith + " is swapped with " + toBeSwapped);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java
new file mode 100644
index 0000000..88a2f9a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NewMethodCaller.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Instruction.InvokeFormatInfo;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class NewMethodCaller extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public enum InvokeType {
+      VIRTUAL,
+      DIRECT,
+      SUPER,
+      STATIC,
+      INTERFACE
+    }
+
+    public int insertionIdx;
+    public InvokeType invokeType;
+    public String className;
+    public String methodName;
+    public String signature;
+    public int numArgs;
+    public int[] args;
+
+    @Override
+    public String getString() {
+      StringBuilder argsString = new StringBuilder();
+      for (int i = 0; i < numArgs; i++) {
+        argsString.append(args[i]);
+        if (i < (numArgs - 1)) {
+          argsString.append(" ");
+        }
+      }
+      String result = String.format("%d %d %s %s %s %d %s",
+          insertionIdx,
+          invokeType.ordinal(),
+          className,
+          methodName,
+          signature,
+          numArgs,
+          argsString);
+      return result;
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insertionIdx = Integer.parseInt(elements[2]);
+      invokeType = InvokeType.values()[Integer.parseInt(elements[3])];
+      className = elements[4];
+      methodName = elements[5];
+      signature = elements[6];
+      numArgs = Integer.parseInt(elements[7]);
+      args = new int[numArgs];
+      for (int i = 0; i < numArgs; i++) {
+        args[i] = Integer.parseInt(elements[8 + i]);
+      }
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public NewMethodCaller() { }
+
+  public NewMethodCaller(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 10;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find the insertion point.
+    int insertionIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insertionPoint =
+          mutatableCode.getInstructionAt(insertionIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insertionPoint.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insertionIdx = insertionIdx;
+
+    // TODO: Right now this mutator can only insert calls to System.gc() Add more!
+
+    mutation.invokeType = AssociatedMutation.InvokeType.STATIC;
+    mutation.className = "Ljava/lang/System;";
+    mutation.methodName = "gc";
+    mutation.signature = "()V";
+    mutation.numArgs = 0;
+
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn newInsn = new MInsn();
+    newInsn.insn = new Instruction();
+
+    switch (mutation.invokeType) {
+      case VIRTUAL:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL);
+        break;
+      case DIRECT:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_DIRECT);
+        break;
+      case SUPER:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_SUPER);
+        break;
+      case STATIC:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_STATIC);
+        break;
+      case INTERFACE:
+        newInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_INTERFACE);
+        break;
+      default:
+    }
+
+    // TODO: Handle more than just static invokes.
+
+    int methodIdx = mutatableCode.program.getNewItemCreator()
+        .findOrCreateMethodId(mutation.className,
+            mutation.methodName, mutation.signature);
+
+    newInsn.insn.vregB = methodIdx;
+    newInsn.insn.invokeFormatInfo = new InvokeFormatInfo();
+
+    // TODO: More field population, when we call methods that take arguments.
+
+    MInsn insertionPoint =
+        mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+    Log.info(String.format("Called new method %s %s %s, inserting at %s",
+        mutation.className, mutation.methodName, mutation.signature, insertionPoint));
+
+    stats.incrementStat("Called new method");
+
+    mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java
new file mode 100644
index 0000000..b4c9d7b
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/NonsenseStringPrinter.java
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class NonsenseStringPrinter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insertionIdx;
+    public String nonsenseString;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(insertionIdx).append(" ");
+      builder.append(nonsenseString);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insertionIdx = Integer.parseInt(elements[2]);
+      nonsenseString = elements[3];
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public NonsenseStringPrinter() { }
+
+  public NonsenseStringPrinter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 10;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find the insertion point
+    int insertionIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insertionPoint =
+          mutatableCode.getInstructionAt(insertionIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insertionPoint.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insertionIdx = insertionIdx;
+    mutation.nonsenseString = getRandomString();
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId(
+        "Ljava/lang/System;",
+        "Ljava/io/PrintStream;",
+        "out");
+    int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId(
+        "Ljava/io/PrintStream;",
+        "print",
+        "(Ljava/lang/String;)V");
+    int nonsenseStringIdx = mutatableCode.program.getNewItemCreator().findOrCreateString(
+        mutation.nonsenseString);
+
+    MInsn insertionPoint = mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+    mutatableCode.allocateTemporaryVRegs(2);
+
+    int streamRegister = mutatableCode.getTemporaryVReg(0);
+    int stringRegister = mutatableCode.getTemporaryVReg(1);
+
+    // Load into string and stream into the temporary registers.
+    // then call print(stream, string)
+    MInsn constStringInsn = new MInsn();
+    constStringInsn.insn = new Instruction();
+    constStringInsn.insn.info = Instruction.getOpcodeInfo(Opcode.CONST_STRING);
+    constStringInsn.insn.vregB = nonsenseStringIdx;
+    constStringInsn.insn.vregA = stringRegister;
+
+    MInsn streamLoadInsn = new MInsn();
+    streamLoadInsn.insn = new Instruction();
+    streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT);
+    streamLoadInsn.insn.vregB = outFieldIdx;
+    streamLoadInsn.insn.vregA = streamRegister;
+
+    MInsn invokeInsn = new MInsn();
+    invokeInsn.insn = new Instruction();
+    invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE);
+    invokeInsn.insn.vregA = 2;
+    invokeInsn.insn.vregB = printMethodIdx;
+    invokeInsn.insn.vregC = streamRegister;
+
+    Log.info(String.format("Printing nonsense string '%s', inserting at %s",
+        mutation.nonsenseString, insertionPoint));
+
+    stats.incrementStat("Printed nonsense string");
+
+    mutatableCode.insertInstructionAt(invokeInsn, mutation.insertionIdx);
+    mutatableCode.insertInstructionAt(streamLoadInsn, mutation.insertionIdx);
+    mutatableCode.insertInstructionAt(constStringInsn, mutation.insertionIdx);
+
+    mutatableCode.finishedUsingTemporaryVRegs();
+  }
+
+  private String getRandomString() {
+    int size = rng.nextInt(10);
+    int start = (int) 'A';
+    int end = (int) 'Z';
+    StringBuilder builder = new StringBuilder();
+    for (int i = 0; i < size; i++) {
+      builder.append((char) (rng.nextInt((end + 1) - start) + start));
+    }
+    return builder.toString();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java
new file mode 100644
index 0000000..cae5dc1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/PoolIndexChanger.java
@@ -0,0 +1,199 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class PoolIndexChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int poolIndexInsnIdx;
+    public int newPoolIndex;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(poolIndexInsnIdx).append(" ");
+      builder.append(newPoolIndex);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      poolIndexInsnIdx = Integer.parseInt(elements[2]);
+      newPoolIndex = Integer.parseInt(elements[3]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public PoolIndexChanger() { }
+
+  public PoolIndexChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> poolIndexInsns = null;
+
+  private void generateCachedPoolIndexInsns(MutatableCode mutatableCode) {
+    if (poolIndexInsns != null) {
+      return;
+    }
+
+    poolIndexInsns = new ArrayList<MInsn>();
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsPoolIndex) {
+        poolIndexInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    // Remember what kinds of pool indices we see.
+    List<PoolIndexKind> seenKinds = new ArrayList<PoolIndexKind>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsPoolIndex) {
+
+        ContainsPoolIndex containsPoolIndex =
+            (ContainsPoolIndex)mInsn.insn.info.format;
+
+        PoolIndexKind newPoolIndexKind =
+            containsPoolIndex.getPoolIndexKind(mInsn.insn.info);
+
+        seenKinds.add(newPoolIndexKind);
+      }
+    }
+
+    // Now check that there exists a kind such that the max pool index for
+    // the kind is greater than 1 (i.e., something can be changed)
+    if (!seenKinds.isEmpty()) {
+
+      for (PoolIndexKind kind : seenKinds) {
+        int numPoolIndices = mutatableCode.program.getTotalPoolIndicesByKind(kind);
+        if (numPoolIndices > 1) {
+          return true;
+        }
+      }
+
+      Log.debug("Method does not contain any insns that index into a const pool size > 1");
+      return false;
+    }
+
+    Log.debug("Method contains no instructions that index into the constant pool.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedPoolIndexInsns(mutatableCode);
+
+    int poolIndexInsnIdx = 0;
+    boolean found = false;
+
+    int oldPoolIndex = 0;
+    int newPoolIndex = 0;
+    int maxPoolIndex = 0;
+
+    // Pick a random instruction.
+    while (!found) {
+      poolIndexInsnIdx = rng.nextInt(poolIndexInsns.size());
+      MInsn poolIndexInsn = poolIndexInsns.get(poolIndexInsnIdx);
+
+      found = true;
+
+      ContainsPoolIndex containsPoolIndex =
+          (ContainsPoolIndex)poolIndexInsn.insn.info.format;
+
+      // Get the pool index.
+      oldPoolIndex = containsPoolIndex.getPoolIndex(poolIndexInsn.insn);
+      newPoolIndex = oldPoolIndex;
+
+      // Get the largest pool index available for the provided kind of pool index.
+      PoolIndexKind poolIndexKind =
+          containsPoolIndex.getPoolIndexKind(poolIndexInsn.insn.info);
+      maxPoolIndex = mutatableCode.program.getTotalPoolIndicesByKind(poolIndexKind);
+
+      if (maxPoolIndex <= 1) {
+        found = false;
+      }
+    }
+
+    // Get a new pool index.
+    while (newPoolIndex == oldPoolIndex) {
+      newPoolIndex = rng.nextInt(maxPoolIndex);
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.poolIndexInsnIdx = poolIndexInsnIdx;
+    mutation.newPoolIndex = newPoolIndex;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedPoolIndexInsns(mutatableCode);
+
+    MInsn poolIndexInsn = poolIndexInsns.get(mutation.poolIndexInsnIdx);
+
+    ContainsPoolIndex containsPoolIndex =
+        (ContainsPoolIndex) poolIndexInsn.insn.info.format;
+
+    int oldPoolIndex = containsPoolIndex.getPoolIndex(poolIndexInsn.insn);
+
+    Log.info("Changed pool index " + oldPoolIndex + " to " + mutation.newPoolIndex
+        + " in " + poolIndexInsn);
+
+    stats.incrementStat("Changed constant pool index");
+
+    // Set the new pool index.
+    containsPoolIndex.setPoolIndex(poolIndexInsn.insn, mutation.newPoolIndex);
+
+    // Clear cache.
+    poolIndexInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java
new file mode 100644
index 0000000..ff43c7c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/RandomInstructionGenerator.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MBranchInsn;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+import dexfuzz.rawdex.formats.AbstractFormat;
+import dexfuzz.rawdex.formats.ContainsConst;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsPoolIndex.PoolIndexKind;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+
+import java.util.List;
+import java.util.Random;
+
+public class RandomInstructionGenerator extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int insertionIdx;
+    public int newOpcode;
+    public boolean hasConst;
+    public long constValue;
+    public boolean hasPoolIndex;
+    public int poolIndexValue;
+    public boolean hasVregs;
+    public int vregCount;
+    public int vregA;
+    public int vregB;
+    public int vregC;
+    public int branchTargetIdx;
+
+    @Override
+    public String getString() {
+      String result = String.format("%d %d %s %d %s %d %s %d %d %d %d %d",
+          insertionIdx,
+          newOpcode,
+          (hasConst ? "T" : "F"),
+          constValue,
+          (hasPoolIndex ? "T" : "F"),
+          poolIndexValue,
+          (hasVregs ? "T" : "F"),
+          vregCount,
+          vregA,
+          vregB,
+          vregC,
+          branchTargetIdx
+          );
+      return result;
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      insertionIdx = Integer.parseInt(elements[2]);
+      newOpcode = Integer.parseInt(elements[3]);
+      hasConst = (elements[4].equals("T"));
+      constValue = Long.parseLong(elements[5]);
+      hasPoolIndex = (elements[6].equals("T"));
+      poolIndexValue = Integer.parseInt(elements[7]);
+      hasVregs = (elements[8].equals("T"));
+      vregCount = Integer.parseInt(elements[9]);
+      vregA = Integer.parseInt(elements[10]);
+      vregB = Integer.parseInt(elements[11]);
+      vregC = Integer.parseInt(elements[12]);
+      branchTargetIdx = Integer.parseInt(elements[13]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public RandomInstructionGenerator() { }
+
+  public RandomInstructionGenerator(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find the insertion point.
+    int insertionIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      insertionIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insertionPoint =
+          mutatableCode.getInstructionAt(insertionIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insertionPoint.insn.justRaw) {
+        foundInsn = false;
+      }
+    }
+
+    Opcode newOpcode = null;
+    int opcodeCount = Opcode.values().length;
+    boolean foundOpcode = false;
+
+    while (!foundOpcode) {
+      newOpcode = Opcode.values()[rng.nextInt(opcodeCount)];
+      foundOpcode = true;
+      if (Opcode.isBetween(newOpcode, Opcode.FILLED_NEW_ARRAY, Opcode.FILL_ARRAY_DATA)
+          || Opcode.isBetween(newOpcode, Opcode.PACKED_SWITCH, Opcode.SPARSE_SWITCH)
+          || Opcode.isBetween(newOpcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
+          || Opcode.isBetween(newOpcode,
+              Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)
+              // Can never accept these instructions at compile time.
+              || Opcode.isBetween(newOpcode, Opcode.IGET_QUICK, Opcode.IPUT_SHORT_QUICK)
+              // Unused opcodes...
+              || Opcode.isBetween(newOpcode, Opcode.UNUSED_3E, Opcode.UNUSED_43)
+              || Opcode.isBetween(newOpcode, Opcode.UNUSED_79, Opcode.UNUSED_7A)
+              || Opcode.isBetween(newOpcode, Opcode.UNUSED_EF, Opcode.UNUSED_FF)) {
+        foundOpcode = false;
+      }
+    }
+
+    OpcodeInfo newOpcodeInfo = Instruction.getOpcodeInfo(newOpcode);
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.insertionIdx = insertionIdx;
+    mutation.newOpcode = newOpcodeInfo.value;
+
+    AbstractFormat fmt = newOpcodeInfo.format;
+
+    if (fmt instanceof ContainsConst) {
+      mutation.hasConst = true;
+      mutation.constValue = rng.nextLong() % ((ContainsConst)fmt).getConstRange();
+    }
+    if (fmt instanceof ContainsPoolIndex) {
+      mutation.hasPoolIndex = true;
+      ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) fmt;
+      PoolIndexKind poolIndexKind = containsPoolIndex.getPoolIndexKind(newOpcodeInfo);
+      int maxPoolIndex = mutatableCode.program.getTotalPoolIndicesByKind(poolIndexKind);
+      if (maxPoolIndex > 0) {
+        mutation.poolIndexValue = rng.nextInt(maxPoolIndex);
+      } else {
+        mutation.poolIndexValue = 0;
+      }
+    }
+    if (mutatableCode.registersSize == 0) {
+      mutatableCode.registersSize = 1;
+    }
+    if (fmt instanceof ContainsVRegs) {
+      mutation.hasVregs = true;
+      ContainsVRegs containsVregs = (ContainsVRegs) fmt;
+      mutation.vregCount = containsVregs.getVRegCount();
+      switch (mutation.vregCount) {
+        case 3:
+          mutation.vregC = rng.nextInt(mutatableCode.registersSize);
+          // fallthrough
+        case 2:
+          mutation.vregB = rng.nextInt(mutatableCode.registersSize);
+          // fallthrough
+        case 1:
+          mutation.vregA = rng.nextInt(mutatableCode.registersSize);
+          break;
+        default:
+          Log.errorAndQuit("Invalid number of vregs specified.");
+      }
+    }
+    // If we have some kind of branch, pick a random target.
+    if (Opcode.isBetween(newOpcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+        || Opcode.isBetween(newOpcode, Opcode.GOTO, Opcode.GOTO_32)) {
+      mutation.branchTargetIdx = rng.nextInt(mutatableCode.getInstructionCount());
+    }
+
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    Opcode newOpcode = Instruction.getOpcodeInfo(mutation.newOpcode).opcode;
+
+    boolean isBranch = false;
+    if (Opcode.isBetween(newOpcode, Opcode.IF_EQ, Opcode.IF_LEZ)
+        || Opcode.isBetween(newOpcode, Opcode.GOTO, Opcode.GOTO_32)) {
+      isBranch = true;
+    }
+
+    MInsn newInsn = null;
+    if (!isBranch) {
+      newInsn = new MInsn();
+    } else {
+      newInsn = new MBranchInsn();
+    }
+    newInsn.insn = new Instruction();
+    newInsn.insn.info = Instruction.getOpcodeInfo(mutation.newOpcode);
+    AbstractFormat fmt = newInsn.insn.info.format;
+
+    if (mutation.hasConst) {
+      ContainsConst containsConst = (ContainsConst) fmt;
+      containsConst.setConst(newInsn.insn, mutation.constValue);
+    }
+    if (mutation.hasPoolIndex) {
+      ContainsPoolIndex containsPoolIndex = (ContainsPoolIndex) fmt;
+      containsPoolIndex.setPoolIndex(newInsn.insn, mutation.poolIndexValue);
+    }
+    if (mutation.hasVregs) {
+      switch (mutation.vregCount) {
+        case 3:
+          newInsn.insn.vregC = mutation.vregC;
+          // fallthrough
+        case 2:
+          newInsn.insn.vregB = mutation.vregB;
+          // fallthrough
+        case 1:
+          newInsn.insn.vregA = mutation.vregA;
+          break;
+        default:
+          Log.errorAndQuit("Invalid number of vregs specified.");
+      }
+    }
+
+    if (isBranch) {
+      // We have a branch instruction, point it at its target.
+      MBranchInsn newBranchInsn = (MBranchInsn) newInsn;
+      newBranchInsn.target = mutatableCode.getInstructionAt(mutation.branchTargetIdx);
+    }
+
+    MInsn insertionPoint =
+        mutatableCode.getInstructionAt(mutation.insertionIdx);
+
+    Log.info("Generated random instruction: " + newInsn
+        + ", inserting at " + insertionPoint);
+
+    stats.incrementStat("Generated random instruction");
+
+    mutatableCode.insertInstructionAt(newInsn, mutation.insertionIdx);
+
+    // If we've generated a monitor insn, generate the matching opposing insn.
+    if (newInsn.insn.info.opcode == Opcode.MONITOR_ENTER) {
+      MInsn exitInsn = newInsn.clone();
+      exitInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MONITOR_EXIT);
+      mutatableCode.insertInstructionAfter(exitInsn, mutation.insertionIdx);
+      Log.info("Generated matching monitor-exit: " + exitInsn);
+    } else if (newInsn.insn.info.opcode == Opcode.MONITOR_EXIT) {
+      MInsn enterInsn = newInsn.clone();
+      enterInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MONITOR_ENTER);
+      mutatableCode.insertInstructionAt(enterInsn, mutation.insertionIdx);
+      Log.info("Generated matching monitor-enter: " + enterInsn);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java
new file mode 100644
index 0000000..6981034
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/SwitchBranchShifter.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MSwitchInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class SwitchBranchShifter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int switchInsnIdx;
+    public int switchTargetIdx;
+    public int newTargetIdx;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(switchInsnIdx).append(" ");
+      builder.append(switchTargetIdx).append(" ");
+      builder.append(newTargetIdx);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      switchInsnIdx = Integer.parseInt(elements[2]);
+      switchTargetIdx = Integer.parseInt(elements[3]);
+      newTargetIdx = Integer.parseInt(elements[4]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public SwitchBranchShifter() { }
+
+  public SwitchBranchShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 30;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MSwitchInsn> switchInsns;
+
+  private void generateCachedSwitchInsns(MutatableCode mutatableCode) {
+    if (switchInsns != null) {
+      return;
+    }
+
+    switchInsns = new ArrayList<MSwitchInsn>();
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        switchInsns.add((MSwitchInsn) mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn instanceof MSwitchInsn) {
+        return true;
+      }
+    }
+
+    Log.debug("Method contains no switch instructions.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedSwitchInsns(mutatableCode);
+
+    // Pick a random switch instruction.
+    int switchInsnIdx = rng.nextInt(switchInsns.size());
+    MSwitchInsn switchInsn = switchInsns.get(switchInsnIdx);
+
+    // Pick a random one of its targets.
+    int switchTargetIdx = rng.nextInt(switchInsn.targets.size());
+
+    // Get the original target, find its index.
+    MInsn oldTargetInsn = switchInsn.targets.get(switchTargetIdx);
+    int oldTargetInsnIdx = mutatableCode.getInstructionIndex(oldTargetInsn);
+
+    int newTargetIdx = oldTargetInsnIdx;
+
+    int delta = 0;
+
+    // Keep searching for a new index.
+    while (newTargetIdx == oldTargetInsnIdx) {
+      // Vary by +/- 2 instructions.
+      delta = 0;
+      while (delta == 0) {
+        delta = (rng.nextInt(5) - 2);
+      }
+
+      newTargetIdx = oldTargetInsnIdx + delta;
+
+      // Check the new index is legal
+      if (newTargetIdx < 0) {
+        newTargetIdx = 0;
+      } else if (newTargetIdx >= mutatableCode.getInstructionCount()) {
+        newTargetIdx = mutatableCode.getInstructionCount() - 1;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.switchInsnIdx = switchInsnIdx;
+    mutation.switchTargetIdx = switchTargetIdx;
+    mutation.newTargetIdx = newTargetIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedSwitchInsns(mutatableCode);
+
+    MSwitchInsn switchInsn = switchInsns.get(mutation.switchInsnIdx);
+
+    // Get the new target.
+    MInsn newTargetInsn =
+        mutatableCode.getInstructionAt(mutation.newTargetIdx);
+
+    // Set the new target.
+    switchInsn.targets.remove(mutation.switchTargetIdx);
+    switchInsn.targets.add(mutation.switchTargetIdx, newTargetInsn);
+
+    Log.info("Shifted target #" + mutation.switchTargetIdx + " of " + switchInsn
+        + " to point to " + newTargetInsn);
+
+    stats.incrementStat("Shifted switch target");
+
+    // Clear cache.
+    switchInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
new file mode 100644
index 0000000..1bf6463
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/TryBlockShifter.java
@@ -0,0 +1,208 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MTryBlock;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+
+import java.util.List;
+import java.util.Random;
+
+public class TryBlockShifter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int tryIdx;
+    public boolean shiftingTryBlock; // false => shifting handler
+    public boolean shiftingStart; // false => shifting end (try block only)
+    public boolean shiftingHandlerCatchall;
+    public int shiftingHandlerIdx;
+    public int newShiftedInsnIdx;
+
+    @Override
+    public String getString() {
+      String result = String.format("%d %s %s %s %d %d",
+          tryIdx,
+          (shiftingTryBlock ? "T" : "F"),
+          (shiftingStart ? "T" : "F"),
+          (shiftingHandlerCatchall ? "T" : "F"),
+          shiftingHandlerIdx,
+          newShiftedInsnIdx
+          );
+      return result;
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      tryIdx = Integer.parseInt(elements[2]);
+      shiftingTryBlock = elements[3].equals("T");
+      shiftingStart = elements[4].equals("T");
+      shiftingHandlerCatchall = elements[5].equals("T");
+      shiftingHandlerIdx = Integer.parseInt(elements[6]);
+      newShiftedInsnIdx = Integer.parseInt(elements[7]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public TryBlockShifter() { }
+
+  public TryBlockShifter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.triesSize > 0) {
+      return true;
+    }
+
+    Log.debug("Method contains no tries.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Pick a random try.
+    int tryIdx = rng.nextInt(mutatableCode.triesSize);
+    MTryBlock tryBlock = mutatableCode.mutatableTries.get(tryIdx);
+
+    boolean shiftingTryBlock = rng.nextBoolean();
+    boolean shiftingStart = false;
+    boolean shiftingHandlerCatchall = false;
+    int shiftingHandlerIdx = -1;
+    if (shiftingTryBlock) {
+      // We're shifting the boundaries of the try block
+      // determine if we shift the start or the end.
+      shiftingStart = rng.nextBoolean();
+    } else {
+      // We're shifting the start of a handler of the try block.
+      if (tryBlock.handlers.isEmpty()) {
+        // No handlers, so we MUST mutate the catchall
+        shiftingHandlerCatchall = true;
+      } else if (tryBlock.catchAllHandler != null) {
+        // There is a catchall handler, so potentially mutate it.
+        shiftingHandlerCatchall = rng.nextBoolean();
+      }
+      // If we're not going to shift the catchall handler, then
+      // pick an explicit handler to shift.
+      if (!shiftingHandlerCatchall) {
+        shiftingHandlerIdx = rng.nextInt(tryBlock.handlers.size());
+      }
+    }
+
+    // Get the original instruction wherever we're shifting.
+    MInsn oldInsn = null;
+    if (shiftingTryBlock && shiftingStart) {
+      oldInsn = tryBlock.startInsn;
+    } else if (shiftingTryBlock && !(shiftingStart)) {
+      oldInsn = tryBlock.endInsn;
+    } else if (!(shiftingTryBlock) && shiftingHandlerCatchall) {
+      oldInsn = tryBlock.catchAllHandler;
+    } else if (!(shiftingTryBlock) && !(shiftingHandlerCatchall)
+        && (shiftingHandlerIdx != -1)) {
+      oldInsn = tryBlock.handlers.get(shiftingHandlerIdx);
+    } else {
+      Log.errorAndQuit("Faulty logic in TryBlockShifter!");
+    }
+
+    // Find the index of this instruction.
+    int oldInsnIdx = mutatableCode.getInstructionIndex(oldInsn);
+
+    int newInsnIdx = oldInsnIdx;
+
+    int delta = 0;
+
+    // Keep searching for a new index.
+    while (newInsnIdx == oldInsnIdx) {
+      // Vary by +/- 2 instructions.
+      delta = 0;
+      while (delta == 0) {
+        delta = (rng.nextInt(5) - 2);
+      }
+
+      newInsnIdx = oldInsnIdx + delta;
+
+      // Check the new index is legal.
+      if (newInsnIdx < 0) {
+        newInsnIdx = 0;
+      } else if (newInsnIdx >= mutatableCode.getInstructionCount()) {
+        newInsnIdx = mutatableCode.getInstructionCount() - 1;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.tryIdx = tryIdx;
+    mutation.shiftingTryBlock = shiftingTryBlock;
+    mutation.shiftingStart = shiftingStart;
+    mutation.shiftingHandlerCatchall = shiftingHandlerCatchall;
+    mutation.shiftingHandlerIdx = shiftingHandlerIdx;
+    mutation.newShiftedInsnIdx = newInsnIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MTryBlock tryBlock = mutatableCode.mutatableTries.get(mutation.tryIdx);
+
+    MInsn newInsn =
+        mutatableCode.getInstructionAt(mutation.newShiftedInsnIdx);
+
+    // Find the right mutatable instruction in try block, and point it at the new instruction.
+    if (mutation.shiftingTryBlock && mutation.shiftingStart) {
+      tryBlock.startInsn = newInsn;
+      Log.info("Shifted the start of try block #" + mutation.tryIdx
+          + " to be at " + newInsn);
+    } else if (mutation.shiftingTryBlock && !(mutation.shiftingStart)) {
+      tryBlock.endInsn = newInsn;
+      Log.info("Shifted the end of try block #" + mutation.tryIdx
+          + " to be at " + newInsn);
+    } else if (!(mutation.shiftingTryBlock) && mutation.shiftingHandlerCatchall) {
+      tryBlock.catchAllHandler = newInsn;
+      Log.info("Shifted the catch all handler of try block #" + mutation.tryIdx
+          + " to be at " + newInsn);
+    } else if (!(mutation.shiftingTryBlock) && !(mutation.shiftingHandlerCatchall)
+        && (mutation.shiftingHandlerIdx != -1)) {
+      tryBlock.handlers.set(mutation.shiftingHandlerIdx, newInsn);
+      Log.info("Shifted handler #" + mutation.shiftingHandlerIdx
+          + " of try block #" + mutation.tryIdx + " to be at " + newInsn);
+    } else {
+      Log.errorAndQuit("faulty logic in TryBlockShifter");
+    }
+
+    stats.incrementStat("Shifted boundary in a try block");
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java b/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java
new file mode 100644
index 0000000..d685f7d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/VRegChanger.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Random;
+
+public class VRegChanger extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int vregInsnIdx;
+    public int mutatingVreg;
+    public int newVregValue;
+
+    @Override
+    public String getString() {
+      StringBuilder builder = new StringBuilder();
+      builder.append(vregInsnIdx).append(" ");
+      builder.append(mutatingVreg).append(" ");
+      builder.append(newVregValue);
+      return builder.toString();
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      vregInsnIdx = Integer.parseInt(elements[2]);
+      mutatingVreg = Integer.parseInt(elements[3]);
+      newVregValue = Integer.parseInt(elements[4]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public VRegChanger() { }
+
+  public VRegChanger(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 60;
+  }
+
+  // A cache that should only exist between generateMutation() and applyMutation(),
+  // or be created at the start of applyMutation(), if we're reading in mutations from
+  // a file.
+  private List<MInsn> vregInsns = null;
+
+  private void generateCachedVRegInsns(MutatableCode mutatableCode) {
+    if (vregInsns != null) {
+      return;
+    }
+
+    vregInsns = new ArrayList<MInsn>();
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsVRegs) {
+        vregInsns.add(mInsn);
+      }
+    }
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    if (mutatableCode.registersSize < 2) {
+      Log.debug("Impossible to change vregs in a method with fewer than 2 registers.");
+      return false;
+    }
+
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (mInsn.insn.info.format instanceof ContainsVRegs) {
+        return true;
+      }
+    }
+
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    generateCachedVRegInsns(mutatableCode);
+
+    // Pick a random vreg instruction.
+    int vregInsnIdx = rng.nextInt(vregInsns.size());
+    MInsn vregInsn = vregInsns.get(vregInsnIdx);
+
+    // Get the number of VRegs this instruction uses.
+    int numVregs = ((ContainsVRegs)vregInsn.insn.info.format).getVRegCount();
+
+    // Pick which vreg to mutate.
+    int mutatingVreg = rng.nextInt(numVregs);
+
+    // Find the old index.
+    int oldVregValue = 0;
+
+    switch (mutatingVreg) {
+      case 0:
+        oldVregValue = (int) vregInsn.insn.vregA;
+        break;
+      case 1:
+        oldVregValue = (int) vregInsn.insn.vregB;
+        break;
+      case 2:
+        oldVregValue = (int) vregInsn.insn.vregC;
+        break;
+      default:
+        Log.errorAndQuit("Invalid number of vregs reported by a Format.");
+    }
+
+    // Search for a new vreg value.
+    int newVregValue = oldVregValue;
+    while (newVregValue == oldVregValue) {
+      newVregValue = rng.nextInt(mutatableCode.registersSize);
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.vregInsnIdx = vregInsnIdx;
+    mutation.mutatingVreg = mutatingVreg;
+    mutation.newVregValue = newVregValue;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    generateCachedVRegInsns(mutatableCode);
+
+    MInsn vregInsn = vregInsns.get(mutation.vregInsnIdx);
+
+    // Remember what the instruction used to look like.
+    String oldInsnString = vregInsn.toString();
+
+    int oldVregValue = 0;
+
+    String vregId = "A";
+    switch (mutation.mutatingVreg) {
+      case 0:
+        oldVregValue = (int) vregInsn.insn.vregA;
+        vregInsn.insn.vregA = (long) mutation.newVregValue;
+        break;
+      case 1:
+        oldVregValue = (int) vregInsn.insn.vregB;
+        vregInsn.insn.vregB = (long) mutation.newVregValue;
+        vregId = "B";
+        break;
+      case 2:
+        oldVregValue = (int) vregInsn.insn.vregC;
+        vregInsn.insn.vregC = (long) mutation.newVregValue;
+        vregId = "C";
+        break;
+      default:
+        Log.errorAndQuit("Invalid number of vregs specified in a VRegChanger mutation.");
+    }
+
+    Log.info("In " + oldInsnString + " changed v" + vregId + ": v" + oldVregValue
+        + " to v" + mutation.newVregValue);
+
+    stats.incrementStat("Changed a virtual register");
+
+    // Clear cache.
+    vregInsns = null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java b/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java
new file mode 100644
index 0000000..271aca3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/program/mutators/ValuePrinter.java
@@ -0,0 +1,266 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.program.mutators;
+
+import dexfuzz.Log;
+import dexfuzz.MutationStats;
+import dexfuzz.program.MInsn;
+import dexfuzz.program.MutatableCode;
+import dexfuzz.program.Mutation;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+
+import java.util.List;
+import java.util.Random;
+
+public class ValuePrinter extends CodeMutator {
+  /**
+   * Every CodeMutator has an AssociatedMutation, representing the
+   * mutation that this CodeMutator can perform, to allow separate
+   * generateMutation() and applyMutation() phases, allowing serialization.
+   */
+  public static class AssociatedMutation extends Mutation {
+    public int printedOutputIdx;
+
+    @Override
+    public String getString() {
+      return Integer.toString(printedOutputIdx);
+    }
+
+    @Override
+    public void parseString(String[] elements) {
+      printedOutputIdx = Integer.parseInt(elements[2]);
+    }
+  }
+
+  // The following two methods are here for the benefit of MutationSerializer,
+  // so it can create a CodeMutator and get the correct associated Mutation, as it
+  // reads in mutations from a dump of mutations.
+  @Override
+  public Mutation getNewMutation() {
+    return new AssociatedMutation();
+  }
+
+  public ValuePrinter() { }
+
+  public ValuePrinter(Random rng, MutationStats stats, List<Mutation> mutations) {
+    super(rng, stats, mutations);
+    likelihood = 40;
+  }
+
+  @Override
+  protected boolean canMutate(MutatableCode mutatableCode) {
+    for (MInsn mInsn : mutatableCode.getInstructions()) {
+      if (getInstructionOutputType(mInsn) != OutputType.UNKNOWN) {
+        return true;
+      }
+    }
+
+    Log.debug("No instructions with legible output in method, skipping.");
+    return false;
+  }
+
+  @Override
+  protected Mutation generateMutation(MutatableCode mutatableCode) {
+    // Find an instruction whose output we wish to print.
+    int printedOutputIdx = 0;
+    boolean foundInsn = false;
+
+    while (!foundInsn) {
+      printedOutputIdx = rng.nextInt(mutatableCode.getInstructionCount());
+      MInsn insnOutputToPrint =
+          mutatableCode.getInstructionAt(printedOutputIdx);
+      foundInsn = true;
+
+      // Don't want to insert instructions where there are raw instructions for now.
+      if (insnOutputToPrint.insn.justRaw) {
+        foundInsn = false;
+      }
+
+      if (getInstructionOutputType(insnOutputToPrint) == OutputType.UNKNOWN) {
+        foundInsn = false;
+      }
+    }
+
+    AssociatedMutation mutation = new AssociatedMutation();
+    mutation.setup(this.getClass(), mutatableCode);
+    mutation.printedOutputIdx = printedOutputIdx;
+    return mutation;
+  }
+
+  @Override
+  protected void applyMutation(Mutation uncastMutation) {
+    // Cast the Mutation to our AssociatedMutation, so we can access its fields.
+    AssociatedMutation mutation = (AssociatedMutation) uncastMutation;
+    MutatableCode mutatableCode = mutation.mutatableCode;
+
+    MInsn insnOutputToPrint =
+        mutatableCode.getInstructionAt(mutation.printedOutputIdx);
+
+    int outFieldIdx = mutatableCode.program.getNewItemCreator().findOrCreateFieldId(
+        "Ljava/lang/System;",
+        "Ljava/io/PrintStream;",
+        "out");
+
+    OutputType outputType = getInstructionOutputType(insnOutputToPrint);
+
+    if (outputType == OutputType.UNKNOWN) {
+      Log.errorAndQuit("Requested to print output of an instruction, whose output"
+          + " type is unknown.");
+    }
+    int printMethodIdx = mutatableCode.program.getNewItemCreator().findOrCreateMethodId(
+        "Ljava/io/PrintStream;",
+        "print",
+        outputType.getSignatureForPrintln());
+
+    boolean isWide = false;
+    boolean isRef = false;
+    if (outputType == OutputType.LONG || outputType == OutputType.DOUBLE) {
+      isWide = true;
+    }
+    if (outputType == OutputType.STRING) {
+      isRef = true;
+    }
+
+    // If we're printing a wide value, we need to allocate 3 registers!
+    if (isWide) {
+      mutatableCode.allocateTemporaryVRegs(3);
+    } else {
+      mutatableCode.allocateTemporaryVRegs(2);
+    }
+
+    int streamRegister = mutatableCode.getTemporaryVReg(0);
+    int valueRegister = mutatableCode.getTemporaryVReg(1);
+
+    // Copy the value we want to print to the 2nd temporary register
+    // Then load the out stream
+    // Then call print(out stream, value)
+
+    MInsn valueCopyInsn = new MInsn();
+    valueCopyInsn.insn = new Instruction();
+    if (isRef) {
+      valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_OBJECT_16);
+    } else if (isWide) {
+      valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_WIDE_16);
+    } else {
+      valueCopyInsn.insn.info = Instruction.getOpcodeInfo(Opcode.MOVE_16);
+    }
+    valueCopyInsn.insn.vregB = insnOutputToPrint.insn.vregA;
+    valueCopyInsn.insn.vregA = valueRegister;
+
+    MInsn streamLoadInsn = new MInsn();
+    streamLoadInsn.insn = new Instruction();
+    streamLoadInsn.insn.info = Instruction.getOpcodeInfo(Opcode.SGET_OBJECT);
+    streamLoadInsn.insn.vregB = outFieldIdx;
+    streamLoadInsn.insn.vregA = streamRegister;
+
+    MInsn invokeInsn = new MInsn();
+    invokeInsn.insn = new Instruction();
+    invokeInsn.insn.info = Instruction.getOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE);
+    if (isWide) {
+      invokeInsn.insn.vregA = 3;
+    } else {
+      invokeInsn.insn.vregA = 2;
+    }
+    invokeInsn.insn.vregB = printMethodIdx;
+    invokeInsn.insn.vregC = streamRegister;
+
+    Log.info(String.format("Printing output value of instruction %s", insnOutputToPrint));
+
+    stats.incrementStat("Printed output value");
+
+    mutatableCode.insertInstructionAfter(invokeInsn, mutation.printedOutputIdx);
+    mutatableCode.insertInstructionAfter(streamLoadInsn, mutation.printedOutputIdx);
+    mutatableCode.insertInstructionAfter(valueCopyInsn, mutation.printedOutputIdx);
+
+    mutatableCode.finishedUsingTemporaryVRegs();
+  }
+
+  private static enum OutputType {
+    STRING("(Ljava/lang/String;)V"),
+    BOOLEAN("(Z)V"),
+    BYTE("(B)V"),
+    CHAR("(C)V"),
+    SHORT("(S)V"),
+    INT("(I)V"),
+    LONG("(J)V"),
+    FLOAT("(F)V"),
+    DOUBLE("(D)V"),
+    UNKNOWN("UNKNOWN");
+
+    private String printingSignature;
+    private OutputType(String s) {
+      printingSignature = s;
+    }
+
+    public String getSignatureForPrintln() {
+      return printingSignature;
+    }
+  }
+
+  private OutputType getInstructionOutputType(MInsn mInsn) {
+    Opcode opcode = mInsn.insn.info.opcode;
+    if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
+      return OutputType.STRING;
+    }
+    if (opcode == Opcode.IGET_BOOLEAN || opcode == Opcode.SGET_BOOLEAN) {
+      return OutputType.BOOLEAN;
+    }
+    if (opcode == Opcode.IGET_BYTE || opcode == Opcode.SGET_BYTE
+        || opcode == Opcode.INT_TO_BYTE) {
+      return OutputType.BYTE;
+    }
+    if (opcode == Opcode.IGET_CHAR || opcode == Opcode.SGET_CHAR
+        || opcode == Opcode.INT_TO_CHAR) {
+      return OutputType.CHAR;
+    }
+    if (opcode == Opcode.IGET_SHORT || opcode == Opcode.SGET_SHORT
+        || opcode == Opcode.INT_TO_SHORT) {
+      return OutputType.SHORT;
+    }
+    if (opcode == Opcode.NEG_INT || opcode == Opcode.NOT_INT
+        || opcode == Opcode.LONG_TO_INT || opcode == Opcode.FLOAT_TO_INT
+        || opcode == Opcode.DOUBLE_TO_INT
+        || Opcode.isBetween(opcode, Opcode.ADD_INT, Opcode.USHR_INT)
+        || Opcode.isBetween(opcode, Opcode.ADD_INT_2ADDR, Opcode.USHR_INT_2ADDR)
+        || Opcode.isBetween(opcode, Opcode.ADD_INT_LIT16, Opcode.USHR_INT_LIT8)) {
+      return OutputType.INT;
+    }
+    if (opcode == Opcode.NEG_LONG || opcode == Opcode.NOT_LONG
+        || opcode == Opcode.INT_TO_LONG || opcode == Opcode.FLOAT_TO_LONG
+        || opcode == Opcode.DOUBLE_TO_LONG
+        || Opcode.isBetween(opcode, Opcode.ADD_LONG, Opcode.USHR_LONG)
+        || Opcode.isBetween(opcode, Opcode.ADD_LONG_2ADDR, Opcode.USHR_LONG_2ADDR)) {
+      return OutputType.LONG;
+    }
+    if (opcode == Opcode.NEG_FLOAT
+        || opcode == Opcode.INT_TO_FLOAT || opcode == Opcode.LONG_TO_FLOAT
+        || opcode == Opcode.DOUBLE_TO_FLOAT
+        || Opcode.isBetween(opcode, Opcode.ADD_FLOAT, Opcode.REM_FLOAT)
+        || Opcode.isBetween(opcode, Opcode.ADD_FLOAT_2ADDR, Opcode.REM_FLOAT_2ADDR)) {
+      return OutputType.FLOAT;
+    }
+    if (opcode == Opcode.NEG_DOUBLE
+        || opcode == Opcode.INT_TO_DOUBLE || opcode == Opcode.LONG_TO_DOUBLE
+        || opcode == Opcode.FLOAT_TO_DOUBLE
+        || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE, Opcode.REM_DOUBLE)
+        || Opcode.isBetween(opcode, Opcode.ADD_DOUBLE_2ADDR, Opcode.REM_DOUBLE_2ADDR)) {
+      return OutputType.DOUBLE;
+    }
+    return OutputType.UNKNOWN;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java
new file mode 100644
index 0000000..6bb2f96
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationElement.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationElement implements RawDexObject {
+  public int nameIdx;
+  public EncodedValue value;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    nameIdx = file.readUleb128();
+    (value = new EncodedValue()).read(file);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(nameIdx);
+    value.write(file);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+      nameIdx++;
+    }
+    value.incrementIndex(kind, insertedIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java
new file mode 100644
index 0000000..40cf5e4
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationItem.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationItem implements RawDexObject {
+  public int visibility;
+  public EncodedAnnotation annotation;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    visibility = file.readUnsignedByte();
+    (annotation = new EncodedAnnotation()).read(file);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeByte(visibility);
+    annotation.write(file);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    annotation.incrementIndex(kind, insertedIdx);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java
new file mode 100644
index 0000000..b44cd18
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationOffItem.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationOffItem implements RawDexObject {
+  public Offset annotationOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    annotationOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().tryToWriteOffset(annotationOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java
new file mode 100644
index 0000000..1e1c540
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetItem.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetItem implements RawDexObject {
+  public int size;
+  public AnnotationOffItem[] entries;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUInt();
+    entries = new AnnotationOffItem[size];
+    for (int i = 0; i < size; i++) {
+      (entries[i] = new AnnotationOffItem()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(size);
+    for (AnnotationOffItem annotationOffItem : entries) {
+      annotationOffItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java
new file mode 100644
index 0000000..cb543de
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefItem.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetRefItem implements RawDexObject {
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java
new file mode 100644
index 0000000..1d27053
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationSetRefList.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationSetRefList implements RawDexObject {
+  public int size;
+  public AnnotationSetRefItem[] list;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUInt();
+    list = new AnnotationSetRefItem[size];
+    for (int i = 0; i < size; i++) {
+      (list[i] = new AnnotationSetRefItem()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(size);
+    for (AnnotationSetRefItem annotationSetRefItem : list) {
+      annotationSetRefItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java
new file mode 100644
index 0000000..8285472
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/AnnotationsDirectoryItem.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class AnnotationsDirectoryItem implements RawDexObject {
+  public Offset classAnnotationsOff;
+  public int fieldsSize;
+  public int annotatedMethodsSize;
+  public int annotatedParametersSize;
+  public FieldAnnotation[] fieldAnnotations;
+  public MethodAnnotation[] methodAnnotations;
+  public ParameterAnnotation[] parameterAnnotations;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classAnnotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    fieldsSize = file.readUInt();
+    annotatedMethodsSize = file.readUInt();
+    annotatedParametersSize = file.readUInt();
+    if (fieldsSize != 0) {
+      fieldAnnotations = new FieldAnnotation[fieldsSize];
+      for (int i = 0; i < fieldsSize; i++) {
+        (fieldAnnotations[i] = new FieldAnnotation()).read(file);
+      }
+    }
+    if (annotatedMethodsSize != 0) {
+      methodAnnotations = new MethodAnnotation[annotatedMethodsSize];
+      for (int i = 0; i < annotatedMethodsSize; i++) {
+        (methodAnnotations[i] = new MethodAnnotation()).read(file);
+      }
+    }
+    if (annotatedParametersSize != 0) {
+      parameterAnnotations = new ParameterAnnotation[annotatedParametersSize];
+      for (int i = 0; i < annotatedParametersSize; i++) {
+        (parameterAnnotations[i] = new ParameterAnnotation()).read(file);
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.getOffsetTracker().tryToWriteOffset(classAnnotationsOff, file, false /* ULEB128 */);
+    file.writeUInt(fieldsSize);
+    file.writeUInt(annotatedMethodsSize);
+    file.writeUInt(annotatedParametersSize);
+    if (fieldAnnotations != null) {
+      for (FieldAnnotation fieldAnnotation : fieldAnnotations) {
+        fieldAnnotation.write(file);
+      }
+    }
+    if (methodAnnotations != null) {
+      for (MethodAnnotation methodAnnotation : methodAnnotations) {
+        methodAnnotation.write(file);
+      }
+    }
+    if (parameterAnnotations != null) {
+      for (ParameterAnnotation parameterAnnotation : parameterAnnotations) {
+        parameterAnnotation.write(file);
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (fieldAnnotations != null) {
+      for (FieldAnnotation fieldAnnotation : fieldAnnotations) {
+        fieldAnnotation.incrementIndex(kind, insertedIdx);
+      }
+    }
+    if (methodAnnotations != null) {
+      for (MethodAnnotation methodAnnotation : methodAnnotations) {
+        methodAnnotation.incrementIndex(kind, insertedIdx);
+      }
+    }
+    if (parameterAnnotations != null) {
+      for (ParameterAnnotation parameterAnnotation : parameterAnnotations) {
+        parameterAnnotation.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java
new file mode 100644
index 0000000..79564bc
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDataItem.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ClassDataItem implements RawDexObject {
+  public int staticFieldsSize;
+  public int instanceFieldsSize;
+  public int directMethodsSize;
+  public int virtualMethodsSize;
+
+  public EncodedField[] staticFields;
+  public EncodedField[] instanceFields;
+  public EncodedMethod[] directMethods;
+  public EncodedMethod[] virtualMethods;
+
+  public static class MetaInfo {
+    public ClassDefItem classDefItem;
+  }
+
+  public MetaInfo meta = new MetaInfo();
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    staticFieldsSize = file.readUleb128();
+    instanceFieldsSize = file.readUleb128();
+    directMethodsSize = file.readUleb128();
+    virtualMethodsSize = file.readUleb128();
+
+    staticFields = new EncodedField[staticFieldsSize];
+    for (int i = 0; i < staticFieldsSize; i++) {
+      (staticFields[i] = new EncodedField()).read(file);
+    }
+    instanceFields = new EncodedField[instanceFieldsSize];
+    for (int i = 0; i < instanceFieldsSize; i++) {
+      (instanceFields[i] = new EncodedField()).read(file);
+    }
+    directMethods = new EncodedMethod[directMethodsSize];
+    for (int i = 0; i < directMethodsSize; i++) {
+      (directMethods[i] = new EncodedMethod()).read(file);
+    }
+    virtualMethods = new EncodedMethod[virtualMethodsSize];
+    for (int i = 0; i < virtualMethodsSize; i++) {
+      (virtualMethods[i] = new EncodedMethod()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUleb128(staticFieldsSize);
+    file.writeUleb128(instanceFieldsSize);
+    file.writeUleb128(directMethodsSize);
+    file.writeUleb128(virtualMethodsSize);
+    for (int i = 0; i < staticFieldsSize; i++) {
+      staticFields[i].write(file);
+    }
+    for (int i = 0; i < instanceFieldsSize; i++) {
+      instanceFields[i].write(file);
+    }
+    for (int i = 0; i < directMethodsSize; i++) {
+      directMethods[i].write(file);
+    }
+    for (int i = 0; i < virtualMethodsSize; i++) {
+      virtualMethods[i].write(file);
+    }
+  }
+
+  private void incrementEncodedFields(int insertedIdx, EncodedField[] fields) {
+    int fieldIdx = 0;
+    for (EncodedField field : fields) {
+      fieldIdx = field.fieldIdxDiff;
+      if (fieldIdx >= insertedIdx) {
+        field.fieldIdxDiff++;
+        // Only need to increment one, as all the others are diffed from the previous.
+        break;
+      }
+    }
+  }
+
+  private void incrementEncodedMethods(int insertedIdx, EncodedMethod[] methods) {
+    int methodIdx = 0;
+    for (EncodedMethod method : methods) {
+      methodIdx = method.methodIdxDiff;
+      if (methodIdx >= insertedIdx) {
+        method.methodIdxDiff++;
+        // Only need to increment one, as all the others are diffed from the previous.
+        break;
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.FIELD_ID) {
+      incrementEncodedFields(insertedIdx, staticFields);
+      incrementEncodedFields(insertedIdx, instanceFields);
+    }
+    if (kind == IndexUpdateKind.METHOD_ID) {
+      incrementEncodedMethods(insertedIdx, directMethods);
+      incrementEncodedMethods(insertedIdx, virtualMethods);
+    }
+  }
+
+  /**
+   * For a given field index, search this ClassDataItem for a definition of this field.
+   * @return null if the field isn't in this ClassDataItem.
+   */
+  public EncodedField getEncodedFieldWithIndex(int fieldIdx) {
+    int searchFieldIdx = 0;
+    for (EncodedField field : instanceFields) {
+      searchFieldIdx += field.fieldIdxDiff;
+      if (searchFieldIdx == fieldIdx) {
+        return field;
+      }
+    }
+    searchFieldIdx = 0;
+    for (EncodedField field : staticFields) {
+      searchFieldIdx += field.fieldIdxDiff;
+      if (searchFieldIdx == fieldIdx) {
+        return field;
+      }
+    }
+    return null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java
new file mode 100644
index 0000000..5e68d24
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ClassDefItem.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ClassDefItem implements RawDexObject {
+  public static int data_size = 0x20;
+
+  public int classIdx;
+  public int accessFlags;
+  public int superclassIdx;
+  public Offset interfacesOff;
+  public int sourceFileIdx;
+  public Offset annotationsOff;
+  public Offset classDataOff;
+  public Offset staticValuesOff;
+
+  public static class MetaInfo {
+    public ClassDataItem classDataItem;
+  }
+
+  public MetaInfo meta = new MetaInfo();
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classIdx = file.readUInt();
+    accessFlags = file.readUInt();
+    superclassIdx = file.readUInt();
+    interfacesOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    sourceFileIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    classDataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    staticValuesOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(classIdx);
+    file.writeUInt(accessFlags);
+    file.writeUInt(superclassIdx);
+    file.getOffsetTracker().tryToWriteOffset(interfacesOff, file, false /* ULEB128 */);
+    file.writeUInt(sourceFileIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+    file.getOffsetTracker().tryToWriteOffset(classDataOff, file, false /* ULEB128 */);
+    file.getOffsetTracker().tryToWriteOffset(staticValuesOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+      classIdx++;
+    }
+    if (kind == IndexUpdateKind.TYPE_ID && superclassIdx >= insertedIdx) {
+      superclassIdx++;
+    }
+    if (kind == IndexUpdateKind.STRING_ID && sourceFileIdx >= insertedIdx) {
+      sourceFileIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java
new file mode 100644
index 0000000..af3c377
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/CodeItem.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+import dexfuzz.program.MutatableCode;
+
+import java.io.IOException;
+import java.util.LinkedList;
+import java.util.List;
+
+public class CodeItem implements RawDexObject {
+  public short registersSize;
+  public short insSize;
+  public short outsSize;
+  public short triesSize;
+  public int debugInfoOff; // NB: this is a special case
+  public int insnsSize;
+  public List<Instruction> insns;
+  public TryItem[] tries;
+  public EncodedCatchHandlerList handlers;
+
+  private MutatableCode mutatableCode;
+
+  public static class MethodMetaInfo {
+    public String methodName;
+    public boolean isStatic;
+    public String shorty;
+  }
+
+  public MethodMetaInfo meta = new MethodMetaInfo();
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    registersSize = file.readUShort();
+    insSize = file.readUShort();
+    outsSize = file.readUShort();
+    triesSize = file.readUShort();
+    debugInfoOff = file.readUInt();
+    insnsSize = file.readUInt();
+    populateInstructionList(file);
+    if (triesSize > 0) {
+      if ((insnsSize % 2) != 0) {
+        // Consume padding.
+        file.readUShort();
+      }
+      tries = new TryItem[triesSize];
+      for (int i = 0; i < triesSize; i++) {
+        (tries[i] = new TryItem()).read(file);
+      }
+      (handlers = new EncodedCatchHandlerList()).read(file);
+    }
+  }
+
+  private void populateInstructionList(DexRandomAccessFile file) throws IOException {
+    insns = new LinkedList<Instruction>();
+    long insnsOffset = file.getFilePointer();
+    if (insnsOffset != 0) {
+      long finger = insnsOffset;
+      long insnsEnd = insnsOffset + (2 * insnsSize);
+
+      while (finger < insnsEnd) {
+        file.seek(finger);
+        Instruction newInsn = new Instruction();
+        newInsn.read(file);
+        insns.add(newInsn);
+        finger += (2 * newInsn.getSize());
+      }
+
+      file.seek(finger);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUShort(registersSize);
+    file.writeUShort(insSize);
+    file.writeUShort(outsSize);
+    file.writeUShort(triesSize);
+    // We do not support retaining debug info currently.
+    file.writeUInt(0 /*debug_info_off*/);
+    file.writeUInt(insnsSize);
+    for (Instruction insn : insns) {
+      insn.write(file);
+    }
+    if (triesSize > 0) {
+      if ((insnsSize % 2) != 0) {
+        // produce padding
+        file.writeUShort((short) 0);
+      }
+      for (TryItem tryItem : tries) {
+        tryItem.write(file);
+      }
+      handlers.write(file);
+    }
+  }
+
+  /**
+   * CodeTranslator should call this to notify a CodeItem about its
+   * mutatable code, so it can always get the "latest" view of its
+   * instructions.
+   */
+  public void registerMutatableCode(MutatableCode mutatableCode) {
+    this.mutatableCode = mutatableCode;
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && triesSize > 0) {
+      // EncodedCatchHandlerList (well, the EncodedTypeAddrPairs it owns)
+      // are only interested in TYPE_IDs.
+      handlers.incrementIndex(kind, insertedIdx);
+    }
+
+    if (kind == IndexUpdateKind.PROTO_ID) {
+      // The only kind we can't encounter in an instruction.
+      return;
+    }
+
+    List<Instruction> insnsToIncrement = insns;
+
+    // If we have an associated MutatableCode, then it may have created some new insns
+    // that we won't know about yet, during the mutation phase.
+    //
+    // Ask for the latest view of the insns.
+    if (mutatableCode != null) {
+      insnsToIncrement = mutatableCode.requestLatestInstructions();
+    }
+
+    for (Instruction insn : insnsToIncrement) {
+      Opcode opcode = insn.info.opcode;
+      switch (kind) {
+        case STRING_ID:
+          if (opcode == Opcode.CONST_STRING || opcode == Opcode.CONST_STRING_JUMBO) {
+            // STRING@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          }
+          break;
+        case TYPE_ID:
+          if (opcode == Opcode.CONST_CLASS
+              || opcode == Opcode.CHECK_CAST
+              || opcode == Opcode.NEW_INSTANCE
+              || opcode == Opcode.FILLED_NEW_ARRAY
+              || opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
+            // TYPE@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          } else if (opcode == Opcode.INSTANCE_OF || opcode == Opcode.NEW_ARRAY) {
+            // TYPE@CCCC
+            if (insn.vregC >= insertedIdx) {
+              insn.vregC++;
+            }
+          }
+          break;
+        case FIELD_ID:
+          if (Opcode.isBetween(opcode, Opcode.SGET, Opcode.SPUT_SHORT)) {
+            // FIELD@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          } else if (Opcode.isBetween(opcode, Opcode.IGET, Opcode.IPUT_SHORT)) {
+            // FIELD@CCCC
+            if (insn.vregC >= insertedIdx) {
+              insn.vregC++;
+            }
+          }
+          break;
+        case METHOD_ID:
+          if (Opcode.isBetween(opcode, Opcode.INVOKE_VIRTUAL, Opcode.INVOKE_INTERFACE)
+              || Opcode.isBetween(opcode,
+                  Opcode.INVOKE_VIRTUAL_RANGE, Opcode.INVOKE_INTERFACE_RANGE)) {
+            // METHOD@BBBB
+            if (insn.vregB >= insertedIdx) {
+              insn.vregB++;
+            }
+          }
+          break;
+        default:
+          Log.errorAndQuit("Unexpected IndexUpdateKind requested "
+              + "in Instruction.incrementIndex()");
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
new file mode 100644
index 0000000..922ee58
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/DebugInfoItem.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+// Right now we are not parsing debug_info_item, just take the raw size
+public class DebugInfoItem implements RawDexObject {
+  private int size;
+  private byte[] data;
+
+  public DebugInfoItem(int size) {
+    this.size = size;
+  }
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    data = new byte[size];
+    file.read(data);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.write(data);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java b/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java
new file mode 100644
index 0000000..ec75585
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/DexRandomAccessFile.java
@@ -0,0 +1,319 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.RandomAccessFile;
+
+/**
+ * An extension to RandomAccessFile that allows reading/writing
+ * DEX files in little-endian form, the variable-length LEB format
+ * and also provides word-alignment functions.
+ */
+public class DexRandomAccessFile extends RandomAccessFile {
+  private OffsetTracker offsetTracker;
+
+  public OffsetTracker getOffsetTracker() {
+    return offsetTracker;
+  }
+
+  public void setOffsetTracker(OffsetTracker offsetTracker) {
+    this.offsetTracker = offsetTracker;
+  }
+
+  /**
+   * Constructor, passes straight on to RandomAccessFile currently.
+   * @param filename The file to open.
+   * @param mode Strings "r" or "rw" work best.
+   */
+  public DexRandomAccessFile(String filename, String mode)
+      throws FileNotFoundException {
+    super(filename, mode);
+  }
+
+  /**
+   * @return A 16-bit number, read from the file as little-endian.
+   */
+  public short readUShort() throws IOException {
+    int b1 = readUnsignedByte();
+    int b2 = readUnsignedByte();
+    return (short) ((b2 << 8) | b1);
+  }
+
+  /**
+   * @param value A 16-bit number to be written to the file in little-endian.
+   */
+  public void writeUShort(short value) throws IOException {
+    int b1 = value & 0xff;
+    int b2 = (value & 0xff00) >> 8;
+    writeByte(b1);
+    writeByte(b2);
+  }
+
+  /**
+   * @return A 32-bit number, read from the file as little-endian.
+   */
+  public int readUInt() throws IOException {
+    int b1 = readUnsignedByte();
+    int b2 = readUnsignedByte();
+    int b3 = readUnsignedByte();
+    int b4 = readUnsignedByte();
+    return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
+  }
+
+  /**
+   * @param value A 32-bit number to be written to the file in little-endian.
+   */
+  public void writeUInt(int value) throws IOException {
+    int b1 = value & 0xff;
+    writeByte(b1);
+    int b2 = (value & 0xff00) >> 8;
+    writeByte(b2);
+    int b3 = (value & 0xff0000) >> 16;
+    writeByte(b3);
+    int b4 = (value & 0xff000000) >> 24;
+    writeByte(b4);
+  }
+
+  /**
+   * @return An up to 32-bit number, read from the file in ULEB128 form.
+   */
+  public int readUleb128() throws IOException {
+    int shift = 0;
+    int value = 0;
+    int rawByte = readUnsignedByte();
+    boolean done = false;
+    while (!done) {
+      // Get the lower seven bits.
+      // 0x7f = 0111 1111
+      value |= ((rawByte & 0x7f) << shift);
+      shift += 7;
+      // Check the 8th bit - if it's 0, we're done.
+      // 0x80 = 1000 0000
+      if ((rawByte & 0x80) == 0) {
+        done = true;
+      } else {
+        rawByte = readUnsignedByte();
+      }
+    }
+    return value;
+  }
+
+  /**
+   * @param value A 32-bit number to be written to the file in ULEB128 form.
+   */
+  public void writeUleb128(int value) throws IOException {
+    if (value == 0) {
+      writeByte(0);
+      return;
+    }
+
+    while (value != 0) {
+      int marker = 1;
+      // If we're down to the last 7 bits, the marker will be 0.
+      if ((value & 0xffffff80) == 0) {
+        marker = 0;
+      }
+      // Get the lowest 7 bits, add on the marker in the high bit.
+      int nextByte = value & 0x7f | (marker << 7);
+      writeByte(nextByte);
+      value >>>= 7;
+    }
+  }
+
+  /**
+   * Write out ULEB128 value always using 5 bytes.
+   * A version of ULEB128 that will always write out 5 bytes, because this
+   * value will be patched later, and if we used a smaller encoding, the new value
+   * may overflow the previously selected encoding size.
+   * The largest encoding for 0 in ULEB128 would be:
+   *   0x80 0x80 0x80 0x80 0x00
+   * and for 1 would be:
+   *   0x81 0x80 0x80 0x80 0x00
+   */
+  public void writeLargestUleb128(int value) throws IOException {
+    Log.debug("Writing " + value + " using the largest possible ULEB128 encoding.");
+    if (value == 0) {
+      writeByte(0x80);
+      writeByte(0x80);
+      writeByte(0x80);
+      writeByte(0x80);
+      writeByte(0x0);
+      return;
+    }
+
+    for (int i = 0; i < 5; i++) {
+      int marker = 1;
+      // If we're writing the 5th byte, the marker is 0.
+      if (i == 4) {
+        marker = 0;
+      }
+      // Get the lowest 7 bits, add on the marker in the high bit.
+      int nextByte = value & 0x7f | (marker << 7);
+      writeByte(nextByte);
+      value >>>= 7;
+    }
+  }
+
+  /**
+   * @return An up to 32-bit number, read from the file in SLEB128 form.
+   */
+  public int readSleb128() throws IOException {
+    int shift = 0;
+    int value = 0;
+    int rawByte = readUnsignedByte();
+    boolean done = false;
+    boolean mustSignExtend = false;
+    while (!done) {
+      // Get the lower seven bits.
+      // 0x7f = 0111 1111
+      value |= ((rawByte & 0x7f) << shift);
+      shift += 7;
+      // Check the 8th bit - if it's 0, we're done.
+      // 0x80 = 1000 0000
+      if ((rawByte & 0x80) == 0) {
+        // Check the 7th bit - if it's a 1, we need to sign extend.
+        if ((rawByte & 0x60) != 0) {
+          mustSignExtend = true;
+        }
+        done = true;
+      } else {
+        rawByte = readUnsignedByte();
+      }
+    }
+    if (mustSignExtend) {
+      // Example:
+      // say we shifted 7 bits, we need
+      // to make all the upper 25 bits 1s.
+      // load a 1...
+      // 00000000 00000000 00000000 00000001
+      // << 7
+      // 00000000 00000000 00000000 10000000
+      // - 1
+      // 00000000 00000000 00000000 01111111
+      // ~
+      // 11111111 11111111 11111111 10000000
+      int upperOnes = ~((1 << shift) - 1);
+      value |= (upperOnes);
+    }
+    return value;
+  }
+
+  /**
+   * @param value A 32-bit number to be written to the file in SLEB128 form.
+   */
+  public void writeSleb128(int value) throws IOException {
+    if (value == 0) {
+      writeByte(0);
+      return;
+    }
+    if (value > 0) {
+      writeUleb128(value);
+      return;
+    }
+    if (value == -1) {
+      writeByte(0x7f);
+    }
+
+    // When it's all 1s (0xffffffff), we're done!
+    while (value != 0xffffffff) {
+      int marker = 1;
+      // If we're down to the last 7 bits (i.e., shifting a further 7 is all 1s),
+      // the marker will be 0.
+      if ((value >> 7) == 0xffffffff) {
+        marker = 0;
+      }
+      // Get the lowest 7 bits, add on the marker in the high bit.
+      int nextByte = value & 0x7f | (marker << 7);
+      writeByte(nextByte);
+      value >>= 7;
+    }
+  }
+
+  /**
+   * In DEX format, strings are in MUTF-8 format, the first ULEB128 value is the decoded size
+   * (i.e., string.length), and then follows a null-terminated series of characters.
+   * @param decodedSize The ULEB128 value that should have been read just before this.
+   * @return The raw bytes of the string, not including the null character.
+   */
+  public byte[] readDexUtf(int decodedSize) throws IOException {
+    // In the dex MUTF-8, the encoded size can never be larger than 3 times
+    // the actual string's length (which is the ULEB128 value just before this
+    // string, the "decoded size")
+
+    // Therefore, allocate as much space as we might need.
+    byte[] str = new byte[decodedSize * 3];
+
+    // Get our first byte.
+    int encodedSize = 0;
+    byte rawByte = readByte();
+
+    // Keep reading until we find the end marker.
+    while (rawByte != 0) {
+      str[encodedSize++] = rawByte;
+      rawByte = readByte();
+    }
+
+    // Copy everything we read into str into the correctly-sized actual string.
+    byte[] actualString = new byte[encodedSize];
+    for (int i = 0; i < encodedSize; i++) {
+      actualString[i] = str[i];
+    }
+
+    return actualString;
+  }
+
+  /**
+   * Writes out raw bytes that would have been read by readDexUTF().
+   * Will automatically write out the null-byte at the end.
+   * @param data Bytes to be written out.
+   */
+  public void writeDexUtf(byte[] data) throws IOException {
+    write(data);
+    // Remember to add the end marker.
+    writeByte(0);
+  }
+
+  /**
+   * Align the file handle's seek pointer to the next N bytes.
+   * @param alignment N to align to.
+   */
+  public void alignForwards(int alignment) throws IOException {
+    long offset = getFilePointer();
+    long mask = alignment - 1;
+    if ((offset & mask) != 0) {
+      int extra = alignment - (int) (offset & mask);
+      seek(offset + extra);
+    }
+  }
+
+  /**
+   * Align the file handle's seek pointer backwards to the previous N bytes.
+   * @param alignment N to align to.
+   */
+  public void alignBackwards(int alignment) throws IOException {
+    long offset = getFilePointer();
+    long mask = alignment - 1;
+    if ((offset & mask) != 0) {
+      offset &= (~mask);
+      seek(offset);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java
new file mode 100644
index 0000000..085a4a8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedAnnotation.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedAnnotation implements RawDexObject {
+  public int typeIdx;
+  public int size;
+  public AnnotationElement[] elements;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    typeIdx = file.readUleb128();
+    size = file.readUleb128();
+    if (size != 0) {
+      elements = new AnnotationElement[size];
+      for (int i = 0; i < size; i++) {
+        (elements[i] = new AnnotationElement()).read(file);
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(typeIdx);
+    file.writeUleb128(size);
+    if (size != 0) {
+      for (AnnotationElement annotationElement : elements) {
+        annotationElement.write(file);
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+    if (size != 0) {
+      for (AnnotationElement element : elements) {
+        element.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java
new file mode 100644
index 0000000..267ff09
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArray.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedArray implements RawDexObject {
+  public int size;
+  public EncodedValue[] values;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    size = file.readUleb128();
+    if (size != 0) {
+      values = new EncodedValue[size];
+      for (int i = 0; i < size; i++) {
+        (values[i] = new EncodedValue()).read(file);
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(size);
+    if (size != 0) {
+      for (EncodedValue encodedValue : values) {
+        encodedValue.write(file);
+      }
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (size != 0) {
+      for (EncodedValue value : values) {
+        value.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java
new file mode 100644
index 0000000..e461a54
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedArrayItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedArrayItem implements RawDexObject {
+  public EncodedArray value;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    (value = new EncodedArray()).read(file);
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    value.write(file);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java
new file mode 100644
index 0000000..83786c8
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandler.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedCatchHandler implements RawDexObject {
+  public int size;
+  public EncodedTypeAddrPair[] handlers;
+  public int catchAllAddr;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    size = file.readSleb128();
+    int absoluteSize = Math.abs(size);
+    if (absoluteSize > 0) {
+      handlers = new EncodedTypeAddrPair[absoluteSize];
+      for (int i = 0; i < Math.abs(size); i++) {
+        (handlers[i] = new EncodedTypeAddrPair()).read(file);
+      }
+    }
+    if (size <= 0) {
+      catchAllAddr = file.readUleb128();
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeSleb128(size);
+    if (handlers != null) {
+      for (EncodedTypeAddrPair encodedTypeAddrPair : handlers) {
+        encodedTypeAddrPair.write(file);
+      }
+    }
+    if (size <= 0) {
+      file.writeUleb128(catchAllAddr);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (handlers != null) {
+      for (EncodedTypeAddrPair handler : handlers) {
+        handler.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java
new file mode 100644
index 0000000..5619b5f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedCatchHandlerList.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedCatchHandlerList implements RawDexObject {
+  public int size;
+  public EncodedCatchHandler[] list;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    size = file.readUleb128();
+    list = new EncodedCatchHandler[size];
+    for (int i = 0; i < size; i++) {
+      (list[i] = new EncodedCatchHandler()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(size);
+    for (EncodedCatchHandler encodedCatchHandler : list) {
+      encodedCatchHandler.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    for (EncodedCatchHandler handler : list) {
+      handler.incrementIndex(kind, insertedIdx);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java
new file mode 100644
index 0000000..18c1d43
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedField.java
@@ -0,0 +1,79 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedField implements RawDexObject {
+  public int fieldIdxDiff;
+  public int accessFlags;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    fieldIdxDiff = file.readUleb128();
+    accessFlags = file.readUleb128();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(fieldIdxDiff);
+    file.writeUleb128(accessFlags);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+    // NB: our idx_diff is handled in ClassDataItem...
+  }
+
+  public boolean isVolatile() {
+    return ((accessFlags & Flag.ACC_VOLATILE.getValue()) != 0);
+  }
+
+  /**
+   * Set/unset the volatile flag for this EncodedField.
+   */
+  public void setVolatile(boolean turnOn) {
+    if (turnOn) {
+      accessFlags |= Flag.ACC_VOLATILE.getValue();
+    } else {
+      accessFlags &= ~(Flag.ACC_VOLATILE.getValue());
+    }
+  }
+
+  private static enum Flag {
+    ACC_PUBLIC(0x1),
+    ACC_PRIVATE(0x2),
+    ACC_PROTECTED(0x4),
+    ACC_STATIC(0x8),
+    ACC_FINAL(0x10),
+    ACC_VOLATILE(0x40),
+    ACC_TRANSIENT(0x80),
+    ACC_SYNTHETIC(0x1000),
+    ACC_ENUM(0x4000);
+
+    private int value;
+
+    private Flag(int value) {
+      this.value = value;
+    }
+
+    public int getValue() {
+      return value;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java
new file mode 100644
index 0000000..3a8163a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedMethod.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+
+public class EncodedMethod implements RawDexObject {
+  public int methodIdxDiff;
+  public int accessFlags;
+  public Offset codeOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    methodIdxDiff = file.readUleb128();
+    accessFlags = file.readUleb128();
+    codeOff = file.getOffsetTracker().getNewOffset(file.readUleb128());
+    if (isNative()) {
+      Log.errorAndQuit("Sorry, DEX files with native methods are not supported yet.");
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(methodIdxDiff);
+    file.writeUleb128(accessFlags);
+    file.getOffsetTracker().tryToWriteOffset(codeOff, file, true /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+    // NB: our idx_diff is handled in ClassDataItem...
+  }
+
+  public boolean isStatic() {
+    return ((accessFlags & Flag.ACC_STATIC.getValue()) != 0);
+  }
+
+  public boolean isNative() {
+    return ((accessFlags & Flag.ACC_NATIVE.getValue()) != 0);
+  }
+
+  /**
+   * Set/unset the static flag for this EncodedMethod.
+   */
+  public void setStatic(boolean turnOn) {
+    if (turnOn) {
+      accessFlags |= Flag.ACC_STATIC.getValue();
+    } else {
+      accessFlags &= ~(Flag.ACC_STATIC.getValue());
+    }
+  }
+
+  private static enum Flag {
+    ACC_PUBLIC(0x1),
+    ACC_PRIVATE(0x2),
+    ACC_PROTECTED(0x4),
+    ACC_STATIC(0x8),
+    ACC_FINAL(0x10),
+    ACC_SYNCHRONIZED(0x20),
+    ACC_VARARGS(0x80),
+    ACC_NATIVE(0x100),
+    ACC_ABSTRACT(0x400),
+    ACC_STRICT(0x800),
+    ACC_SYNTHETIC(0x1000),
+    ACC_ENUM(0x4000),
+    ACC_CONSTRUCTOR(0x10000);
+
+    private int value;
+
+    private Flag(int value) {
+      this.value = value;
+    }
+
+    public int getValue() {
+      return value;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java
new file mode 100644
index 0000000..ea49ae1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedTypeAddrPair.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedTypeAddrPair implements RawDexObject {
+  public int typeIdx;
+  public int addr;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    typeIdx = file.readUleb128();
+    addr = file.readUleb128();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUleb128(typeIdx);
+    file.writeUleb128(addr);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java
new file mode 100644
index 0000000..fdf133f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/EncodedValue.java
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class EncodedValue implements RawDexObject {
+  public byte valueArg;
+  public byte valueType;
+  public byte[] value;
+  public EncodedArray encodedArray;
+  public EncodedAnnotation encodedAnnotation;
+
+  private static final byte VALUE_BYTE = 0x00;
+  private static final byte VALUE_ARRAY = 0x1c;
+  private static final byte VALUE_ANNOTATION = 0x1d;
+  private static final byte VALUE_NULL = 0x1e;
+  private static final byte VALUE_BOOLEAN = 0x1f;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    int valueArgAndType = file.readUnsignedByte();
+
+    // Get lower 5 bits.
+    valueType = (byte) (valueArgAndType & 0x1f);
+    // Get upper 3 bits.
+    valueArg = (byte) ((valueArgAndType & 0xe0) >> 5);
+
+    int size = 0;
+
+    switch (valueType) {
+      case VALUE_BYTE:
+        size = 1;
+        break;
+      case VALUE_ARRAY:
+        (encodedArray = new EncodedArray()).read(file);
+        size = 0; // So we don't read into value.
+        break;
+      case VALUE_ANNOTATION:
+        (encodedAnnotation = new EncodedAnnotation()).read(file);
+        size = 0; // So we don't read into value.
+        break;
+      case VALUE_NULL:
+      case VALUE_BOOLEAN:
+        // No value
+        size = 0;
+        break;
+      default:
+        // All others encode value_arg as (size - 1), so...
+        size = valueArg + 1;
+        break;
+    }
+
+    if (size != 0) {
+      value = new byte[size];
+      for (int i = 0; i < size; i++) {
+        value[i] = file.readByte();
+      }
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    int valueArgAndType = ((valueType) | (valueArg << 5));
+    file.writeByte(valueArgAndType);
+
+    if (encodedArray != null) {
+      encodedArray.write(file);
+    } else if (encodedAnnotation != null) {
+      encodedAnnotation.write(file);
+    } else if (value != null) {
+      file.write(value);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (encodedArray != null) {
+      encodedArray.incrementIndex(kind, insertedIdx);
+    } else if (encodedAnnotation != null) {
+      encodedAnnotation.incrementIndex(kind, insertedIdx);
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java
new file mode 100644
index 0000000..98f812e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/FieldAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class FieldAnnotation implements RawDexObject {
+  public int fieldIdx;
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    fieldIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(fieldIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.FIELD_ID && fieldIdx >= insertedIdx) {
+      fieldIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java
new file mode 100644
index 0000000..75f2078
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/FieldIdItem.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class FieldIdItem implements RawDexObject {
+  public short classIdx;
+  public short typeIdx;
+  public int nameIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classIdx = file.readUShort();
+    typeIdx = file.readUShort();
+    nameIdx = file.readUInt();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUShort(classIdx);
+    file.writeUShort(typeIdx);
+    file.writeUInt(nameIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+      classIdx++;
+    }
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+    if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+      nameIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java
new file mode 100644
index 0000000..e6b290c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/HeaderItem.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+
+public class HeaderItem implements RawDexObject {
+  public byte[] magic;
+  public int checksum;
+  public byte[] signature; // Verification doesn't depend on this, so we don't update it.
+  public int fileSize;
+  public int headerSize;
+  public int endianTag;
+  public int linkSize;
+  public Offset linkOff;
+  public Offset mapOff;
+  public int stringIdsSize;
+  public Offset stringIdsOff;
+  public int typeIdsSize;
+  public Offset typeIdsOff;
+  public int protoIdsSize;
+  public Offset protoIdsOff;
+  public int fieldIdsSize;
+  public Offset fieldIdsOff;
+  public int methodIdsSize;
+  public Offset methodIdsOff;
+  public int classDefsSize;
+  public Offset classDefsOff;
+  public int dataSize;
+  public Offset dataOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    magic = new byte[8];
+    for (int i = 0; i < 8; i++) {
+      magic[i] = file.readByte();
+    }
+    checksum = file.readUInt();
+    signature = new byte[20];
+    for (int i = 0; i < 20; i++) {
+      signature[i] = file.readByte();
+    }
+    fileSize = file.readUInt();
+    headerSize = file.readUInt();
+    endianTag = file.readUInt();
+    linkSize = file.readUInt();
+    linkOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    mapOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    stringIdsSize = file.readUInt();
+    stringIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    typeIdsSize = file.readUInt();
+    typeIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    protoIdsSize = file.readUInt();
+    protoIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    fieldIdsSize = file.readUInt();
+    fieldIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    methodIdsSize = file.readUInt();
+    methodIdsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    classDefsSize = file.readUInt();
+    classDefsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    dataSize = file.readUInt();
+    dataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+    if (headerSize != 0x70) {
+      Log.errorAndQuit("Invalid header size in header.");
+    }
+    if (file.getFilePointer() != headerSize) {
+      Log.errorAndQuit("Read a different amount than expected in header: "
+          + file.getFilePointer());
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    for (int i = 0; i < 8; i++) {
+      file.writeByte(magic[i]);
+    }
+    // Will be recalculated later!
+    file.writeUInt(checksum);
+    for (int i = 0; i < 20; i++) {
+      file.writeByte(signature[i]);
+    }
+    // Will be recalculated later!
+    file.writeUInt(fileSize);
+    file.writeUInt(headerSize);
+    file.writeUInt(endianTag);
+    file.writeUInt(linkSize);
+    file.getOffsetTracker().tryToWriteOffset(linkOff, file, false /* ULEB128 */);
+    file.getOffsetTracker().tryToWriteOffset(mapOff, file, false /* ULEB128 */);
+    file.writeUInt(stringIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(stringIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(typeIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(typeIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(protoIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(protoIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(fieldIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(fieldIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(methodIdsSize);
+    file.getOffsetTracker().tryToWriteOffset(methodIdsOff, file, false /* ULEB128 */);
+    file.writeUInt(classDefsSize);
+    file.getOffsetTracker().tryToWriteOffset(classDefsOff, file, false /* ULEB128 */);
+    // will be recalculated later
+    file.writeUInt(dataSize);
+    file.getOffsetTracker().tryToWriteOffset(dataOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java b/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java
new file mode 100644
index 0000000..2dda78f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Instruction.java
@@ -0,0 +1,584 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+import dexfuzz.rawdex.formats.AbstractFormat;
+import dexfuzz.rawdex.formats.ContainsConst;
+import dexfuzz.rawdex.formats.ContainsPoolIndex;
+import dexfuzz.rawdex.formats.ContainsTarget;
+import dexfuzz.rawdex.formats.ContainsVRegs;
+import dexfuzz.rawdex.formats.Format10t;
+import dexfuzz.rawdex.formats.Format10x;
+import dexfuzz.rawdex.formats.Format11n;
+import dexfuzz.rawdex.formats.Format11x;
+import dexfuzz.rawdex.formats.Format12x;
+import dexfuzz.rawdex.formats.Format20t;
+import dexfuzz.rawdex.formats.Format21c;
+import dexfuzz.rawdex.formats.Format21h;
+import dexfuzz.rawdex.formats.Format21s;
+import dexfuzz.rawdex.formats.Format21t;
+import dexfuzz.rawdex.formats.Format22b;
+import dexfuzz.rawdex.formats.Format22c;
+import dexfuzz.rawdex.formats.Format22s;
+import dexfuzz.rawdex.formats.Format22t;
+import dexfuzz.rawdex.formats.Format22x;
+import dexfuzz.rawdex.formats.Format23x;
+import dexfuzz.rawdex.formats.Format30t;
+import dexfuzz.rawdex.formats.Format31c;
+import dexfuzz.rawdex.formats.Format31i;
+import dexfuzz.rawdex.formats.Format31t;
+import dexfuzz.rawdex.formats.Format32x;
+import dexfuzz.rawdex.formats.Format35c;
+import dexfuzz.rawdex.formats.Format3rc;
+import dexfuzz.rawdex.formats.Format51l;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+public class Instruction implements RawDexObject {
+  // Only used by Format35* instructions
+  public static class InvokeFormatInfo {
+    public byte vregD;
+    public byte vregE;
+    public byte vregF;
+    public byte vregG;
+  }
+
+  // Immutable information about this class of instruction.
+  public OpcodeInfo info;
+
+  // The raw bytes of the instruction.
+  // Only used during reading, and writing out is done from the decoded instruction data.
+  //  Except in the case of the 3 "data" instructions.
+  public byte[] rawBytes;
+
+  public static final int RAW_TYPE_PACKED_SWITCH_DATA = 1;
+  public static final int RAW_TYPE_SPARSE_SWITCH_DATA = 2;
+  public static final int RAW_TYPE_FILL_ARRAY_DATA_DATA = 3;
+
+  public int rawType;
+  public boolean justRaw;
+  public int rawSize;
+
+  public long vregA = 0;
+  public long vregB = 0;
+  public long vregC = 0;
+
+  public InvokeFormatInfo invokeFormatInfo;
+
+  /**
+   * Clone an instruction.
+   */
+  public Instruction clone() {
+    Instruction newInsn = new Instruction();
+    // If we've generated a new instruction, we won't have calculated its raw array.
+    if (newInsn.rawBytes != null) {
+      newInsn.rawBytes = new byte[rawBytes.length];
+      for (int i = 0; i < rawBytes.length; i++) {
+        newInsn.rawBytes[i] = rawBytes[i];
+      }
+    }
+    newInsn.justRaw = justRaw;
+    newInsn.rawType = rawType;
+    newInsn.rawSize = rawSize;
+
+    newInsn.vregA = vregA;
+    newInsn.vregB = vregB;
+    newInsn.vregC = vregC;
+    newInsn.info = info;
+    if (invokeFormatInfo != null) {
+      newInsn.invokeFormatInfo = new InvokeFormatInfo();
+      newInsn.invokeFormatInfo.vregD = invokeFormatInfo.vregD;
+      newInsn.invokeFormatInfo.vregE = invokeFormatInfo.vregE;
+      newInsn.invokeFormatInfo.vregF = invokeFormatInfo.vregF;
+      newInsn.invokeFormatInfo.vregG = invokeFormatInfo.vregG;
+    }
+    return newInsn;
+  }
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    // Remember the offset, so after reading the opcode, we can read the whole
+    // insn into raw_bytes.
+    long offset = file.getFilePointer();
+    int opcodeValue = readOpcode(file);
+    info = getOpcodeInfo(opcodeValue);
+    if (info == null) {
+      Log.errorAndQuit("Couldn't find OpcodeInfo for opcode with value: "
+          + opcodeValue);
+    }
+
+    rawBytes = new byte[2 * getSize()];
+    file.seek(offset);
+    file.read(rawBytes);
+
+    vregA = info.format.getA(rawBytes);
+    vregB = info.format.getB(rawBytes);
+    vregC = info.format.getC(rawBytes);
+
+    // Special case for 35* formats.
+    if (info.format.needsInvokeFormatInfo()) {
+      invokeFormatInfo = new InvokeFormatInfo();
+      invokeFormatInfo.vregD = (byte) (rawBytes[4] >> 4);
+      invokeFormatInfo.vregE = (byte) (rawBytes[5] & 0xf);
+      invokeFormatInfo.vregF = (byte) (rawBytes[5] >> 4);
+      invokeFormatInfo.vregG = (byte) (rawBytes[1] & 0xf);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    if (justRaw) {
+      // It is the responsibility of the CodeTranslator to make
+      // sure the raw bytes have been updated.
+      file.write(rawBytes);
+    } else {
+      info.format.writeToFile(file, this);
+    }
+  }
+
+  /**
+   * Get the size of an instruction, in code-words. (Code-words are 16-bits.)
+   */
+  public int getSize() {
+    if (justRaw) {
+      // It is the responsibility of the CodeTranslator to make sure
+      // the raw size has been updated.
+      return rawSize;
+    }
+    return info.format.getSize();
+  }
+
+  private int readOpcode(DexRandomAccessFile file) throws IOException {
+    short firstCodeWord = file.readUShort();
+    int opcode = (firstCodeWord & 0xff);
+    int upperBits = (firstCodeWord & 0xff00) >> 8;
+    if (opcode == 0x0 && upperBits != 0x0) {
+      justRaw = true;
+      rawType = upperBits;
+      // Need to calculate special sizes.
+      switch (rawType) {
+        case RAW_TYPE_PACKED_SWITCH_DATA:
+          rawSize = (file.readUShort() * 2) + 4;
+          break;
+        case RAW_TYPE_SPARSE_SWITCH_DATA:
+          rawSize = (file.readUShort() * 4) + 2;
+          break;
+        case RAW_TYPE_FILL_ARRAY_DATA_DATA:
+        {
+          int elementWidth = file.readUShort();
+          rawSize = ((file.readUInt() * elementWidth + 1) / 2) + 4;
+          break;
+        }
+        default:
+          Log.errorAndQuit("Unrecognised ident in data-payload instruction: " + rawType);
+      }
+    }
+    return opcode;
+  }
+
+  @Override
+  public String toString() {
+    if (justRaw) {
+      switch (rawType) {
+        case RAW_TYPE_PACKED_SWITCH_DATA:
+          return "PACKED SWITCH DATA";
+        case RAW_TYPE_SPARSE_SWITCH_DATA:
+          return "SPARSE SWITCH DATA";
+        case RAW_TYPE_FILL_ARRAY_DATA_DATA:
+          return "FILL ARRAY DATA DATA";
+        default:
+      }
+
+    }
+
+    String repr = info.name;
+
+    AbstractFormat format = info.format;
+
+    if (invokeFormatInfo != null) {
+      String vregs = "";
+
+      int numVregs = (int) vregA;
+
+      if (numVregs > 5) {
+        Log.debug("vA in an 35c invoke was greater than 5? Assuming 5.");
+        numVregs = 5;
+      } else if (numVregs < 0) {
+        Log.debug("vA in an 35c invoke was less than 0? Assuming 0.");
+        numVregs = 0;
+      }
+
+      switch (numVregs) {
+        case 5:
+          vregs = ", v" + invokeFormatInfo.vregG;
+          // fallthrough
+        case 4:
+          vregs = ", v" + invokeFormatInfo.vregF + vregs;
+          // fallthrough
+        case 3:
+          vregs = ", v" + invokeFormatInfo.vregE + vregs;
+          // fallthrough
+        case 2:
+          vregs = ", v" + invokeFormatInfo.vregD + vregs;
+          // fallthrough
+        case 1:
+          vregs = "v" + vregC + vregs;
+          break;
+        default:
+      }
+
+      repr += "(" + vregs + ")";
+
+      long poolIndex = ((ContainsPoolIndex)format).getPoolIndex(this);
+      repr += " meth@" + poolIndex;
+
+      return repr;
+    }
+
+
+
+    if (format instanceof ContainsVRegs) {
+      String vregs = "";
+      switch (((ContainsVRegs)format).getVRegCount()) {
+        case 3:
+          vregs = ", v" + vregC;
+          // fallthrough
+        case 2:
+          vregs = ", v" + vregB + vregs;
+          // fallthrough
+        case 1:
+          vregs = "v" + vregA + vregs;
+          break;
+        default:
+          Log.errorAndQuit("Invalid number of vregs reported by a Format.");
+      }
+
+      repr += " " + vregs;
+    }
+    if (format instanceof ContainsConst) {
+      long constant = ((ContainsConst)format).getConst(this);
+      repr += " #" + constant;
+    }
+    if (format instanceof ContainsPoolIndex) {
+      long poolIndex = ((ContainsPoolIndex)format).getPoolIndex(this);
+      repr += " pool@" + poolIndex;
+    }
+    if (format instanceof ContainsTarget) {
+      long target = ((ContainsTarget)format).getTarget(this);
+      repr += " +" + target;
+    }
+
+    return repr;
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+
+  // STATIC INSTRUCTION CODE
+  private static Map<Integer,OpcodeInfo> opcode_map_by_int = new HashMap<Integer,OpcodeInfo>();
+  private static Map<Opcode,OpcodeInfo> opcode_map_by_enum = new HashMap<Opcode,OpcodeInfo>();
+
+  public static OpcodeInfo getOpcodeInfo(Opcode opcode) {
+    return opcode_map_by_enum.get(opcode);
+  }
+
+  public static OpcodeInfo getOpcodeInfo(int opcodeValue) {
+    return opcode_map_by_int.get(opcodeValue);
+  }
+
+  private static void addOpcodeInfo(Opcode opcode, String name,
+      int opcodeValue, AbstractFormat fmt) {
+    OpcodeInfo info = new OpcodeInfo(opcode, name, opcodeValue, fmt);
+    if (opcode.ordinal() != opcodeValue) {
+      Log.errorAndQuit(String.format("Opcode: %s (enum ordinal 0x%x) != (value 0x%x)",
+          opcode.toString(), opcode.ordinal(), opcodeValue));
+    }
+    opcode_map_by_int.put(opcodeValue, info);
+    opcode_map_by_enum.put(opcode, info);
+  }
+
+  static {
+    addOpcodeInfo(Opcode.NOP, "nop", 0x00, new Format10x());
+    addOpcodeInfo(Opcode.MOVE, "move", 0x01, new Format12x());
+    addOpcodeInfo(Opcode.MOVE_FROM16, "move/from16", 0x02, new Format22x());
+    addOpcodeInfo(Opcode.MOVE_16, "move/16", 0x03, new Format32x());
+    addOpcodeInfo(Opcode.MOVE_WIDE, "move-wide", 0x04, new Format12x());
+    addOpcodeInfo(Opcode.MOVE_WIDE_FROM16, "move-wide/from16", 0x05, new Format22x());
+    addOpcodeInfo(Opcode.MOVE_WIDE_16, "move-wide/16", 0x06, new Format32x());
+    addOpcodeInfo(Opcode.MOVE_OBJECT, "move-object", 0x07, new Format12x());
+    addOpcodeInfo(Opcode.MOVE_OBJECT_FROM16, "move-object/from16", 0x08, new Format22x());
+    addOpcodeInfo(Opcode.MOVE_OBJECT_16, "move-object/16", 0x09, new Format32x());
+    addOpcodeInfo(Opcode.MOVE_RESULT, "move-result", 0x0a, new Format11x());
+    addOpcodeInfo(Opcode.MOVE_RESULT_WIDE, "move-result-wide", 0x0b, new Format11x());
+    addOpcodeInfo(Opcode.MOVE_RESULT_OBJECT, "move-result-object", 0x0c, new Format11x());
+    addOpcodeInfo(Opcode.MOVE_EXCEPTION, "move-exception", 0x0d, new Format11x());
+    addOpcodeInfo(Opcode.RETURN_VOID, "return-void", 0x0e, new Format10x());
+    addOpcodeInfo(Opcode.RETURN, "return", 0x0f, new Format11x());
+    addOpcodeInfo(Opcode.RETURN_WIDE, "return-wide", 0x10, new Format11x());
+    addOpcodeInfo(Opcode.RETURN_OBJECT, "return-object", 0x11, new Format11x());
+    addOpcodeInfo(Opcode.CONST_4, "const/4", 0x12, new Format11n());
+    addOpcodeInfo(Opcode.CONST_16, "const/16", 0x13, new Format21s());
+    addOpcodeInfo(Opcode.CONST, "const", 0x14, new Format31i());
+    addOpcodeInfo(Opcode.CONST_HIGH16, "const/high16", 0x15, new Format21h());
+    addOpcodeInfo(Opcode.CONST_WIDE_16, "const-wide/16", 0x16, new Format21s());
+    addOpcodeInfo(Opcode.CONST_WIDE_32, "const-wide/32", 0x17, new Format31i());
+    addOpcodeInfo(Opcode.CONST_WIDE, "const-wide", 0x18, new Format51l());
+    addOpcodeInfo(Opcode.CONST_WIDE_HIGH16, "const-wide/high16", 0x19, new Format21h());
+    addOpcodeInfo(Opcode.CONST_STRING, "const-string", 0x1a, new Format21c());
+    addOpcodeInfo(Opcode.CONST_STRING_JUMBO, "const-string/jumbo", 0x1b, new Format31c());
+    addOpcodeInfo(Opcode.CONST_CLASS, "const-class", 0x1c, new Format21c());
+    addOpcodeInfo(Opcode.MONITOR_ENTER, "monitor-enter", 0x1d, new Format11x());
+    addOpcodeInfo(Opcode.MONITOR_EXIT, "monitor-exit", 0x1e, new Format11x());
+    addOpcodeInfo(Opcode.CHECK_CAST, "check-cast", 0x1f, new Format21c());
+    addOpcodeInfo(Opcode.INSTANCE_OF, "instance-of", 0x20, new Format22c());
+    addOpcodeInfo(Opcode.ARRAY_LENGTH, "array-length", 0x21, new Format12x());
+    addOpcodeInfo(Opcode.NEW_INSTANCE, "new-instance", 0x22, new Format21c());
+    addOpcodeInfo(Opcode.NEW_ARRAY, "new-array", 0x23, new Format22c());
+    addOpcodeInfo(Opcode.FILLED_NEW_ARRAY, "filled-new-array", 0x24, new Format35c());
+    addOpcodeInfo(Opcode.FILLED_NEW_ARRAY_RANGE, "filled-new-array/range",
+        0x25, new Format3rc());
+    addOpcodeInfo(Opcode.FILL_ARRAY_DATA, "fill-array-data", 0x26, new Format31t());
+    addOpcodeInfo(Opcode.THROW, "throw", 0x27, new Format11x());
+    addOpcodeInfo(Opcode.GOTO, "goto", 0x28, new Format10t());
+    addOpcodeInfo(Opcode.GOTO_16, "goto/16", 0x29, new Format20t());
+    addOpcodeInfo(Opcode.GOTO_32, "goto/32", 0x2a, new Format30t());
+    addOpcodeInfo(Opcode.PACKED_SWITCH, "packed-switch", 0x2b, new Format31t());
+    addOpcodeInfo(Opcode.SPARSE_SWITCH, "sparse-switch", 0x2c, new Format31t());
+    addOpcodeInfo(Opcode.CMPL_FLOAT, "cmpl-float", 0x2d, new Format23x());
+    addOpcodeInfo(Opcode.CMPG_FLOAT, "cmpg-float", 0x2e, new Format23x());
+    addOpcodeInfo(Opcode.CMPL_DOUBLE, "cmpl-double", 0x2f, new Format23x());
+    addOpcodeInfo(Opcode.CMPG_DOUBLE, "cmpg-double", 0x30, new Format23x());
+    addOpcodeInfo(Opcode.CMP_LONG, "cmp-long", 0x31, new Format23x());
+    addOpcodeInfo(Opcode.IF_EQ, "if-eq", 0x32, new Format22t());
+    addOpcodeInfo(Opcode.IF_NE, "if-ne", 0x33, new Format22t());
+    addOpcodeInfo(Opcode.IF_LT, "if-lt", 0x34, new Format22t());
+    addOpcodeInfo(Opcode.IF_GE, "if-ge", 0x35, new Format22t());
+    addOpcodeInfo(Opcode.IF_GT, "if-gt", 0x36, new Format22t());
+    addOpcodeInfo(Opcode.IF_LE, "if-le", 0x37, new Format22t());
+    addOpcodeInfo(Opcode.IF_EQZ, "if-eqz", 0x38, new Format21t());
+    addOpcodeInfo(Opcode.IF_NEZ, "if-nez", 0x39, new Format21t());
+    addOpcodeInfo(Opcode.IF_LTZ, "if-ltz", 0x3a, new Format21t());
+    addOpcodeInfo(Opcode.IF_GEZ, "if-gez", 0x3b, new Format21t());
+    addOpcodeInfo(Opcode.IF_GTZ, "if-gtz", 0x3c, new Format21t());
+    addOpcodeInfo(Opcode.IF_LEZ, "if-lez", 0x3d, new Format21t());
+    addOpcodeInfo(Opcode.UNUSED_3E, "unused-3e", 0x3e, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_3F, "unused-3f", 0x3f, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_40, "unused-40", 0x40, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_41, "unused-41", 0x41, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_42, "unused-42", 0x42, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_43, "unused-43", 0x43, new Format10x());
+    addOpcodeInfo(Opcode.AGET, "aget", 0x44, new Format23x());
+    addOpcodeInfo(Opcode.AGET_WIDE, "aget-wide", 0x45, new Format23x());
+    addOpcodeInfo(Opcode.AGET_WIDE, "aget-wide", 0x45, new Format23x());
+    addOpcodeInfo(Opcode.AGET_OBJECT, "aget-object", 0x46, new Format23x());
+    addOpcodeInfo(Opcode.AGET_BOOLEAN, "aget-boolean", 0x47, new Format23x());
+    addOpcodeInfo(Opcode.AGET_BYTE, "aget-byte", 0x48, new Format23x());
+    addOpcodeInfo(Opcode.AGET_CHAR, "aget-char", 0x49, new Format23x());
+    addOpcodeInfo(Opcode.AGET_SHORT, "aget-short", 0x4a, new Format23x());
+    addOpcodeInfo(Opcode.APUT, "aput", 0x4b, new Format23x());
+    addOpcodeInfo(Opcode.APUT_WIDE, "aput-wide", 0x4c, new Format23x());
+    addOpcodeInfo(Opcode.APUT_OBJECT, "aput-object", 0x4d, new Format23x());
+    addOpcodeInfo(Opcode.APUT_BOOLEAN, "aput-boolean", 0x4e, new Format23x());
+    addOpcodeInfo(Opcode.APUT_BYTE, "aput-byte", 0x4f, new Format23x());
+    addOpcodeInfo(Opcode.APUT_CHAR, "aput-char", 0x50, new Format23x());
+    addOpcodeInfo(Opcode.APUT_SHORT, "aput-short", 0x51, new Format23x());
+    addOpcodeInfo(Opcode.IGET, "iget", 0x52, new Format22c());
+    addOpcodeInfo(Opcode.IGET_WIDE, "iget-wide", 0x53, new Format22c());
+    addOpcodeInfo(Opcode.IGET_OBJECT, "iget-object", 0x54, new Format22c());
+    addOpcodeInfo(Opcode.IGET_BOOLEAN, "iget-boolean", 0x55, new Format22c());
+    addOpcodeInfo(Opcode.IGET_BYTE, "iget-byte", 0x56, new Format22c());
+    addOpcodeInfo(Opcode.IGET_CHAR, "iget-char", 0x57, new Format22c());
+    addOpcodeInfo(Opcode.IGET_SHORT, "iget-short", 0x58, new Format22c());
+    addOpcodeInfo(Opcode.IPUT, "iput", 0x59, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_WIDE, "iput-wide", 0x5a, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_OBJECT, "iput-object", 0x5b, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_BOOLEAN, "iput-boolean", 0x5c, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_BYTE, "iput-byte", 0x5d, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_CHAR, "iput-char", 0x5e, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_SHORT, "iput-short", 0x5f, new Format22c());
+    addOpcodeInfo(Opcode.SGET, "sget", 0x60, new Format21c());
+    addOpcodeInfo(Opcode.SGET_WIDE, "sget-wide", 0x61, new Format21c());
+    addOpcodeInfo(Opcode.SGET_OBJECT, "sget-object", 0x62, new Format21c());
+    addOpcodeInfo(Opcode.SGET_BOOLEAN, "sget-boolean", 0x63, new Format21c());
+    addOpcodeInfo(Opcode.SGET_BYTE, "sget-byte", 0x64, new Format21c());
+    addOpcodeInfo(Opcode.SGET_CHAR, "sget-char", 0x65, new Format21c());
+    addOpcodeInfo(Opcode.SGET_SHORT, "sget-short", 0x66, new Format21c());
+    addOpcodeInfo(Opcode.SPUT, "sput", 0x67, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_WIDE, "sput-wide", 0x68, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_OBJECT, "sput-object", 0x69, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_BOOLEAN, "sput-boolean", 0x6a, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_BYTE, "sput-byte", 0x6b, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_CHAR, "sput-char", 0x6c, new Format21c());
+    addOpcodeInfo(Opcode.SPUT_SHORT, "sput-short", 0x6d, new Format21c());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL, "invoke-virtual", 0x6e, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_SUPER, "invoke-super", 0x6f, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_DIRECT, "invoke-direct", 0x70, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_STATIC, "invoke-static", 0x71, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_INTERFACE, "invoke-interface", 0x72, new Format35c());
+    addOpcodeInfo(Opcode.RETURN_VOID_BARRIER, "return-void-barrier", 0x73, new Format10x());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL_RANGE, "invoke-virtual/range", 0x74, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_SUPER_RANGE, "invoke-super/range", 0x75, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_DIRECT_RANGE, "invoke-direct/range", 0x76, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_STATIC_RANGE, "invoke-static/range", 0x77, new Format3rc());
+    addOpcodeInfo(Opcode.INVOKE_INTERFACE_RANGE, "invoke-interface/range",
+        0x78, new Format3rc());
+    addOpcodeInfo(Opcode.UNUSED_79, "unused-79", 0x79, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_7A, "unused-7a", 0x7a, new Format10x());
+    addOpcodeInfo(Opcode.NEG_INT, "neg-int", 0x7b, new Format12x());
+    addOpcodeInfo(Opcode.NOT_INT, "not-int", 0x7c, new Format12x());
+    addOpcodeInfo(Opcode.NEG_LONG, "neg-long", 0x7d, new Format12x());
+    addOpcodeInfo(Opcode.NOT_LONG, "not-long", 0x7e, new Format12x());
+    addOpcodeInfo(Opcode.NEG_FLOAT, "neg-float", 0x7f, new Format12x());
+    addOpcodeInfo(Opcode.NEG_DOUBLE, "neg-double", 0x80, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_LONG, "int-to-long", 0x81, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_FLOAT, "int-to-float", 0x82, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_DOUBLE, "int-to-double", 0x83, new Format12x());
+    addOpcodeInfo(Opcode.LONG_TO_INT, "long-to-int", 0x84, new Format12x());
+    addOpcodeInfo(Opcode.LONG_TO_FLOAT, "long-to-float", 0x85, new Format12x());
+    addOpcodeInfo(Opcode.LONG_TO_DOUBLE, "long-to-double", 0x86, new Format12x());
+    addOpcodeInfo(Opcode.FLOAT_TO_INT, "float-to-int", 0x87, new Format12x());
+    addOpcodeInfo(Opcode.FLOAT_TO_LONG, "float-to-long", 0x88, new Format12x());
+    addOpcodeInfo(Opcode.FLOAT_TO_DOUBLE, "float-to-double", 0x89, new Format12x());
+    addOpcodeInfo(Opcode.DOUBLE_TO_INT, "double-to-int", 0x8a, new Format12x());
+    addOpcodeInfo(Opcode.DOUBLE_TO_LONG, "double-to-long", 0x8b, new Format12x());
+    addOpcodeInfo(Opcode.DOUBLE_TO_FLOAT, "double-to-float", 0x8c, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_BYTE, "int-to-byte", 0x8d, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_CHAR, "int-to-char", 0x8e, new Format12x());
+    addOpcodeInfo(Opcode.INT_TO_SHORT, "int-to-short", 0x8f, new Format12x());
+    addOpcodeInfo(Opcode.ADD_INT, "add-int", 0x90, new Format23x());
+    addOpcodeInfo(Opcode.SUB_INT, "sub-int", 0x91, new Format23x());
+    addOpcodeInfo(Opcode.MUL_INT, "mul-int", 0x92, new Format23x());
+    addOpcodeInfo(Opcode.DIV_INT, "div-int", 0x93, new Format23x());
+    addOpcodeInfo(Opcode.REM_INT, "rem-int", 0x94, new Format23x());
+    addOpcodeInfo(Opcode.AND_INT, "and-int", 0x95, new Format23x());
+    addOpcodeInfo(Opcode.OR_INT, "or-int", 0x96, new Format23x());
+    addOpcodeInfo(Opcode.XOR_INT, "xor-int", 0x97, new Format23x());
+    addOpcodeInfo(Opcode.SHL_INT, "shl-int", 0x98, new Format23x());
+    addOpcodeInfo(Opcode.SHR_INT, "shr-int", 0x99, new Format23x());
+    addOpcodeInfo(Opcode.USHR_INT, "ushr-int", 0x9a, new Format23x());
+    addOpcodeInfo(Opcode.ADD_LONG, "add-long", 0x9b, new Format23x());
+    addOpcodeInfo(Opcode.SUB_LONG, "sub-long", 0x9c, new Format23x());
+    addOpcodeInfo(Opcode.MUL_LONG, "mul-long", 0x9d, new Format23x());
+    addOpcodeInfo(Opcode.DIV_LONG, "div-long", 0x9e, new Format23x());
+    addOpcodeInfo(Opcode.REM_LONG, "rem-long", 0x9f, new Format23x());
+    addOpcodeInfo(Opcode.AND_LONG, "and-long", 0xa0, new Format23x());
+    addOpcodeInfo(Opcode.OR_LONG, "or-long", 0xa1, new Format23x());
+    addOpcodeInfo(Opcode.XOR_LONG, "xor-long", 0xa2, new Format23x());
+    addOpcodeInfo(Opcode.SHL_LONG, "shl-long", 0xa3, new Format23x());
+    addOpcodeInfo(Opcode.SHR_LONG, "shr-long", 0xa4, new Format23x());
+    addOpcodeInfo(Opcode.USHR_LONG, "ushr-long", 0xa5, new Format23x());
+    addOpcodeInfo(Opcode.ADD_FLOAT, "add-float", 0xa6, new Format23x());
+    addOpcodeInfo(Opcode.SUB_FLOAT, "sub-float", 0xa7, new Format23x());
+    addOpcodeInfo(Opcode.MUL_FLOAT, "mul-float", 0xa8, new Format23x());
+    addOpcodeInfo(Opcode.DIV_FLOAT, "div-float", 0xa9, new Format23x());
+    addOpcodeInfo(Opcode.REM_FLOAT, "rem-float", 0xaa, new Format23x());
+    addOpcodeInfo(Opcode.ADD_DOUBLE, "add-double", 0xab, new Format23x());
+    addOpcodeInfo(Opcode.SUB_DOUBLE, "sub-double", 0xac, new Format23x());
+    addOpcodeInfo(Opcode.MUL_DOUBLE, "mul-double", 0xad, new Format23x());
+    addOpcodeInfo(Opcode.DIV_DOUBLE, "div-double", 0xae, new Format23x());
+    addOpcodeInfo(Opcode.REM_DOUBLE, "rem-double", 0xaf, new Format23x());
+    addOpcodeInfo(Opcode.ADD_INT_2ADDR, "add-int/2addr", 0xb0, new Format12x());
+    addOpcodeInfo(Opcode.SUB_INT_2ADDR, "sub-int/2addr", 0xb1, new Format12x());
+    addOpcodeInfo(Opcode.MUL_INT_2ADDR, "mul-int/2addr", 0xb2, new Format12x());
+    addOpcodeInfo(Opcode.DIV_INT_2ADDR, "div-int/2addr", 0xb3, new Format12x());
+    addOpcodeInfo(Opcode.REM_INT_2ADDR, "rem-int/2addr", 0xb4, new Format12x());
+    addOpcodeInfo(Opcode.AND_INT_2ADDR, "and-int/2addr", 0xb5, new Format12x());
+    addOpcodeInfo(Opcode.OR_INT_2ADDR, "or-int/2addr", 0xb6, new Format12x());
+    addOpcodeInfo(Opcode.XOR_INT_2ADDR, "xor-int/2addr", 0xb7, new Format12x());
+    addOpcodeInfo(Opcode.SHL_INT_2ADDR, "shl-int/2addr", 0xb8, new Format12x());
+    addOpcodeInfo(Opcode.SHR_INT_2ADDR, "shr-int/2addr", 0xb9, new Format12x());
+    addOpcodeInfo(Opcode.USHR_INT_2ADDR, "ushr-int/2addr", 0xba, new Format12x());
+    addOpcodeInfo(Opcode.ADD_LONG_2ADDR, "add-long/2addr", 0xbb, new Format12x());
+    addOpcodeInfo(Opcode.SUB_LONG_2ADDR, "sub-long/2addr", 0xbc, new Format12x());
+    addOpcodeInfo(Opcode.MUL_LONG_2ADDR, "mul-long/2addr", 0xbd, new Format12x());
+    addOpcodeInfo(Opcode.DIV_LONG_2ADDR, "div-long/2addr", 0xbe, new Format12x());
+    addOpcodeInfo(Opcode.REM_LONG_2ADDR, "rem-long/2addr", 0xbf, new Format12x());
+    addOpcodeInfo(Opcode.AND_LONG_2ADDR, "and-long/2addr", 0xc0, new Format12x());
+    addOpcodeInfo(Opcode.OR_LONG_2ADDR, "or-long/2addr", 0xc1, new Format12x());
+    addOpcodeInfo(Opcode.XOR_LONG_2ADDR, "xor-long/2addr", 0xc2, new Format12x());
+    addOpcodeInfo(Opcode.SHL_LONG_2ADDR, "shl-long/2addr", 0xc3, new Format12x());
+    addOpcodeInfo(Opcode.SHR_LONG_2ADDR, "shr-long/2addr", 0xc4, new Format12x());
+    addOpcodeInfo(Opcode.USHR_LONG_2ADDR, "ushr-long/2addr", 0xc5, new Format12x());
+    addOpcodeInfo(Opcode.ADD_FLOAT_2ADDR, "add-float/2addr", 0xc6, new Format12x());
+    addOpcodeInfo(Opcode.SUB_FLOAT_2ADDR, "sub-float/2addr", 0xc7, new Format12x());
+    addOpcodeInfo(Opcode.MUL_FLOAT_2ADDR, "mul-float/2addr", 0xc8, new Format12x());
+    addOpcodeInfo(Opcode.DIV_FLOAT_2ADDR, "div-float/2addr", 0xc9, new Format12x());
+    addOpcodeInfo(Opcode.REM_FLOAT_2ADDR, "rem-float/2addr", 0xca, new Format12x());
+    addOpcodeInfo(Opcode.ADD_DOUBLE_2ADDR, "add-double/2addr", 0xcb, new Format12x());
+    addOpcodeInfo(Opcode.SUB_DOUBLE_2ADDR, "sub-double/2addr", 0xcc, new Format12x());
+    addOpcodeInfo(Opcode.MUL_DOUBLE_2ADDR, "mul-double/2addr", 0xcd, new Format12x());
+    addOpcodeInfo(Opcode.DIV_DOUBLE_2ADDR, "div-double/2addr", 0xce, new Format12x());
+    addOpcodeInfo(Opcode.REM_DOUBLE_2ADDR, "rem-double/2addr", 0xcf, new Format12x());
+    addOpcodeInfo(Opcode.ADD_INT_LIT16, "add-int/lit16", 0xd0, new Format22s());
+    addOpcodeInfo(Opcode.RSUB_INT, "rsub-int", 0xd1, new Format22s());
+    addOpcodeInfo(Opcode.MUL_INT_LIT16, "mul-int/lit16", 0xd2, new Format22s());
+    addOpcodeInfo(Opcode.DIV_INT_LIT16, "div-int/lit16", 0xd3, new Format22s());
+    addOpcodeInfo(Opcode.REM_INT_LIT16, "rem-int/lit16", 0xd4, new Format22s());
+    addOpcodeInfo(Opcode.AND_INT_LIT16, "and-int/lit16", 0xd5, new Format22s());
+    addOpcodeInfo(Opcode.OR_INT_LIT16, "or-int/lit16", 0xd6, new Format22s());
+    addOpcodeInfo(Opcode.XOR_INT_LIT16, "xor-int/lit16", 0xd7, new Format22s());
+    addOpcodeInfo(Opcode.ADD_INT_LIT8, "add-int/lit8", 0xd8, new Format22b());
+    addOpcodeInfo(Opcode.RSUB_INT_LIT8, "rsub-int/lit8", 0xd9, new Format22b());
+    addOpcodeInfo(Opcode.MUL_INT_LIT8, "mul-int/lit8", 0xda, new Format22b());
+    addOpcodeInfo(Opcode.DIV_INT_LIT8, "div-int/lit8", 0xdb, new Format22b());
+    addOpcodeInfo(Opcode.REM_INT_LIT8, "rem-int/lit8", 0xdc, new Format22b());
+    addOpcodeInfo(Opcode.AND_INT_LIT8, "and-int/lit8", 0xdd, new Format22b());
+    addOpcodeInfo(Opcode.OR_INT_LIT8, "or-int/lit8", 0xde, new Format22b());
+    addOpcodeInfo(Opcode.XOR_INT_LIT8, "xor-int/lit8", 0xdf, new Format22b());
+    addOpcodeInfo(Opcode.SHL_INT_LIT8, "shl-int/lit8", 0xe0, new Format22b());
+    addOpcodeInfo(Opcode.SHR_INT_LIT8, "shr-int/lit8", 0xe1, new Format22b());
+    addOpcodeInfo(Opcode.USHR_INT_LIT8, "ushr-int/lit8", 0xe2, new Format22b());
+    addOpcodeInfo(Opcode.IGET_QUICK, "+iget-quick", 0xe3, new Format22c());
+    addOpcodeInfo(Opcode.IGET_WIDE_QUICK, "+iget-wide-quick", 0xe4, new Format22c());
+    addOpcodeInfo(Opcode.IGET_OBJECT_QUICK, "+iget-object-quick", 0xe5, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_QUICK, "+iput-quick", 0xe6, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_WIDE_QUICK, "+iput-wide-quick", 0xe7, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_OBJECT_QUICK, "+iput-object-quick", 0xe8, new Format22c());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL_QUICK, "+invoke-virtual-quick", 0xe9, new Format35c());
+    addOpcodeInfo(Opcode.INVOKE_VIRTUAL_QUICK_RANGE, "+invoke-virtual-quick/range",
+        0xea, new Format3rc());
+    addOpcodeInfo(Opcode.IPUT_BOOLEAN_QUICK, "+iput-boolean-quick", 0xeb, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_BYTE_QUICK, "+iput-byte-quick", 0xec, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_CHAR_QUICK, "+iput-char-quick", 0xed, new Format22c());
+    addOpcodeInfo(Opcode.IPUT_SHORT_QUICK, "+iput-short-quick", 0xee, new Format22c());
+    addOpcodeInfo(Opcode.UNUSED_EF, "unused-ef", 0xef, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F0, "unused-f0", 0xf0, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F1, "unused-f1", 0xf1, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F2, "unused-f2", 0xf2, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F3, "unused-f3", 0xf3, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F4, "unused-f4", 0xf4, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F5, "unused-f5", 0xf5, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F6, "unused-f6", 0xf6, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F7, "unused-f7", 0xf7, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F8, "unused-f8", 0xf8, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_F9, "unused-f9", 0xf9, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FA, "unused-fa", 0xfa, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FB, "unused-fb", 0xfb, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FC, "unused-fc", 0xfc, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FD, "unused-fd", 0xfd, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FE, "unused-fe", 0xfe, new Format10x());
+    addOpcodeInfo(Opcode.UNUSED_FF, "unused-ff", 0xff, new Format10x());
+    if (opcode_map_by_int.size() != 256) {
+      Log.errorAndQuit("Incorrect number of bytecodes defined.");
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java
new file mode 100644
index 0000000..4ca2463
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MapItem.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MapItem implements RawDexObject {
+  public static final int TYPE_HEADER_ITEM = 0x0;
+  public static final int TYPE_STRING_ID_ITEM = 0x1;
+  public static final int TYPE_TYPE_ID_ITEM = 0x2;
+  public static final int TYPE_PROTO_ID_ITEM = 0x3;
+  public static final int TYPE_FIELD_ID_ITEM = 0x4;
+  public static final int TYPE_METHOD_ID_ITEM = 0x5;
+  public static final int TYPE_CLASS_DEF_ITEM = 0x6;
+  public static final int TYPE_MAP_LIST = 0x1000;
+  public static final int TYPE_TYPE_LIST = 0x1001;
+  public static final int TYPE_ANNOTATION_SET_REF_LIST = 0x1002;
+  public static final int TYPE_ANNOTATION_SET_ITEM = 0x1003;
+  public static final int TYPE_CLASS_DATA_ITEM = 0x2000;
+  public static final int TYPE_CODE_ITEM = 0x2001;
+  public static final int TYPE_STRING_DATA_ITEM = 0x2002;
+  public static final int TYPE_DEBUG_INFO_ITEM = 0x2003;
+  public static final int TYPE_ANNOTATION_ITEM = 0x2004;
+  public static final int TYPE_ENCODED_ARRAY_ITEM = 0x2005;
+  public static final int TYPE_ANNOTATIONS_DIRECTORY_ITEM = 0x2006;
+
+  public short type;
+  public int size;
+  public Offset offset;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    type = file.readUShort();
+    file.readUShort(); // Unused padding.
+    size = file.readUInt();
+    if (type == TYPE_HEADER_ITEM) {
+      offset = file.getOffsetTracker().getNewHeaderOffset(file.readUInt());
+    } else {
+      offset = file.getOffsetTracker().getNewOffset(file.readUInt());
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUShort(type);
+    file.writeUShort((short) 0); // Unused padding.
+    file.writeUInt(size);
+    file.getOffsetTracker().tryToWriteOffset(offset, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
new file mode 100644
index 0000000..080b5a4
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MapList.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class MapList implements RawDexObject {
+
+  private RawDexFile rawDexFile;
+
+  public int size;
+  public List<MapItem> mapItems;
+
+  public MapList(RawDexFile rawDexFile) {
+    this.rawDexFile = rawDexFile;
+  }
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    // Find the map list.
+    file.seek(rawDexFile.header.mapOff.getOriginalOffset());
+
+    file.getOffsetTracker().getNewOffsettable(file, this);
+
+    // Get the number of entries.
+    size = file.readUInt();
+
+    // Allocate and populate the array.
+    mapItems = new ArrayList<MapItem>(size);
+    for (int i = 0; i < size; i++) {
+      MapItem mapItem = new MapItem();
+      mapItems.add(mapItem);
+      mapItem.read(file);
+    }
+
+    file.getOffsetTracker().rememberPointAfterMapList();
+
+    // NB: We track the current index into the MapList, so when we encounter the DebugInfoItem
+    // MapItem, we know how to find the next MapItem, so we know how large the DebugInfo
+    // area is, so we can copy it as a blob.
+    int mapItemIdx = 0;
+
+    // Iterate through the list, and create all the other data structures.
+    for (MapItem mapItem : mapItems) {
+      file.seek(mapItem.offset.getOriginalOffset());
+      switch (mapItem.type) {
+        case MapItem.TYPE_HEADER_ITEM:
+          // Already read it; skip.
+          break;
+        case MapItem.TYPE_STRING_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            StringIdItem newStringId = new StringIdItem();
+            rawDexFile.stringIds.add(newStringId);
+            newStringId.read(file);
+          }
+          break;
+        case MapItem.TYPE_TYPE_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            TypeIdItem newTypeId = new TypeIdItem();
+            rawDexFile.typeIds.add(newTypeId);
+            newTypeId.read(file);
+          }
+          break;
+        case MapItem.TYPE_PROTO_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            ProtoIdItem newProtoId = new ProtoIdItem();
+            rawDexFile.protoIds.add(newProtoId);
+            newProtoId.read(file);
+          }
+          break;
+        case MapItem.TYPE_FIELD_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            FieldIdItem newFieldId = new FieldIdItem();
+            rawDexFile.fieldIds.add(newFieldId);
+            newFieldId.read(file);
+          }
+          break;
+        case MapItem.TYPE_METHOD_ID_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            MethodIdItem newMethodId = new MethodIdItem();
+            rawDexFile.methodIds.add(newMethodId);
+            newMethodId.read(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DEF_ITEM:
+          for (int i = 0; i < mapItem.size; i++) {
+            ClassDefItem newClassDef = new ClassDefItem();
+            rawDexFile.classDefs.add(newClassDef);
+            newClassDef.read(file);
+          }
+          break;
+        case MapItem.TYPE_MAP_LIST:
+          // Already read it; skip.
+          break;
+        case MapItem.TYPE_TYPE_LIST:
+          rawDexFile.typeLists = new ArrayList<TypeList>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            TypeList newTypeList = new TypeList();
+            rawDexFile.typeLists.add(newTypeList);
+            newTypeList.read(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_REF_LIST:
+          rawDexFile.annotationSetRefLists =
+            new ArrayList<AnnotationSetRefList>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationSetRefList newAnnotationSetRefList = new AnnotationSetRefList();
+            rawDexFile.annotationSetRefLists.add(newAnnotationSetRefList);
+            newAnnotationSetRefList.read(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_ITEM:
+          rawDexFile.annotationSetItems = new ArrayList<AnnotationSetItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationSetItem newAnnotationSetItem = new AnnotationSetItem();
+            rawDexFile.annotationSetItems.add(newAnnotationSetItem);
+            newAnnotationSetItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DATA_ITEM:
+          rawDexFile.classDatas = new ArrayList<ClassDataItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            ClassDataItem newClassData = new ClassDataItem();
+            rawDexFile.classDatas.add(newClassData);
+            newClassData.read(file);
+          }
+          break;
+        case MapItem.TYPE_CODE_ITEM:
+          rawDexFile.codeItems = new ArrayList<CodeItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            CodeItem newCodeItem = new CodeItem();
+            rawDexFile.codeItems.add(newCodeItem);
+            newCodeItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_STRING_DATA_ITEM:
+          rawDexFile.stringDatas = new ArrayList<StringDataItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            StringDataItem newStringData = new StringDataItem();
+            rawDexFile.stringDatas.add(newStringData);
+            newStringData.read(file);
+          }
+          break;
+        case MapItem.TYPE_DEBUG_INFO_ITEM:
+        {
+          // We aren't interested in updating the debug data, so just read it as a blob.
+          int start = mapItem.offset.getOriginalOffset();
+          int end = mapItems.get(mapItemIdx + 1).offset.getOriginalOffset();
+          int size = end - start;
+          rawDexFile.debugInfoItem = new DebugInfoItem(size);
+          rawDexFile.debugInfoItem.read(file);
+          break;
+        }
+        case MapItem.TYPE_ANNOTATION_ITEM:
+          rawDexFile.annotationItems = new ArrayList<AnnotationItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationItem newAnnotationItem = new AnnotationItem();
+            rawDexFile.annotationItems.add(newAnnotationItem);
+            newAnnotationItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_ENCODED_ARRAY_ITEM:
+          rawDexFile.encodedArrayItems = new ArrayList<EncodedArrayItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            EncodedArrayItem newEncodedArrayItem = new EncodedArrayItem();
+            rawDexFile.encodedArrayItems.add(newEncodedArrayItem);
+            newEncodedArrayItem.read(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+          rawDexFile.annotationsDirectoryItems =
+          new ArrayList<AnnotationsDirectoryItem>(mapItem.size);
+          for (int i = 0; i < mapItem.size; i++) {
+            AnnotationsDirectoryItem newAnnotationsDirectoryItem = new AnnotationsDirectoryItem();
+            rawDexFile.annotationsDirectoryItems.add(newAnnotationsDirectoryItem);
+            newAnnotationsDirectoryItem.read(file);
+          }
+          break;
+        default:
+          Log.errorAndQuit("Encountered unknown map item when reading map item list.");
+      }
+      mapItemIdx++;
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(mapItems.size());
+    for (MapItem mapItem : mapItems) {
+      mapItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java
new file mode 100644
index 0000000..1a24fb6
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MethodAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MethodAnnotation implements RawDexObject {
+  public int methodIdx;
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    methodIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(methodIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.METHOD_ID && methodIdx >= insertedIdx) {
+      methodIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java
new file mode 100644
index 0000000..12d0187
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/MethodIdItem.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class MethodIdItem implements RawDexObject {
+  public short classIdx;
+  public short protoIdx;
+  public int nameIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    classIdx = file.readUShort();
+    protoIdx = file.readUShort();
+    nameIdx = file.readUInt();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUShort(classIdx);
+    file.writeUShort(protoIdx);
+    file.writeUInt(nameIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && classIdx >= insertedIdx) {
+      classIdx++;
+    }
+    if (kind == IndexUpdateKind.PROTO_ID && protoIdx >= insertedIdx) {
+      protoIdx++;
+    }
+    if (kind == IndexUpdateKind.STRING_ID && nameIdx >= insertedIdx) {
+      nameIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java b/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java
new file mode 100644
index 0000000..b6718e3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Offset.java
@@ -0,0 +1,191 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+public class Offset {
+  /**
+   * The absolute value of this offset as it was originally read.
+   */
+  private int originalOffset;
+
+  /**
+   * The Offsettable that this Offset points to.
+   */
+  private Offsettable offsettable;
+
+  /**
+   * The location of this Offset in the new file, ONLY SET IF the Offset
+   * couldn't be written because what it points to hasn't been written
+   * yet.
+   */
+  private int outputLocation;
+
+  /**
+   * Was the output location for this Offset set?.
+   */
+  private boolean outputLocationSet;
+
+  /**
+   * Does this Offset need to be written out using ULEB128?.
+   */
+  private boolean useUleb128;
+
+  /**
+   * Was this Offset created after reading, during mutation?.
+   */
+  private boolean isNewOffset;
+
+  /**
+   * Only one Offset should have this flag set, the MapItem that points
+   * to the HeaderItem.
+   */
+  private boolean pointsAtHeader;
+
+  /**
+   * If an Offset pointed at 0 (because it is not actually a valid Offset),
+   * and it's not pointing at the header, then this is set.
+   */
+  private boolean pointsAtNull;
+
+  public Offset(boolean header) {
+    pointsAtHeader = header;
+  }
+
+  public RawDexObject getPointedToItem() {
+    return offsettable.getItem();
+  }
+
+  public boolean pointsToSomething() {
+    return offsettable != null;
+  }
+
+  public boolean pointsAtNull() {
+    return pointsAtNull;
+  }
+
+  public boolean pointsAtHeader() {
+    return pointsAtHeader;
+  }
+
+  /**
+   * Returns true if this Offset points at the provided RawDexObject.
+   */
+  public boolean pointsToThisItem(RawDexObject thisItem) {
+    if (!pointsToSomething()) {
+      return false;
+    }
+    return (offsettable.getItem().equals(thisItem));
+  }
+
+  /**
+   * Returns true if this Offset points at the provided Offsettable.
+   */
+  public boolean pointsToThisOffsettable(Offsettable thisOffsettable) {
+    if (!pointsToSomething()) {
+      return false;
+    }
+    return (offsettable.equals(thisOffsettable));
+  }
+
+  /**
+   * Makes this Offset point at a new Offsettable.
+   */
+  public void pointTo(Offsettable offsettableItem) {
+    if (offsettable != null) {
+      Log.debug("Updating what an Offset points to...");
+    }
+    offsettable = offsettableItem;
+  }
+
+  /**
+   * Call this to make an Offset that pointed at null before now point at something.
+   * An Offset may have previously pointed at null before...
+   * Example: if there are no fields referred to in a DEX file, then header.field_ids_off
+   * will point at null. We distinguish when Offsets point at null, and are not pointing
+   * at the header (only the header MapItem should do this) with a flag. Therefore, this
+   * method is needed to indicate that this Offset now points at something.
+   */
+  public void unsetNullAndPointTo(Offsettable offsettableItem) {
+    pointsAtNull = false;
+    if (offsettable != null) {
+      Log.debug("Updating what an Offset points to...");
+    }
+    offsettable = offsettableItem;
+  }
+
+  public void pointToNew(Offsettable offsettableItem) {
+    offsettable = offsettableItem;
+    isNewOffset = true;
+  }
+
+  public int getNewPositionOfItem() {
+    return offsettable.getNewPosition();
+  }
+
+  public boolean usesUleb128() {
+    return useUleb128;
+  }
+
+  /**
+   * Mark this Offset as using the ULEB128 encoding.
+   */
+  public void setUsesUleb128() {
+    if (useUleb128) {
+      throw new Error("Offset is already marked as using ULEB128!");
+    }
+    useUleb128 = true;
+  }
+
+  public boolean isNewOffset() {
+    return isNewOffset;
+  }
+
+  public void setPointsAtNull() {
+    pointsAtNull = true;
+  }
+
+  public void setOutputLocation(int loc) {
+    outputLocation = loc;
+    outputLocationSet = true;
+  }
+
+  /**
+   * Get the location in the output DEX file where this offset has been written.
+   * (This is used when patching Offsets when the Offsettable position was not
+   * known at the time of writing out the Offset.)
+   */
+  public int getOutputLocation() {
+    if (!outputLocationSet) {
+      throw new Error("Output location was not set yet!");
+    }
+    return outputLocation;
+  }
+
+  public void setOriginalOffset(int offset) {
+    originalOffset = offset;
+  }
+
+  public int getOriginalOffset() {
+    return originalOffset;
+  }
+
+  public boolean readyForWriting() {
+    return offsettable.readyForFinalOffsetToBeWritten();
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java b/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java
new file mode 100644
index 0000000..34d609a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/OffsetTracker.java
@@ -0,0 +1,513 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * This class allows the recording of both Offsettable items (that is, items that can be
+ * referred to by an offset somewhere else in the file - RawDexObjects) and Offsets.
+ * The idea in a nutshell is that for every Offsettable item we read, we remember
+ * its original position in the file using a map, and the order in which the Offsettables were
+ * written out. We also remember every Offset we read in, and its value. Then, after reading
+ * the whole file, we use the map to find the Offsettable it pointed at.
+ * Then, as we write out the file, for every Offsettable we write out, we record its new position,
+ * using the order we collected earlier. For every Offset we write out, we look at its Offsettable
+ * to see where it was written. If it hasn't been written yet, then we write out a blank value
+ * for the time being, remember where that blank value was written, and put the Offset into a
+ * table for patching once everything has been written out.
+ * There are some variables (index_after_map_list, restore_point) used for remembering certain
+ * points to jump forward and back to, because we cannot read and write the file out in exactly
+ * the same order.
+ * TODO: Perhaps it makes more sense to just reorder the offsettable_table once it's been read,
+ * in preparation for the order in which the file is written out?
+ * Finally, we provide methods for adding new Offsettable items into the right place in the order
+ * table.
+ */
+public class OffsetTracker {
+  /**
+   * A map from the original offset in the input DEX file to
+   * the Offsettable it points to. (That Offsettable will contain
+   * the actual item, and later on the new offset for the item when
+   * the item is written out.
+   */
+  private Map<Integer, Offsettable> offsettableMap;
+
+  /**
+   * A table of all Offsettables. We need to ensure we write out
+   * all items in the same order we read them in, to make sure we update
+   * the Offsettable.new_position field with the correct value wrt to
+   * the original_position field.
+   */
+  private List<Offsettable> offsettableTable;
+
+  /**
+   * A table of all offsets that is populated as we read in the DEX file.
+   * As the end, we find the correct Offsettable for the Offset in the above
+   * map, and associate them.
+   */
+  private List<Offset> needsAssociationTable;
+
+  /**
+   * A table of all offsets that we could not write out an updated offset for
+   * as we write out a DEX file. Will be checked after writing is complete,
+   * to allow specific patching of each offset's location as at that point
+   * all Offsettables will have been updated with their new position.
+   */
+  private List<Offset> needsUpdateTable;
+
+  /**
+   * Tracks how far we are through the offsettable_table as we write out the file.
+   */
+  private int offsettableTableIdx;
+
+  /**
+   * Because we write in a slightly different order to how we read
+   * (why? to read, we read the header, then the map list, and then use the map
+   *  list to read everything else.
+   *  when we write, we write the header, and then we cannot write the map list
+   *  because we don't know where it will go yet, so we write everything else first)
+   * so: we remember the position in the offsettable_table after we read the map list,
+   * so we can skip there after we write out the header.
+   */
+  private int indexAfterMapList;
+
+  /**
+   * Related to index_after_map_list, this is the index we save when we're jumping back to
+   * write the map list.
+   */
+  private int restorePoint;
+
+  /**
+   * Create a new OffsetTracker. Should persist between parsing a DEX file, and outputting
+   * the mutated DEX file.
+   */
+  public OffsetTracker() {
+    offsettableMap = new HashMap<Integer,Offsettable>();
+    offsettableTable = new ArrayList<Offsettable>();
+    needsAssociationTable = new ArrayList<Offset>();
+    needsUpdateTable = new ArrayList<Offset>();
+  }
+
+  /**
+   * Lookup an Item by the offset it had in the input DEX file.
+   * @param offset The offset in the input DEX file.
+   * @return The corresponding Item.
+   */
+  public RawDexObject getItemByOffset(int offset) {
+    return offsettableMap.get(offset).getItem();
+  }
+
+  /**
+   * As Items are read in, they call this function once they have word-aligned the file pointer,
+   * to record their position and themselves into an Offsettable object, that will be tracked.
+   * @param file Used for recording position into the new Offsettable.
+   * @param item Used for recording the relevant Item into the new Offsettable.
+   */
+  public void getNewOffsettable(DexRandomAccessFile file, RawDexObject item) throws IOException {
+    Offsettable offsettable = new Offsettable(item, false);
+    offsettable.setOriginalPosition((int) file.getFilePointer());
+    offsettableMap.put(offsettable.getOriginalPosition(), offsettable);
+    offsettableTable.add(offsettable);
+  }
+
+  /**
+   * As Items read in Offsets, they call this function with the offset they originally
+   * read from the file, to allow later association with an Offsettable.
+   * @param originalOffset The original offset read from the input DEX file.
+   * @return An Offset that will later be associated with an Offsettable.
+   */
+  public Offset getNewOffset(int originalOffset) throws IOException {
+    Offset offset = new Offset(false);
+    offset.setOriginalOffset(originalOffset);
+    needsAssociationTable.add(offset);
+    return offset;
+  }
+
+  /**
+   * Only MapItem should call this method, when the MapItem that points to the header
+   * is read.
+   */
+  public Offset getNewHeaderOffset(int originalOffset) throws IOException {
+    Offset offset = new Offset(true);
+    offset.setOriginalOffset(originalOffset);
+    needsAssociationTable.add(offset);
+    return offset;
+  }
+
+  /**
+   * Call this after reading, to associate Offsets with Offsettables.
+   */
+  public void associateOffsets() {
+    for (Offset offset : needsAssociationTable) {
+      if (offset.getOriginalOffset() == 0 && !(offset.pointsAtHeader())) {
+        offset.setPointsAtNull();
+      } else {
+        offset.pointTo(offsettableMap.get(offset.getOriginalOffset()));
+        if (!offset.pointsToSomething()) {
+          Log.error(String.format("Couldn't find original offset 0x%x!",
+              offset.getOriginalOffset()));
+        }
+      }
+    }
+    needsAssociationTable.clear();
+  }
+
+  /**
+   * As Items are written out into the output DEX file, this function is called
+   * to update the next Offsettable with the file pointer's current position.
+   * This should allow the tracking of new offset locations.
+   * This also requires that reading and writing of all items happens in the same order
+   * (with the exception of the map list, see above)
+   * @param file Used for recording the new position.
+   */
+  public void updatePositionOfNextOffsettable(DexRandomAccessFile file) throws IOException {
+    if (offsettableTableIdx == offsettableTable.size()) {
+      Log.errorAndQuit("Not all created Offsettable items have been added to the "
+          + "Offsettable Table!");
+    }
+    Offsettable offsettable = offsettableTable.get(offsettableTableIdx);
+    offsettable.setNewPosition((int) file.getFilePointer());
+    offsettableTableIdx++;
+  }
+
+  /**
+   * As Items are written out, any writing out of an offset must call this function, passing
+   * in the relevant offset. This function will write out the offset, if the associated
+   * Offsettable has been updated with its new position, or else will write out a null value, and
+   * the Offset will be stored for writing after all Items have been written, and all
+   * Offsettables MUST have been updated.
+   * @param offset The offset received from getNewOffset().
+   * @param file Used for writing out to the file.
+   * @param useUleb128 Whether or not the offset should be written in UINT or ULEB128 form.
+   */
+  public void tryToWriteOffset(Offset offset, DexRandomAccessFile file, boolean useUleb128)
+      throws IOException {
+    if (!offset.isNewOffset() && (!offset.pointsToSomething())) {
+      if (useUleb128) {
+        file.writeUleb128(0);
+      } else {
+        file.writeUInt(0);
+      }
+      return;
+    }
+
+    if (offset.readyForWriting()) {
+      if (useUleb128) {
+        file.writeUleb128(offset.getNewPositionOfItem());
+      } else {
+        file.writeUInt(offset.getNewPositionOfItem());
+      }
+    } else {
+      offset.setOutputLocation((int) file.getFilePointer());
+      if (useUleb128) {
+        file.writeLargestUleb128(offset.getOriginalOffset());
+        offset.setUsesUleb128();
+      } else {
+        file.writeUInt(offset.getOriginalOffset());
+      }
+      needsUpdateTable.add(offset);
+    }
+  }
+
+  /**
+   * This is called after all writing has finished, to write out any Offsets
+   * that could not be written out during the original writing phase, because their
+   * associated Offsettables hadn't had their new positions calculated yet.
+   * @param file Used for writing out to the file.
+   */
+  public void updateOffsets(DexRandomAccessFile file) throws IOException {
+    if (offsettableTableIdx != offsettableTable.size()) {
+      Log.errorAndQuit("Being asked to update dangling offsets but the "
+          + "correct number of offsettables has not been written out!");
+    }
+    for (Offset offset : needsUpdateTable) {
+      file.seek(offset.getOutputLocation());
+      if (offset.usesUleb128()) {
+        file.writeLargestUleb128(offset.getNewPositionOfItem());
+      } else {
+        file.writeUInt(offset.getNewPositionOfItem());
+      }
+    }
+    needsUpdateTable.clear();
+  }
+
+  /**
+   * Called after writing out the header, to skip to after the map list.
+   */
+  public void skipToAfterMapList() {
+    offsettableTableIdx = indexAfterMapList;
+  }
+
+  /**
+   * Called once the map list needs to be written out, to set the
+   * offsettable table index back to the right place.
+   */
+  public void goBackToMapList() {
+    restorePoint = offsettableTableIdx;
+    offsettableTableIdx = (indexAfterMapList - 1);
+  }
+
+  /**
+   * Called once the map list has been written out, to set the
+   * offsettable table index back to where it was before.
+   */
+  public void goBackToPreviousPoint() {
+    if (offsettableTableIdx != indexAfterMapList) {
+      Log.errorAndQuit("Being asked to go to the point before the MapList was written out,"
+          + " but we're not in the right place.");
+    }
+    offsettableTableIdx = restorePoint;
+  }
+
+  /**
+   * Called after reading in the map list, to remember the point to be skipped
+   * to later.
+   */
+  public void rememberPointAfterMapList() {
+    indexAfterMapList = offsettableTable.size();
+  }
+
+  private void updateHeaderOffsetIfValid(Offset offset, Offsettable previousFirst,
+      Offsettable newFirst, String offsetName) {
+    if (offset.pointsToThisOffsettable(previousFirst)) {
+      offset.pointToNew(newFirst);
+    } else {
+      Log.errorAndQuit("Header " + offsetName + " offset not pointing at first element?");
+    }
+  }
+
+  private void addTypeListsToMapFile(RawDexFile rawDexFile, Offsettable typeListOffsettable) {
+    // Create a MapItem for the TypeLists
+    MapItem typeListMapItem = new MapItem();
+    typeListMapItem.offset = new Offset(false);
+    typeListMapItem.offset.pointToNew(typeListOffsettable);
+    typeListMapItem.type = MapItem.TYPE_TYPE_LIST;
+    typeListMapItem.size = 1;
+
+    // Insert into the MapList.
+    // (first, find the MapItem that points to StringDataItems...)
+    int idx = 0;
+    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+      if (mapItem.type == MapItem.TYPE_STRING_DATA_ITEM) {
+        break;
+      }
+      idx++;
+    }
+    // (now insert the TypeList MapItem just before the StringDataItem one...)
+    rawDexFile.mapList.mapItems.add(idx, typeListMapItem);
+  }
+
+  private void addFieldIdsToHeaderAndMapFile(RawDexFile rawDexFile,
+      Offsettable fieldOffsettable) {
+    // Add the field IDs to the header.
+    rawDexFile.header.fieldIdsOff.unsetNullAndPointTo(fieldOffsettable);
+    rawDexFile.header.fieldIdsSize = 1;
+
+    // Create a MapItem for the field IDs.
+    MapItem fieldMapItem = new MapItem();
+    fieldMapItem.offset = new Offset(false);
+    fieldMapItem.offset.pointToNew(fieldOffsettable);
+    fieldMapItem.type = MapItem.TYPE_FIELD_ID_ITEM;
+    fieldMapItem.size = 1;
+
+    // Insert into the MapList.
+    // (first, find the MapItem that points to MethodIdItems...)
+    int idx = 0;
+    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+      if (mapItem.type == MapItem.TYPE_METHOD_ID_ITEM) {
+        break;
+      }
+      idx++;
+    }
+    // (now insert the FieldIdItem MapItem just before the MethodIdItem one...)
+    rawDexFile.mapList.mapItems.add(idx, fieldMapItem);
+  }
+
+
+  private void updateOffsetsInHeaderAndMapFile(RawDexFile rawDexFile,
+      Offsettable newFirstOffsettable) {
+    Offsettable prevFirstOffsettable = null;
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i) == newFirstOffsettable) {
+        prevFirstOffsettable = offsettableTable.get(i + 1);
+        break;
+      }
+    }
+    if (prevFirstOffsettable == null) {
+      Log.errorAndQuit("When calling updateMapListOffsets, could not find new "
+          + "first offsettable?");
+    }
+
+    // Based on the type of the item we just added, check the relevant Offset in the header
+    // and if it pointed at the prev_first_offsettable, make it point at the new one.
+    // NB: if it isn't pointing at the prev one, something is wrong.
+    HeaderItem header = rawDexFile.header;
+    if (newFirstOffsettable.getItem() instanceof StringIdItem) {
+      updateHeaderOffsetIfValid(header.stringIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "StringID");
+    } else if (newFirstOffsettable.getItem() instanceof TypeIdItem) {
+      updateHeaderOffsetIfValid(header.typeIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "TypeID");
+    } else if (newFirstOffsettable.getItem() instanceof ProtoIdItem) {
+      updateHeaderOffsetIfValid(header.protoIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "ProtoID");
+    } else if (newFirstOffsettable.getItem() instanceof FieldIdItem) {
+      updateHeaderOffsetIfValid(header.fieldIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "FieldID");
+    } else if (newFirstOffsettable.getItem() instanceof MethodIdItem) {
+      updateHeaderOffsetIfValid(header.methodIdsOff, prevFirstOffsettable,
+          newFirstOffsettable, "MethodID");
+    } else if (newFirstOffsettable.getItem() instanceof ClassDefItem) {
+      updateHeaderOffsetIfValid(header.classDefsOff, prevFirstOffsettable,
+          newFirstOffsettable, "ClassDef");
+    }
+
+    // Now iterate through the MapList's MapItems, and see if their Offsets pointed at the
+    // prev_first_offsettable, and if so, make them now point at the new_first_offsettable.
+    for (MapItem mapItem : rawDexFile.mapList.mapItems) {
+      if (mapItem.offset.pointsToThisOffsettable(prevFirstOffsettable)) {
+        Log.info("Updating offset in MapItem (type: " + mapItem.type + ") after "
+            + "we called insertNewOffsettableAsFirstOfType()");
+        mapItem.offset.pointToNew(newFirstOffsettable);
+      }
+    }
+  }
+
+  private void insertOffsettableAt(int idx, Offsettable offsettable) {
+    offsettableTable.add(idx, offsettable);
+    if (indexAfterMapList > idx) {
+      indexAfterMapList++;
+    }
+    if (restorePoint > idx) {
+      restorePoint++;
+    }
+  }
+
+  /**
+   * If we're creating our first TypeList, then IdCreator has to call this method to
+   * ensure it gets put into the correct place in the offsettable table.
+   * This assumes TypeLists always come before StringDatas.
+   */
+  public Offsettable insertNewOffsettableAsFirstEverTypeList(RawDexObject item,
+      RawDexFile rawDexFile) {
+    // We find the first StringDataItem, the type lists will come before this.
+    Log.info("Calling insertNewOffsettableAsFirstEverTypeList()");
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() instanceof StringDataItem) {
+        Offsettable offsettable = new Offsettable(item, true);
+        insertOffsettableAt(i, offsettable);
+        addTypeListsToMapFile(rawDexFile, offsettable);
+        return offsettable;
+      }
+    }
+    Log.errorAndQuit("Could not find any StringDataItems to insert the type list before.");
+    return null;
+  }
+
+  /**
+   * If we're creating our first FieldId, then IdCreator has to call this method to
+   * ensure it gets put into the correct place in the offsettable table.
+   * This assumes FieldIds always come before MethodIds.
+   */
+  public Offsettable insertNewOffsettableAsFirstEverField(RawDexObject item,
+      RawDexFile rawDexFile) {
+    // We find the first MethodIdItem, the fields will come before this.
+    Log.info("Calling insertNewOffsettableAsFirstEverField()");
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() instanceof MethodIdItem) {
+        Offsettable offsettable = new Offsettable(item, true);
+        insertOffsettableAt(i, offsettable);
+        addFieldIdsToHeaderAndMapFile(rawDexFile, offsettable);
+        return offsettable;
+      }
+    }
+    Log.errorAndQuit("Could not find any MethodIdItems to insert the field before.");
+    return null;
+  }
+
+  /**
+   * If we're creating a new Item (such as FieldId, MethodId) that is placed into the
+   * first position of the relevant ID table, then IdCreator has to call this method to
+   * ensure it gets put into the correct place in the offsettable table.
+   */
+  public Offsettable insertNewOffsettableAsFirstOfType(RawDexObject item,
+      RawDexFile rawDexFile) {
+    Log.debug("Calling insertNewOffsettableAsFirstOfType()");
+    int index = getOffsettableIndexForFirstItemType(item);
+    if (index == -1) {
+      Log.errorAndQuit("Could not find any object of class: " + item.getClass());
+    }
+    Offsettable offsettable = new Offsettable(item, true);
+    insertOffsettableAt(index, offsettable);
+    updateOffsetsInHeaderAndMapFile(rawDexFile, offsettable);
+    return offsettable;
+  }
+
+  /**
+   * IdCreator has to call this method when it creates a new IdItem, to make sure it
+   * gets put into the correct place in the offsettable table. IdCreator should
+   * provide the IdItem that should come before this new IdItem.
+   */
+  public Offsettable insertNewOffsettableAfter(RawDexObject item, RawDexObject itemBefore) {
+    Log.debug("Calling insertNewOffsettableAfter()");
+    int index = getOffsettableIndexForItem(itemBefore);
+    if (index == -1) {
+      Log.errorAndQuit("Did not find specified 'after' object in offsettable table.");
+    }
+    Offsettable offsettable = new Offsettable(item, true);
+    insertOffsettableAt(index + 1, offsettable);
+    return offsettable;
+  }
+
+  private int getOffsettableIndexForFirstItemType(RawDexObject item) {
+    Class<?> itemClass = item.getClass();
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem().getClass().equals(itemClass)) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  private int getOffsettableIndexForItem(RawDexObject item) {
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() == item) {
+        return i;
+      }
+    }
+    return -1;
+  }
+
+  /**
+   * Given a RawDexObject, get the Offsettable that contains it.
+   */
+  public Offsettable getOffsettableForItem(RawDexObject item) {
+    for (int i = 0; i < offsettableTable.size(); i++) {
+      if (offsettableTable.get(i).getItem() == item) {
+        return offsettableTable.get(i);
+      }
+    }
+    return null;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java b/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java
new file mode 100644
index 0000000..1b8cb24
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Offsettable.java
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+/**
+ * Tracks the original and updated positions of a RawDexObject when it is
+ * parsed in from a DEX file, and written out to a mutated DEX file.
+ */
+public class Offsettable {
+  /**
+   * The position of this Offsettable's item when it was read in.
+   */
+  private int originalPosition;
+
+  /**
+   * Set as we write out any Offsettable, so the Offset knows what its
+   * new value should be.
+   */
+  private int newPosition;
+
+  /**
+   * The actual Item this Offsettable contains.
+   */
+  private RawDexObject item;
+
+  /**
+   *  Set either when getOriginalPosition() is called by the OffsetTracker
+   *  to put the location in the offsettable map, so when Offsets are being
+   *  associated, they know which Offsettable to point at.
+   *  Or when an Offsettable is created that is marked as new, so we don't
+   *  need to know its original position, because an Offset will be directly
+   *  associated with it.
+   */
+  private boolean originalPositionKnown;
+
+  /**
+   * Set when we calculate the new position of this Offsettable as the file is
+   * being output.
+   */
+  private boolean updated;
+
+  /**
+   * Only the OffsetTracker should be able to create a new Offsettable.
+   */
+  public Offsettable(RawDexObject item, boolean isNew) {
+    this.item = item;
+    if (isNew) {
+      // We no longer care about the original position of the Offsettable, because
+      // we are at the stage where we manually point Offsets at Offsettables, and
+      // don't need to use the OffsetTracker's offsettable map.
+      // So just lie and say we know it now.
+      originalPositionKnown = true;
+    }
+  }
+
+  public RawDexObject getItem() {
+    return item;
+  }
+
+  /**
+   * Gets the offset from the beginning of the file to the RawDexObject this Offsettable
+   * contains, when the file was originally read.
+   * Called when we're associating Offsets with Offsettables using the OffsetTracker's
+   * offsettable map.
+   */
+  public int getOriginalPosition() {
+    if (!originalPositionKnown) {
+      throw new Error("Cannot get the original position of an Offsettable when not yet set.");
+    }
+    return originalPosition;
+  }
+
+  public void setOriginalPosition(int pos) {
+    originalPosition = pos;
+    originalPositionKnown = true;
+  }
+
+  /**
+   * Get the new position of this Offsettable, once it's been written out to the output file.
+   */
+  public int getNewPosition() {
+    if (!updated) {
+      throw new Error("Cannot request new position before it has been set!");
+    }
+    return newPosition;
+  }
+
+  /**
+   * Record the new position of this Offsettable, as it is written out to the output file.
+   */
+  public void setNewPosition(int pos) {
+    if (!updated) {
+      newPosition = pos;
+      updated = true;
+    } else {
+      throw new Error("Cannot update an Offsettable twice!");
+    }
+  }
+
+  public boolean readyForFinalOffsetToBeWritten() {
+    return (originalPositionKnown && updated);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java b/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java
new file mode 100644
index 0000000..312e855
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/Opcode.java
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+public enum Opcode {
+  NOP,
+  MOVE,
+  MOVE_FROM16,
+  MOVE_16,
+  MOVE_WIDE,
+  MOVE_WIDE_FROM16,
+  MOVE_WIDE_16,
+  MOVE_OBJECT,
+  MOVE_OBJECT_FROM16,
+  MOVE_OBJECT_16,
+  MOVE_RESULT,
+  MOVE_RESULT_WIDE,
+  MOVE_RESULT_OBJECT,
+  MOVE_EXCEPTION,
+  RETURN_VOID,
+  RETURN,
+  RETURN_WIDE,
+  RETURN_OBJECT,
+  CONST_4,
+  CONST_16,
+  CONST,
+  CONST_HIGH16,
+  CONST_WIDE_16,
+  CONST_WIDE_32,
+  CONST_WIDE,
+  CONST_WIDE_HIGH16,
+  CONST_STRING,
+  CONST_STRING_JUMBO,
+  CONST_CLASS,
+  MONITOR_ENTER,
+  MONITOR_EXIT,
+  CHECK_CAST,
+  INSTANCE_OF,
+  ARRAY_LENGTH,
+  NEW_INSTANCE,
+  NEW_ARRAY,
+  FILLED_NEW_ARRAY,
+  FILLED_NEW_ARRAY_RANGE,
+  FILL_ARRAY_DATA,
+  THROW,
+  GOTO,
+  GOTO_16,
+  GOTO_32,
+  PACKED_SWITCH,
+  SPARSE_SWITCH,
+  CMPL_FLOAT,
+  CMPG_FLOAT,
+  CMPL_DOUBLE,
+  CMPG_DOUBLE,
+  CMP_LONG,
+  IF_EQ,
+  IF_NE,
+  IF_LT,
+  IF_GE,
+  IF_GT,
+  IF_LE,
+  IF_EQZ,
+  IF_NEZ,
+  IF_LTZ,
+  IF_GEZ,
+  IF_GTZ,
+  IF_LEZ,
+  UNUSED_3E,
+  UNUSED_3F,
+  UNUSED_40,
+  UNUSED_41,
+  UNUSED_42,
+  UNUSED_43,
+  AGET,
+  AGET_WIDE,
+  AGET_OBJECT,
+  AGET_BOOLEAN,
+  AGET_BYTE,
+  AGET_CHAR,
+  AGET_SHORT,
+  APUT,
+  APUT_WIDE,
+  APUT_OBJECT,
+  APUT_BOOLEAN,
+  APUT_BYTE,
+  APUT_CHAR,
+  APUT_SHORT,
+  IGET,
+  IGET_WIDE,
+  IGET_OBJECT,
+  IGET_BOOLEAN,
+  IGET_BYTE,
+  IGET_CHAR,
+  IGET_SHORT,
+  IPUT,
+  IPUT_WIDE,
+  IPUT_OBJECT,
+  IPUT_BOOLEAN,
+  IPUT_BYTE,
+  IPUT_CHAR,
+  IPUT_SHORT,
+  SGET,
+  SGET_WIDE,
+  SGET_OBJECT,
+  SGET_BOOLEAN,
+  SGET_BYTE,
+  SGET_CHAR,
+  SGET_SHORT,
+  SPUT,
+  SPUT_WIDE,
+  SPUT_OBJECT,
+  SPUT_BOOLEAN,
+  SPUT_BYTE,
+  SPUT_CHAR,
+  SPUT_SHORT,
+  INVOKE_VIRTUAL,
+  INVOKE_SUPER,
+  INVOKE_DIRECT,
+  INVOKE_STATIC,
+  INVOKE_INTERFACE,
+  RETURN_VOID_BARRIER,
+  INVOKE_VIRTUAL_RANGE,
+  INVOKE_SUPER_RANGE,
+  INVOKE_DIRECT_RANGE,
+  INVOKE_STATIC_RANGE,
+  INVOKE_INTERFACE_RANGE,
+  UNUSED_79,
+  UNUSED_7A,
+  NEG_INT,
+  NOT_INT,
+  NEG_LONG,
+  NOT_LONG,
+  NEG_FLOAT,
+  NEG_DOUBLE,
+  INT_TO_LONG,
+  INT_TO_FLOAT,
+  INT_TO_DOUBLE,
+  LONG_TO_INT,
+  LONG_TO_FLOAT,
+  LONG_TO_DOUBLE,
+  FLOAT_TO_INT,
+  FLOAT_TO_LONG,
+  FLOAT_TO_DOUBLE,
+  DOUBLE_TO_INT,
+  DOUBLE_TO_LONG,
+  DOUBLE_TO_FLOAT,
+  INT_TO_BYTE,
+  INT_TO_CHAR,
+  INT_TO_SHORT,
+  ADD_INT,
+  SUB_INT,
+  MUL_INT,
+  DIV_INT,
+  REM_INT,
+  AND_INT,
+  OR_INT,
+  XOR_INT,
+  SHL_INT,
+  SHR_INT,
+  USHR_INT,
+  ADD_LONG,
+  SUB_LONG,
+  MUL_LONG,
+  DIV_LONG,
+  REM_LONG,
+  AND_LONG,
+  OR_LONG,
+  XOR_LONG,
+  SHL_LONG,
+  SHR_LONG,
+  USHR_LONG,
+  ADD_FLOAT,
+  SUB_FLOAT,
+  MUL_FLOAT,
+  DIV_FLOAT,
+  REM_FLOAT,
+  ADD_DOUBLE,
+  SUB_DOUBLE,
+  MUL_DOUBLE,
+  DIV_DOUBLE,
+  REM_DOUBLE,
+  ADD_INT_2ADDR,
+  SUB_INT_2ADDR,
+  MUL_INT_2ADDR,
+  DIV_INT_2ADDR,
+  REM_INT_2ADDR,
+  AND_INT_2ADDR,
+  OR_INT_2ADDR,
+  XOR_INT_2ADDR,
+  SHL_INT_2ADDR,
+  SHR_INT_2ADDR,
+  USHR_INT_2ADDR,
+  ADD_LONG_2ADDR,
+  SUB_LONG_2ADDR,
+  MUL_LONG_2ADDR,
+  DIV_LONG_2ADDR,
+  REM_LONG_2ADDR,
+  AND_LONG_2ADDR,
+  OR_LONG_2ADDR,
+  XOR_LONG_2ADDR,
+  SHL_LONG_2ADDR,
+  SHR_LONG_2ADDR,
+  USHR_LONG_2ADDR,
+  ADD_FLOAT_2ADDR,
+  SUB_FLOAT_2ADDR,
+  MUL_FLOAT_2ADDR,
+  DIV_FLOAT_2ADDR,
+  REM_FLOAT_2ADDR,
+  ADD_DOUBLE_2ADDR,
+  SUB_DOUBLE_2ADDR,
+  MUL_DOUBLE_2ADDR,
+  DIV_DOUBLE_2ADDR,
+  REM_DOUBLE_2ADDR,
+  ADD_INT_LIT16,
+  RSUB_INT,
+  MUL_INT_LIT16,
+  DIV_INT_LIT16,
+  REM_INT_LIT16,
+  AND_INT_LIT16,
+  OR_INT_LIT16,
+  XOR_INT_LIT16,
+  ADD_INT_LIT8,
+  RSUB_INT_LIT8,
+  MUL_INT_LIT8,
+  DIV_INT_LIT8,
+  REM_INT_LIT8,
+  AND_INT_LIT8,
+  OR_INT_LIT8,
+  XOR_INT_LIT8,
+  SHL_INT_LIT8,
+  SHR_INT_LIT8,
+  USHR_INT_LIT8,
+  IGET_QUICK,
+  IGET_WIDE_QUICK,
+  IGET_OBJECT_QUICK,
+  IPUT_QUICK,
+  IPUT_WIDE_QUICK,
+  IPUT_OBJECT_QUICK,
+  INVOKE_VIRTUAL_QUICK,
+  INVOKE_VIRTUAL_QUICK_RANGE,
+  IPUT_BOOLEAN_QUICK,
+  IPUT_BYTE_QUICK,
+  IPUT_CHAR_QUICK,
+  IPUT_SHORT_QUICK,
+  UNUSED_EF,
+  UNUSED_F0,
+  UNUSED_F1,
+  UNUSED_F2,
+  UNUSED_F3,
+  UNUSED_F4,
+  UNUSED_F5,
+  UNUSED_F6,
+  UNUSED_F7,
+  UNUSED_F8,
+  UNUSED_F9,
+  UNUSED_FA,
+  UNUSED_FB,
+  UNUSED_FC,
+  UNUSED_FD,
+  UNUSED_FE,
+  UNUSED_FF;
+
+  public static boolean isBetween(Opcode opcode, Opcode opcode1, Opcode opcode2) {
+    return (opcode.ordinal() >= opcode1.ordinal() && opcode.ordinal() <= opcode2.ordinal());
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java b/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java
new file mode 100644
index 0000000..fb1d093
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/OpcodeInfo.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.rawdex.formats.AbstractFormat;
+
+/**
+ * Every Instruction points to an OpcodeInfo object that holds useful information
+ * about that kind of instruction, including the Format that allows us to read the
+ * instructions fields correctly.
+ */
+public class OpcodeInfo {
+  public final Opcode opcode;
+  public final String name;
+  public final int value;
+  public final AbstractFormat format;
+
+  /**
+   * Construct an OpcodeInfo. A static list of these is created in Instruction.java.
+   */
+  public OpcodeInfo(Opcode opcode, String name, int opcodeValue, AbstractFormat fmt) {
+    this.opcode = opcode;
+    this.name = name;
+    this.value = opcodeValue;
+    this.format = fmt;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java b/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java
new file mode 100644
index 0000000..0db5efd
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ParameterAnnotation.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ParameterAnnotation implements RawDexObject {
+  public int methodIdx;
+  public Offset annotationsOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    methodIdx = file.readUInt();
+    annotationsOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(methodIdx);
+    file.getOffsetTracker().tryToWriteOffset(annotationsOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.METHOD_ID && methodIdx >= insertedIdx) {
+      methodIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java
new file mode 100644
index 0000000..3ccc82f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/ProtoIdItem.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class ProtoIdItem implements RawDexObject {
+  public int shortyIdx;
+  public int returnTypeIdx;
+  public Offset parametersOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    shortyIdx = file.readUInt();
+    returnTypeIdx = file.readUInt();
+    parametersOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(shortyIdx);
+    file.writeUInt(returnTypeIdx);
+    file.getOffsetTracker().tryToWriteOffset(parametersOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.STRING_ID && shortyIdx >= insertedIdx) {
+      shortyIdx++;
+    }
+    if (kind == IndexUpdateKind.TYPE_ID && returnTypeIdx >= insertedIdx) {
+      returnTypeIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java
new file mode 100644
index 0000000..483ed6c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexFile.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.List;
+
+public class RawDexFile implements RawDexObject {
+  private OffsetTracker offsetTracker;
+
+  public HeaderItem header;
+
+  public MapList mapList;
+
+  // Can be allocated after reading the header.
+  public List<StringIdItem> stringIds;
+  public List<TypeIdItem> typeIds;
+  public List<ProtoIdItem> protoIds;
+  public List<FieldIdItem> fieldIds;
+  public List<MethodIdItem> methodIds;
+  public List<ClassDefItem> classDefs;
+
+  // Need to be allocated later (will be allocated in MapList.java)
+  public List<StringDataItem> stringDatas;
+  public List<ClassDataItem> classDatas;
+  public List<TypeList> typeLists;
+  public List<CodeItem> codeItems;
+  public DebugInfoItem debugInfoItem;
+  public List<AnnotationsDirectoryItem> annotationsDirectoryItems;
+  public List<AnnotationSetRefList> annotationSetRefLists;
+  public List<AnnotationSetItem> annotationSetItems;
+  public List<AnnotationItem> annotationItems;
+  public List<EncodedArrayItem> encodedArrayItems;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    // Get a reference to the OffsetTracker, so that IdCreator can use it.
+    offsetTracker = file.getOffsetTracker();
+
+    file.seek(0);
+
+    // Read header.
+    (header = new HeaderItem()).read(file);
+
+    // We can allocate all of these now.
+    stringIds = new ArrayList<StringIdItem>(header.stringIdsSize);
+    typeIds = new ArrayList<TypeIdItem>(header.typeIdsSize);
+    protoIds = new ArrayList<ProtoIdItem>(header.protoIdsSize);
+    fieldIds = new ArrayList<FieldIdItem>(header.fieldIdsSize);
+    methodIds = new ArrayList<MethodIdItem>(header.methodIdsSize);
+    classDefs = new ArrayList<ClassDefItem>(header.classDefsSize);
+
+    mapList = new MapList(this);
+    mapList.read(file);
+
+    file.getOffsetTracker().associateOffsets();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.seek(0);
+
+    // We read the header first, and then the map list, and then everything
+    // else. Therefore, when we get to the end of the header, tell OffsetTracker
+    // to skip past the map list offsets, and then when we get to the map list,
+    // tell OffsetTracker to skip back there, and then return to where it was previously.
+
+    // Update the map items' sizes first
+    // - but only update the items that we expect to have changed size.
+    // ALSO update the header's table sizes!
+    for (MapItem mapItem : mapList.mapItems) {
+      switch (mapItem.type) {
+        case MapItem.TYPE_STRING_ID_ITEM:
+          if (mapItem.size != stringIds.size()) {
+            Log.debug("Updating StringIDs List size: " + stringIds.size());
+            mapItem.size = stringIds.size();
+            header.stringIdsSize = stringIds.size();
+          }
+          break;
+        case MapItem.TYPE_STRING_DATA_ITEM:
+          if (mapItem.size != stringDatas.size()) {
+            Log.debug("Updating StringDatas List size: " + stringDatas.size());
+            mapItem.size = stringDatas.size();
+          }
+          break;
+        case MapItem.TYPE_METHOD_ID_ITEM:
+          if (mapItem.size != methodIds.size()) {
+            Log.debug("Updating MethodIDs List size: " + methodIds.size());
+            mapItem.size = methodIds.size();
+            header.methodIdsSize = methodIds.size();
+          }
+          break;
+        case MapItem.TYPE_FIELD_ID_ITEM:
+          if (mapItem.size != fieldIds.size()) {
+            Log.debug("Updating FieldIDs List size: " + fieldIds.size());
+            mapItem.size = fieldIds.size();
+            header.fieldIdsSize = fieldIds.size();
+          }
+          break;
+        case MapItem.TYPE_PROTO_ID_ITEM:
+          if (mapItem.size != protoIds.size()) {
+            Log.debug("Updating ProtoIDs List size: " + protoIds.size());
+            mapItem.size = protoIds.size();
+            header.protoIdsSize = protoIds.size();
+          }
+          break;
+        case MapItem.TYPE_TYPE_ID_ITEM:
+          if (mapItem.size != typeIds.size()) {
+            Log.debug("Updating TypeIDs List size: " + typeIds.size());
+            mapItem.size = typeIds.size();
+            header.typeIdsSize = typeIds.size();
+          }
+          break;
+        case MapItem.TYPE_TYPE_LIST:
+          if (mapItem.size != typeLists.size()) {
+            Log.debug("Updating TypeLists List size: " + typeLists.size());
+            mapItem.size = typeLists.size();
+          }
+          break;
+        default:
+      }
+    }
+
+    // Use the map list to write the file.
+    for (MapItem mapItem : mapList.mapItems) {
+      switch (mapItem.type) {
+        case MapItem.TYPE_HEADER_ITEM:
+          header.write(file);
+          file.getOffsetTracker().skipToAfterMapList();
+          break;
+        case MapItem.TYPE_STRING_ID_ITEM:
+          if (mapItem.size != stringIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches StringIDs table size " + stringIds.size());
+          }
+          for (StringIdItem stringId : stringIds) {
+            stringId.write(file);
+          }
+          break;
+        case MapItem.TYPE_TYPE_ID_ITEM:
+          if (mapItem.size != typeIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches TypeIDs table size " + typeIds.size());
+          }
+          for (TypeIdItem typeId : typeIds) {
+            typeId.write(file);
+          }
+          break;
+        case MapItem.TYPE_PROTO_ID_ITEM:
+          if (mapItem.size != protoIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches ProtoIDs table size " + protoIds.size());
+          }
+          for (ProtoIdItem protoId : protoIds) {
+            protoId.write(file);
+          }
+          break;
+        case MapItem.TYPE_FIELD_ID_ITEM:
+          if (mapItem.size != fieldIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches FieldIDs table size " + fieldIds.size());
+          }
+          for (FieldIdItem fieldId : fieldIds) {
+            fieldId.write(file);
+          }
+          break;
+        case MapItem.TYPE_METHOD_ID_ITEM:
+          if (mapItem.size != methodIds.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches MethodIDs table size " + methodIds.size());
+          }
+          for (MethodIdItem methodId : methodIds) {
+            methodId.write(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DEF_ITEM:
+          if (mapItem.size != classDefs.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches ClassDefs table size " + classDefs.size());
+          }
+          for (ClassDefItem classDef : classDefs) {
+            classDef.write(file);
+          }
+          break;
+        case MapItem.TYPE_MAP_LIST:
+          file.getOffsetTracker().goBackToMapList();
+          mapList.write(file);
+          file.getOffsetTracker().goBackToPreviousPoint();
+          break;
+        case MapItem.TYPE_TYPE_LIST:
+          if (mapItem.size != typeLists.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches TypeLists table size " + typeLists.size());
+          }
+          for (TypeList typeList : typeLists) {
+            typeList.write(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_REF_LIST:
+          if (mapItem.size != annotationSetRefLists.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationSetRefLists table size "
+                + annotationSetRefLists.size());
+          }
+          for (AnnotationSetRefList annotationSetRefList : annotationSetRefLists) {
+            annotationSetRefList.write(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATION_SET_ITEM:
+          if (mapItem.size != annotationSetItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationSetItems table size "
+                + annotationSetItems.size());
+          }
+          for (AnnotationSetItem annotationSetItem : annotationSetItems) {
+            annotationSetItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_CLASS_DATA_ITEM:
+          if (mapItem.size != classDatas.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches ClassDataItems table size " + classDatas.size());
+          }
+          for (ClassDataItem classData : classDatas) {
+            classData.write(file);
+          }
+          break;
+        case MapItem.TYPE_CODE_ITEM:
+          if (mapItem.size != codeItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches CodeItems table size " + codeItems.size());
+          }
+          for (CodeItem codeItem : codeItems) {
+            codeItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_STRING_DATA_ITEM:
+          if (mapItem.size != stringDatas.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches StringDataItems table size "
+                + stringDatas.size());
+          }
+          for (StringDataItem stringDataItem : stringDatas) {
+            stringDataItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_DEBUG_INFO_ITEM:
+          debugInfoItem.write(file);
+          break;
+        case MapItem.TYPE_ANNOTATION_ITEM:
+          if (mapItem.size != annotationItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationItems table size "
+                + annotationItems.size());
+          }
+          for (AnnotationItem annotationItem : annotationItems) {
+            annotationItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_ENCODED_ARRAY_ITEM:
+          if (mapItem.size != encodedArrayItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches EncodedArrayItems table size "
+                + encodedArrayItems.size());
+          }
+          for (EncodedArrayItem encodedArrayItem : encodedArrayItems) {
+            encodedArrayItem.write(file);
+          }
+          break;
+        case MapItem.TYPE_ANNOTATIONS_DIRECTORY_ITEM:
+          if (mapItem.size != annotationsDirectoryItems.size()) {
+            Log.errorAndQuit("MapItem's size " + mapItem.size
+                + " no longer matches AnnotationDirectoryItems table size "
+                + annotationsDirectoryItems.size());
+          }
+          for (AnnotationsDirectoryItem annotationsDirectory : annotationsDirectoryItems) {
+            annotationsDirectory.write(file);
+          }
+          break;
+        default:
+          Log.errorAndQuit("Encountered unknown map item in map item list.");
+      }
+    }
+
+    file.getOffsetTracker().updateOffsets(file);
+  }
+
+  /**
+   * Given a DexRandomAccessFile, calculate the correct adler32 checksum for it.
+   */
+  private int calculateAdler32Checksum(DexRandomAccessFile file) throws IOException {
+    // Skip magic + checksum.
+    file.seek(12);
+    int a = 1;
+    int b = 0;
+    while (file.getFilePointer() < file.length()) {
+      a = (a + file.readUnsignedByte()) % 65521;
+      b = (b + a) % 65521;
+    }
+    return (b << 16) | a;
+  }
+
+  /**
+   * Given a DexRandomAccessFile, update the file size, data size, and checksum.
+   */
+  public void updateHeader(DexRandomAccessFile file) throws IOException {
+    // File size must be updated before checksum.
+    int newFileSize = (int) file.length();
+    file.seek(32);
+    file.writeUInt(newFileSize);
+
+    // Data size must be updated before checksum.
+    int newDataSize = newFileSize - header.dataOff.getNewPositionOfItem();
+    file.seek(104);
+    file.writeUInt(newDataSize);
+
+    // Now update the checksum.
+    int newChecksum = calculateAdler32Checksum(file);
+    file.seek(8);
+    file.writeUInt(newChecksum);
+
+    header.fileSize = newFileSize;
+    header.dataSize = newDataSize;
+    header.checksum = newChecksum;
+  }
+
+  /**
+   * This should only be called from NewItemCreator.
+   */
+  public OffsetTracker getOffsetTracker() {
+    return offsetTracker;
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    for (TypeIdItem typeId : typeIds) {
+      typeId.incrementIndex(kind, insertedIdx);
+    }
+    for (ProtoIdItem protoId : protoIds) {
+      protoId.incrementIndex(kind, insertedIdx);
+    }
+    for (FieldIdItem fieldId : fieldIds) {
+      fieldId.incrementIndex(kind, insertedIdx);
+    }
+    for (MethodIdItem methodId : methodIds) {
+      methodId.incrementIndex(kind, insertedIdx);
+    }
+    for (ClassDefItem classDef : classDefs) {
+      classDef.incrementIndex(kind, insertedIdx);
+    }
+    for (ClassDataItem classData : classDatas) {
+      classData.incrementIndex(kind, insertedIdx);
+    }
+    if (typeLists != null) {
+      for (TypeList typeList : typeLists) {
+        typeList.incrementIndex(kind, insertedIdx);
+      }
+    }
+    for (CodeItem codeItem : codeItems) {
+      codeItem.incrementIndex(kind, insertedIdx);
+    }
+    if (annotationsDirectoryItems != null) {
+      for (AnnotationsDirectoryItem annotationsDirectoryItem : annotationsDirectoryItems) {
+        annotationsDirectoryItem.incrementIndex(kind, insertedIdx);
+      }
+    }
+    if (annotationItems != null) {
+      for (AnnotationItem annotationItem : annotationItems) {
+        annotationItem.incrementIndex(kind, insertedIdx);
+      }
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java
new file mode 100644
index 0000000..0b67e9c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/RawDexObject.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+/**
+ * Base class for any data structure that we may read or write from a DEX file.
+ */
+public interface RawDexObject {
+  /**
+   * Populate information for this DEX data from the file.
+   * @param file Input file, should already be "seeked" to the correct position.
+   * @throws IOException If there's a problem writing to the file.
+   */
+  public void read(DexRandomAccessFile file) throws IOException;
+
+  /**
+   * Write information for this DEX data to the file.
+   * @param file Output file, should already be "seeked" to the correct position.
+   * @throws IOException If there's a problem writing to the file.
+   */
+  public void write(DexRandomAccessFile file) throws IOException;
+
+  public static enum IndexUpdateKind {
+    STRING_ID,
+    TYPE_ID,
+    PROTO_ID,
+    FIELD_ID,
+    METHOD_ID
+  }
+
+  /**
+   * When we insert a new string, type, proto, field or method into the DEX file,
+   * this must be called. We may have inserted something into the middle of a table,
+   * so any indices pointing afterwards must be updated.
+   */
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx);
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java
new file mode 100644
index 0000000..87c603e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/StringDataItem.java
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import dexfuzz.Log;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+public class StringDataItem implements RawDexObject {
+  private int size;
+  private String data;
+  private byte[] dataAsBytes;
+  private boolean writeRawBytes;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUleb128();
+    if (size != 0) {
+      dataAsBytes = file.readDexUtf(size);
+      data = new String(dataAsBytes, StandardCharsets.US_ASCII);
+      if (size != data.length()) {
+        Log.warn("Don't have full support for decoding MUTF-8 yet, DEX file "
+            + "may be incorrectly mutated. Avoid using this test case for now.");
+        writeRawBytes = true;
+      }
+    } else {
+      // Read past the null byte.
+      file.readByte();
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUleb128(size);
+    if (size > 0) {
+      if (writeRawBytes) {
+        file.writeDexUtf(dataAsBytes);
+      } else {
+        file.writeDexUtf(data.getBytes(StandardCharsets.US_ASCII));
+      }
+    } else {
+      // Write out the null byte.
+      file.writeByte(0);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+
+  public void setSize(int size) {
+    this.size = size;
+  }
+
+  public int getSize() {
+    return size;
+  }
+
+  public void setString(String data) {
+    this.data = data;
+  }
+
+  public String getString() {
+    if (writeRawBytes) {
+      Log.warn("Reading a string that hasn't been properly decoded! Returning empty string.");
+      return "";
+    }
+    return data;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java
new file mode 100644
index 0000000..da8c294
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/StringIdItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class StringIdItem implements RawDexObject {
+  public Offset stringDataOff;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    stringDataOff = file.getOffsetTracker().getNewOffset(file.readUInt());
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.getOffsetTracker().tryToWriteOffset(stringDataOff, file, false /* ULEB128 */);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java
new file mode 100644
index 0000000..99be6de
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TryItem.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TryItem implements RawDexObject {
+  public int startAddr;
+  public short insnCount;
+  public short handlerOff; // Not a global offset; don't need to adjust like an Offset.
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    startAddr = file.readUInt();
+    insnCount = file.readUShort();
+    handlerOff = file.readUShort();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUInt(startAddr);
+    file.writeUShort(insnCount);
+    file.writeUShort(handlerOff);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    // Do nothing.
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java
new file mode 100644
index 0000000..bb3eff1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeIdItem.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeIdItem implements RawDexObject {
+  public int descriptorIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    descriptorIdx = file.readUInt();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(descriptorIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.STRING_ID && descriptorIdx >= insertedIdx) {
+      descriptorIdx++;
+    }
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java
new file mode 100644
index 0000000..a788b47
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeItem.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeItem implements RawDexObject {
+  public short typeIdx;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    typeIdx = file.readUShort();
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.writeUShort(typeIdx);
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    if (kind == IndexUpdateKind.TYPE_ID && typeIdx >= insertedIdx) {
+      typeIdx++;
+    }
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java b/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java
new file mode 100644
index 0000000..90a2b57
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/TypeList.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex;
+
+import java.io.IOException;
+
+public class TypeList implements RawDexObject {
+  public int size;
+  public TypeItem[] list;
+
+  @Override
+  public void read(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().getNewOffsettable(file, this);
+    size = file.readUInt();
+    list = new TypeItem[size];
+    for (int i = 0; i < size; i++) {
+      (list[i] = new TypeItem()).read(file);
+    }
+  }
+
+  @Override
+  public void write(DexRandomAccessFile file) throws IOException {
+    file.alignForwards(4);
+    file.getOffsetTracker().updatePositionOfNextOffsettable(file);
+    file.writeUInt(size);
+    for (TypeItem typeItem : list) {
+      typeItem.write(file);
+    }
+  }
+
+  @Override
+  public void incrementIndex(IndexUpdateKind kind, int insertedIdx) {
+    for (TypeItem type : list) {
+      type.incrementIndex(kind, insertedIdx);
+    }
+  }
+
+  /**
+   * Returns if this TypeList comes before the provided TypeList, considering the legal
+   * ordering of TypeLists in DEX files.
+   */
+  public boolean comesBefore(TypeList other) {
+    int checkSize = Math.min(size, other.size);
+    for (int i = 0; i < checkSize; i++) {
+      if (list[i].typeIdx < other.list[i].typeIdx) {
+        return true;
+      } else if (list[i].typeIdx > other.list[i].typeIdx) {
+        return false;
+      }
+    }
+    if (size == other.size) {
+      return false;
+    }
+    return true;
+  }
+}
\ No newline at end of file
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java
new file mode 100644
index 0000000..29b15ae
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/AbstractFormat.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+/**
+ * Every Format subclasses this AbstractFormat. The subclasses then implement these
+ * methods to write out a provided Instruction according to this format, and also methods
+ * to read the vregs from an Instruction's raw bytes.
+ * Hierarchy is as follows:
+ * AbstractFormat
+ *   |____________Format1
+ *   |              |_____Format10t
+ *   |              |_____Format10x
+ *   |              |_____Format11n
+ *   |              |_____Format11x
+ *   |              |_____Format12x
+ *   |____________Format2
+ *   |              |_____Format20bc
+ *   |              |_____Format20t
+ *     etc...
+ */
+public abstract class AbstractFormat {
+  /**
+   * Get the size of an Instruction that has this format.
+   */
+  public abstract int getSize();
+
+  /**
+   * Given a file handle and an instruction, write that Instruction out to the file
+   * correctly, considering the current format.
+   */
+  public abstract void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException;
+
+  /**
+   * Read the value of vA, considering this format.
+   */
+  public abstract long getA(byte[] raw) throws IOException;
+
+  /**
+   * Read the value of vB, considering this format.
+   */
+  public abstract long getB(byte[] raw) throws IOException;
+
+  /**
+   * Read the value of vC, considering this format.
+   */
+  public abstract long getC(byte[] raw) throws IOException;
+
+  /**
+   * Only Format35c should return true for this.
+   */
+  public abstract boolean needsInvokeFormatInfo();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java
new file mode 100644
index 0000000..e2b164a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsConst.java
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.Instruction;
+
+/**
+ * Every Format that contains a value that is a constant (that includes instructions like
+ * const/4, but also add-int/lit8) should implement this interface, to allow the constant
+ * part of a provided Instruction to be read and set correctly.
+ */
+public interface ContainsConst {
+  public long getConst(Instruction insn);
+
+  public void setConst(Instruction insn, long constant);
+
+  public long getConstRange();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java
new file mode 100644
index 0000000..5f66b0e
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsPoolIndex.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+/**
+ * Every Format that contains a value that is an index into the ID tables of this DEX file
+ * should implement this interface, to allow the index part of a provided Instruction
+ * to be read and set correctly.
+ */
+public interface ContainsPoolIndex {
+  public enum PoolIndexKind {
+    Type,
+    Field,
+    String,
+    Method,
+    Invalid
+  }
+
+  public int getPoolIndex(Instruction insn);
+
+  public void setPoolIndex(Instruction insn, int poolIndex);
+
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info);
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
new file mode 100644
index 0000000..bb24e61
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsTarget.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.Instruction;
+
+/**
+ * Every Format that contains an offset to a target instruction
+ * should implement this interface, to allow the offset to be read and set correctly.
+ */
+public interface ContainsTarget {
+  public long getTarget(Instruction insn);
+
+  public void setTarget(Instruction insn, long target);
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
new file mode 100644
index 0000000..40ba5ac
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/ContainsVRegs.java
@@ -0,0 +1,25 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+/**
+ * Every Format that contains virtual registers should implement this interface,
+ * to allow the number of virtual registers specified by the format to be found.
+ */
+public interface ContainsVRegs {
+  public int getVRegCount();
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java
new file mode 100644
index 0000000..aae7469
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format00x.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format00x extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 0;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java
new file mode 100644
index 0000000..e503513
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format1.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format1 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 1;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java
new file mode 100644
index 0000000..a9e13f1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10t.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format10t extends Format1 implements ContainsTarget {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregA;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregA = target;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java
new file mode 100644
index 0000000..aabf725
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format10x.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format10x extends Format1 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java
new file mode 100644
index 0000000..4b8c35c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11n.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format11n extends Format1 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 4);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java
new file mode 100644
index 0000000..e8963f1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format11x.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format11x extends Format1 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java
new file mode 100644
index 0000000..170ebe1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format12x.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format12x extends Format1 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java
new file mode 100644
index 0000000..5ddc124
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format2.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format2 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 2;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java
new file mode 100644
index 0000000..4f21489
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20bc.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+// NB: This format is only used for statically determined verification errors,
+// so we shouldn't encounter it in ART. (Or even before, as they are only written in during
+// verification, which comes after our fuzzing...)
+// Therefore, no need to say this implements ContainsPoolIndex, even though it is a *c format
+public class Format20bc extends Format2 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java
new file mode 100644
index 0000000..1e33c15
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format20t.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format20t extends Format2 implements ContainsTarget {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    file.writeUShort((short) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregA;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregA = target;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java
new file mode 100644
index 0000000..e60b54a
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21c.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format21c extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.CONST_STRING) {
+      return PoolIndexKind.String;
+    }
+    if (info.opcode == Opcode.CONST_CLASS
+        || info.opcode == Opcode.CHECK_CAST
+        || info.opcode == Opcode.NEW_INSTANCE) {
+      return PoolIndexKind.Type;
+    }
+    // everything else is static field accesses
+    return PoolIndexKind.Field;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java
new file mode 100644
index 0000000..c279f6c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21h.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21h extends Format2 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 16);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java
new file mode 100644
index 0000000..594d1a7
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21s.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21s extends Format2 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 16);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java
new file mode 100644
index 0000000..dc3d659
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format21t.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format21t extends Format2 implements ContainsTarget, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregB = target;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java
new file mode 100644
index 0000000..bbdc7e3
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22b.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22b extends Format2 implements ContainsVRegs, ContainsConst {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeByte((byte) insn.vregB);
+    file.writeByte((byte) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedByteFromByte(raw, 3);
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregC;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregC = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 8);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java
new file mode 100644
index 0000000..4dff336
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22c.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format22c extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregC;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregC = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.INSTANCE_OF || info.opcode == Opcode.NEW_ARRAY) {
+      return PoolIndexKind.Type;
+    }
+    return PoolIndexKind.Field;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java
new file mode 100644
index 0000000..b66e14c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22cs.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format22cs extends Format2 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    throw new Error("Did not expect to have to write a 22cs instruction!");
+    // If for some reason 22cs instructions were in DEX files in the future, uncomment:
+    //file.writeByte((byte) insn.info.value);
+    //file.writeByte((byte) (insn.vreg_a | (insn.vreg_b << 4)));
+    //file.writeUShort((short) insn.vreg_c);
+    //return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregC;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregC = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    // This should technically be a byte offset, but we should never encounter
+    // this format during DEX mutation anyway. (see writeToFile())
+    return PoolIndexKind.Field;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java
new file mode 100644
index 0000000..9497179
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22s.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22s extends Format2 implements ContainsVRegs, ContainsConst {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregC;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregC = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1 << 16);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java
new file mode 100644
index 0000000..5ea9579
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22t.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22t extends Format2 implements ContainsTarget, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.vregA | (insn.vregB << 4)));
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregC;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregC = target;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java
new file mode 100644
index 0000000..5cd3d73
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format22x.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format22x extends Format2 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java
new file mode 100644
index 0000000..8ce7c7c
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format23x.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format23x extends Format2 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeByte((byte) insn.vregB);
+    file.writeByte((byte) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 3);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 3;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java
new file mode 100644
index 0000000..d9d7ce1
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 3;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java
new file mode 100644
index 0000000..0b62646
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format30t.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format30t extends Format3 implements ContainsTarget {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    file.writeUInt((int) insn.vregA);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregA;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregA = target;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java
new file mode 100644
index 0000000..435fa19
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31c.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format31c extends Format3 implements ContainsVRegs, ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUInt((int) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    return PoolIndexKind.String;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java
new file mode 100644
index 0000000..d54c074
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31i.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format31i extends Format3 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUInt((int) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1L << 32);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java
new file mode 100644
index 0000000..b74db8f
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format31t.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format31t extends Format3 implements ContainsTarget, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUInt((int) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedIntFromFourBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getTarget(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setTarget(Instruction insn, long target) {
+    insn.vregB = target;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java
new file mode 100644
index 0000000..2f46105
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format32x.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format32x extends Format3 implements ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) 0); // padding
+    file.writeUShort((short) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 2;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java
new file mode 100644
index 0000000..e4a50ff
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35c.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format35c extends Format3 implements ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+    file.writeUShort((short) insn.vregB);
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+        | insn.invokeFormatInfo.vregE));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return true;
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.FILLED_NEW_ARRAY) {
+      return PoolIndexKind.Type;
+    }
+    return PoolIndexKind.Method;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java
new file mode 100644
index 0000000..f622385
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35mi.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format35mi extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+    file.writeUShort((short) insn.vregB);
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+        | insn.invokeFormatInfo.vregE));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java
new file mode 100644
index 0000000..3be9707
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format35ms.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format35ms extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) (insn.invokeFormatInfo.vregG | (insn.vregA << 4)));
+    file.writeUShort((short) insn.vregB);
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregD << 4) | insn.vregC));
+    file.writeByte((byte) ((insn.invokeFormatInfo.vregF << 4)
+        | insn.invokeFormatInfo.vregE));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedHighNibbleFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedLowNibbleFromByte(raw, 4);
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return true;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java
new file mode 100644
index 0000000..630825d
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rc.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+import dexfuzz.rawdex.Opcode;
+import dexfuzz.rawdex.OpcodeInfo;
+
+import java.io.IOException;
+
+public class Format3rc extends Format3 implements ContainsPoolIndex {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+
+  @Override
+  public int getPoolIndex(Instruction insn) {
+    return (int) insn.vregB;
+  }
+
+  @Override
+  public void setPoolIndex(Instruction insn, int poolIndex) {
+    insn.vregB = poolIndex;
+  }
+
+  @Override
+  public PoolIndexKind getPoolIndexKind(OpcodeInfo info) {
+    if (info.opcode == Opcode.FILLED_NEW_ARRAY_RANGE) {
+      return PoolIndexKind.Type;
+    }
+    return PoolIndexKind.Method;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java
new file mode 100644
index 0000000..7b6ceea
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rmi.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3rmi extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java
new file mode 100644
index 0000000..17535f9
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format3rms.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format3rms extends Format3 {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) insn.vregB);
+    file.writeUShort((short) insn.vregC);
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedShortFromTwoBytes(raw, 4);
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java
new file mode 100644
index 0000000..bc141be
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format5.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format5 extends AbstractFormat {
+  @Override
+  public int getSize() {
+    return 5;
+  }
+
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return 0;
+  }
+
+  @Override
+  public boolean needsInvokeFormatInfo() {
+    return false;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java
new file mode 100644
index 0000000..fc2e0ed
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/Format51l.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+import dexfuzz.rawdex.DexRandomAccessFile;
+import dexfuzz.rawdex.Instruction;
+
+import java.io.IOException;
+
+public class Format51l extends Format5 implements ContainsConst, ContainsVRegs {
+  @Override
+  public void writeToFile(DexRandomAccessFile file, Instruction insn) throws IOException {
+    file.writeByte((byte) insn.info.value);
+    file.writeByte((byte) insn.vregA);
+    file.writeUShort((short) (insn.vregB & 0xffff));
+    file.writeUShort((short) ((insn.vregB & 0xffff0000) >> 16));
+    file.writeUShort((short) ((insn.vregB & 0xffff00000000L) >> 32));
+    file.writeUShort((short) ((insn.vregB & 0xffff000000000000L) >> 48));
+    return;
+  }
+
+  @Override
+  public long getA(byte[] raw) throws IOException {
+    return RawInsnHelper.getUnsignedByteFromByte(raw, 1);
+  }
+
+  @Override
+  public long getB(byte[] raw) throws IOException {
+    return RawInsnHelper.getSignedLongFromEightBytes(raw, 2);
+  }
+
+  @Override
+  public long getC(byte[] raw) throws IOException {
+    return (long) 0;
+  }
+
+  @Override
+  public long getConst(Instruction insn) {
+    return insn.vregB;
+  }
+
+  @Override
+  public void setConst(Instruction insn, long constant) {
+    insn.vregB = constant;
+  }
+
+  @Override
+  public long getConstRange() {
+    return (1L << 63);
+  }
+
+  @Override
+  public int getVRegCount() {
+    return 1;
+  }
+}
diff --git a/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java b/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java
new file mode 100644
index 0000000..b16a1b5
--- /dev/null
+++ b/tools/dexfuzz/src/dexfuzz/rawdex/formats/RawInsnHelper.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package dexfuzz.rawdex.formats;
+
+/**
+ * Class consisting of static methods used for common read/write operations
+ * perfomed in the Format classes.
+ */
+public class RawInsnHelper {
+  /**
+   * Read a signed byte from the idx into the raw array.
+   */
+  public static long getSignedByteFromByte(byte[] raw, int idx) {
+    return (long) raw[idx];
+  }
+
+  /**
+   * Read an unsigned byte from the idx into the raw array.
+   */
+  public static long getUnsignedByteFromByte(byte[] raw, int idx) {
+    return ((long) raw[idx]) & 0xff;
+  }
+
+  /**
+   * Read an unsigned lower 4 bits from the idx into the raw array.
+   */
+  public static long getUnsignedLowNibbleFromByte(byte[] raw, int idx) {
+    return ((long) raw[idx]) & 0xf;
+  }
+
+  /**
+   * Read an unsigned higher 4 bits from the idx into the raw array.
+   */
+  public static long getUnsignedHighNibbleFromByte(byte[] raw, int idx) {
+    return (((long) raw[idx]) >> 4) & 0xf;
+  }
+
+  /**
+   * Read an unsigned 2 bytes as a short from the idx into the raw array.
+   */
+  public static long getUnsignedShortFromTwoBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8));
+  }
+
+  /**
+   * Read a signed 2 bytes as a short from the idx into the raw array.
+   */
+  public static long getSignedShortFromTwoBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | (((long) raw[idx + 1]) << 8));
+  }
+
+  /**
+   * Read an unsigned 4 bytes as an int from the idx into the raw array.
+   */
+  public static long getUnsignedIntFromFourBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8)
+        | ((((long) raw[idx + 2]) & 0xff) << 16)
+        | ((((long) raw[idx + 3]) & 0xff) << 24) );
+  }
+
+  /**
+   * Read a signed 4 bytes as an int from the idx into the raw array.
+   */
+  public static long getSignedIntFromFourBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8)
+        | ((((long) raw[idx + 2]) & 0xff) << 16)
+        | (((long) raw[idx + 3]) << 24) );
+  }
+
+  /**
+   * Read a signed 8 bytes as a long from the idx into the raw array.
+   */
+  public static long getSignedLongFromEightBytes(byte[] raw, int idx) {
+    return (long) ( (((long) raw[idx]) & 0xff)
+        | ((((long) raw[idx + 1]) & 0xff) << 8)
+        | ((((long) raw[idx + 2]) & 0xff) << 16)
+        | ((((long) raw[idx + 3]) & 0xff) << 24)
+        | ((((long) raw[idx + 4]) & 0xff) << 32)
+        | ((((long) raw[idx + 5]) & 0xff) << 40)
+        | ((((long) raw[idx + 6]) & 0xff) << 48)
+        | (((long) raw[idx + 7]) << 56) );
+  }
+
+  /**
+   * Given an idx into a raw array, and an int, write that int into the array at that position.
+   */
+  public static void writeUnsignedIntToFourBytes(byte[] raw, int idx, int value) {
+    raw[idx] = (byte) (value & 0xFF);
+    raw[idx + 1] = (byte) ((value & 0xFF00) >>> 8);
+    raw[idx + 2] = (byte) ((value & 0xFF0000) >>> 16);
+    raw[idx + 3] = (byte) ((value & 0xFF000000) >>> 24);
+  }
+
+  /**
+   * Given an idx into a raw array, and a short, write that int into the array at that position.
+   */
+  public static void writeUnsignedShortToTwoBytes(byte[] raw, int idx, int value) {
+    raw[idx] = (byte) (value & 0xFF);
+    raw[idx + 1] = (byte) ((value & 0xFF00) >>> 8);
+  }
+}