Adds the dexfuzz fuzzing tool to ART.

This tool can mutate DEX files, and execute them with multiple backends
of ART, making sure that all backends agree on the output of the
program. This should help identify bugs in particular backends, should
they produce output that disagrees with the other backends.

Build with mmm tools/dexfuzz from within art/.

See the README for details about how to use dexfuzz.

Change-Id: I3fc5d84a08b7d142a1fdc3d43a0324ce16b43608
diff --git a/Android.mk b/Android.mk
index d11d011..cc3e0c4 100644
--- a/Android.mk
+++ b/Android.mk
@@ -104,6 +104,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/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);
+  }
+}