Merge commit 'refs/changes/32/214432/1' of persistent-https://android.git.corp.google.com/platform/external/smali into incr-dex

- Merges in upstream smali into nyc-dev for dex version 37 support.

Bug: 27809626

Change-Id: Iff12b1e384355bcb261b55afd16af3bcac6ef1a0
diff --git a/.gitignore b/.gitignore
index 45e097e..b7db779 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,6 +5,7 @@
 /dexlib2/accessorTestGenerator/build
 /smali/build
 /util/build
+/smalidea/build
 *.iml
 *.ipr
 *.iws
diff --git a/NOTICE b/NOTICE
index 4ce4514..e29e98b 100644
--- a/NOTICE
+++ b/NOTICE
@@ -82,4 +82,24 @@
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
+*******************************************************************************
+
+
+Some parts of the smalidea plugin are based on code from the IDEA project, per the
+following license
+
+*******************************************************************************
+Copyright 2000-2014 JetBrains s.r.o.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
 *******************************************************************************
\ No newline at end of file
diff --git a/README.md b/README.md
index 5784899..ccae135 100644
--- a/README.md
+++ b/README.md
@@ -4,6 +4,8 @@
 
 Downloads are at  https://bitbucket.org/JesusFreke/smali/downloads. If you are interested in submitting a patch, feel free to send me a pull request here.
 
+See [the wiki](https://github.com/JesusFreke/smali/wiki) for more info/news/release notes/etc.
+
 #### Support
 - [github Issue tracker](https://github.com/JesusFreke/smali/issues) - For any bugs/issues/feature requests
 - [#smali on freenode](http://webchat.freenode.net/?channels=smali) - Free free to drop by and ask a question. Don't expect an instant response, but if you hang around someone will respond.
diff --git a/baksmali/build.gradle b/baksmali/build.gradle
index 4780cd7..f3a14b1 100644
--- a/baksmali/build.gradle
+++ b/baksmali/build.gradle
@@ -51,16 +51,13 @@
 processResources.inputs.property('version', version)
 processResources.expand('version': version)
 
-// This is the jar that gets uploaded to maven
-jar {
-    baseName = 'maven'
-}
-
 // Build a separate jar that contains all dependencies
 task fatJar(type: Jar) {
     from sourceSets.main.output
     from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
 
+    classifier = 'fat'
+
     manifest {
         attributes('Main-Class': 'org.jf.baksmali.main')
     }
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
index b3f9ae1..fc43d6f 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
@@ -76,8 +76,7 @@
             return false;
         }
 
-        return opcode.isOdexedInstanceVolatile() || opcode.isOdexedStaticVolatile() ||
-            opcode == Opcode.THROW_VERIFICATION_ERROR;
+        return opcode.isVolatileFieldAccessor() || opcode == Opcode.THROW_VERIFICATION_ERROR;
     }
 
     @Override
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
index 4081a75..ef2110a 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
@@ -320,7 +320,7 @@
             String parameterType = parameter.getType();
             String parameterName = parameter.getName();
             Collection<? extends Annotation> annotations = parameter.getAnnotations();
-            if (parameterName != null || annotations.size() != 0) {
+            if ((options.outputDebugInfo && parameterName != null) || annotations.size() != 0) {
                 writer.write(".param p");
                 writer.printSignedIntAsDec(registerNumber);
 
@@ -366,7 +366,8 @@
     private List<MethodItem> getMethodItems() {
         ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
 
-        if ((classDef.options.registerInfo != 0) || (classDef.options.deodex && needsAnalyzed())) {
+        if ((classDef.options.registerInfo != 0) || (classDef.options.normalizeVirtualMethods) ||
+                (classDef.options.deodex && needsAnalyzed())) {
             addAnalyzedInstructionMethodItems(methodItems);
         } else {
             addInstructionMethodItems(methodItems);
@@ -460,7 +461,7 @@
 
     private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) {
         MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classDef.options.classPath, method,
-                classDef.options.inlineResolver);
+                classDef.options.inlineResolver, classDef.options.normalizeVirtualMethods);
 
         AnalysisException analysisException = methodAnalyzer.getAnalysisException();
         if (analysisException != null) {
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java
index fa54f09..f532938 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java
@@ -156,7 +156,8 @@
             RegisterType mergedRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNum);
 
             for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
-                RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
+                RegisterType predecessorRegisterType = analyzedInstruction.getPredecessorRegisterType(
+                        predecessor, registerNum);
                 if (predecessorRegisterType.category != RegisterType.UNKNOWN &&
                         !predecessorRegisterType.equals(mergedRegisterType)) {
                     registers.set(registerNum);
@@ -179,7 +180,8 @@
         boolean first = true;
 
         for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
-            RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
+            RegisterType predecessorRegisterType = analyzedInstruction.getPredecessorRegisterType(
+                    predecessor, registerNum);
 
             if (!first) {
                 writer.write(',');
diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java
index 47fa406..5060734 100644
--- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java
+++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java
@@ -44,19 +44,18 @@
 import org.xml.sax.SAXException;
 import org.xml.sax.helpers.DefaultHandler;
 
+import javax.xml.parsers.ParserConfigurationException;
+import javax.xml.parsers.SAXParser;
+import javax.xml.parsers.SAXParserFactory;
 import java.io.*;
 import java.util.List;
 import java.util.Map.Entry;
 import java.util.concurrent.*;
 
-import javax.xml.parsers.SAXParser;
-import javax.xml.parsers.SAXParserFactory;
-import javax.xml.parsers.ParserConfigurationException;
-
 public class baksmali {
 
     public static boolean disassembleDexFile(DexFile dexFile, final baksmaliOptions options) {
-        if (options.registerInfo != 0 || options.deodex) {
+        if (options.registerInfo != 0 || options.deodex || options.normalizeVirtualMethods) {
             try {
                 Iterable<String> extraClassPathEntries;
                 if (options.extraClassPathEntries != null) {
@@ -136,7 +135,7 @@
         List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
 
         if (!options.noAccessorComments) {
-            options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs);
+            options.syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile.getOpcodes(), classDefs);
         }
 
         final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java b/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java
index b6cc157..32685dd 100644
--- a/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java
+++ b/baksmali/src/main/java/org/jf/baksmali/baksmaliOptions.java
@@ -36,6 +36,7 @@
 import org.jf.dexlib2.analysis.InlineMethodResolver;
 import org.jf.dexlib2.util.SyntheticAccessorResolver;
 
+import javax.annotation.Nullable;
 import java.io.File;
 import java.util.Arrays;
 import java.util.HashMap;
@@ -54,7 +55,7 @@
 
     public int apiLevel = 15;
     public String outputDirectory = "out";
-    public String dexEntry = "classes.dex";
+    @Nullable public String dexEntry = null;
     public List<String> bootClassPathDirs = Lists.newArrayList();
 
     public List<String> bootClassPathEntries = Lists.newArrayList();
@@ -75,11 +76,15 @@
     public boolean ignoreErrors = false;
     public boolean checkPackagePrivateAccess = false;
     public boolean useImplicitReferences = false;
+    public boolean normalizeVirtualMethods = false;
     public File customInlineDefinitions = null;
     public InlineMethodResolver inlineResolver = null;
     public int registerInfo = 0;
     public ClassPath classPath = null;
-    public int jobs = -1;
+    public int jobs = Runtime.getRuntime().availableProcessors();
+    public boolean disassemble = true;
+    public boolean dump = false;
+    public String dumpFileName = null;
 
     public SyntheticAccessorResolver syntheticAccessorResolver = null;
 
diff --git a/baksmali/src/main/java/org/jf/baksmali/dump.java b/baksmali/src/main/java/org/jf/baksmali/dump.java
index 1ef7df0..79405e5 100644
--- a/baksmali/src/main/java/org/jf/baksmali/dump.java
+++ b/baksmali/src/main/java/org/jf/baksmali/dump.java
@@ -40,7 +40,7 @@
 import java.io.Writer;
 
 public class dump {
-    public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel, boolean experimental) throws IOException {
+    public static void dump(DexBackedDexFile dexFile, String dumpFileName, int apiLevel) throws IOException {
         if (dumpFileName != null) {
             Writer writer = null;
 
@@ -52,7 +52,7 @@
                     consoleWidth = 120;
                 }
 
-                RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel, experimental), dexFile);
+                RawDexFile rawDexFile = new RawDexFile(Opcodes.forApi(apiLevel), dexFile);
                 DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
                 annotator.writeAnnotations(writer);
             } catch (IOException ex) {
diff --git a/baksmali/src/main/java/org/jf/baksmali/main.java b/baksmali/src/main/java/org/jf/baksmali/main.java
index 71598fa..2d6ed8c 100644
--- a/baksmali/src/main/java/org/jf/baksmali/main.java
+++ b/baksmali/src/main/java/org/jf/baksmali/main.java
@@ -31,9 +31,12 @@
 import com.google.common.collect.Lists;
 import org.apache.commons.cli.*;
 import org.jf.dexlib2.DexFileFactory;
+import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
 import org.jf.dexlib2.analysis.InlineMethodResolver;
 import org.jf.dexlib2.dexbacked.DexBackedDexFile;
 import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
+import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
+import org.jf.dexlib2.iface.DexFile;
 import org.jf.util.ConsoleUtil;
 import org.jf.util.SmaliHelpFormatter;
 
@@ -82,6 +85,45 @@
     }
 
     /**
+     * A more programmatic-friendly entry point for baksmali
+     *
+     * @param options a baksmaliOptions object with the options to run baksmali with
+     * @param inputDexFile The DexFile to disassemble
+     * @return true if disassembly completed with no errors, or false if errors were encountered
+     */
+    public static boolean run(@Nonnull baksmaliOptions options, @Nonnull DexFile inputDexFile) throws IOException {
+        if (options.bootClassPathEntries.isEmpty() &&
+                (options.deodex || options.registerInfo != 0 || options.normalizeVirtualMethods)) {
+            if (inputDexFile instanceof DexBackedOdexFile) {
+                options.bootClassPathEntries = ((DexBackedOdexFile)inputDexFile).getDependencies();
+            } else {
+                options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
+                        options.experimental);
+            }
+        }
+
+        if (options.customInlineDefinitions == null && inputDexFile instanceof DexBackedOdexFile) {
+            options.inlineResolver =
+                    InlineMethodResolver.createInlineMethodResolver(
+                            ((DexBackedOdexFile)inputDexFile).getOdexVersion());
+        }
+
+        boolean errorOccurred = false;
+        if (options.disassemble) {
+            errorOccurred = !baksmali.disassembleDexFile(inputDexFile, options);
+        }
+
+        if (options.dump) {
+            if (!(inputDexFile instanceof DexBackedDexFile)) {
+                throw new IllegalArgumentException("Annotated hex-dumps require a DexBackedDexFile");
+            }
+            dump.dump((DexBackedDexFile)inputDexFile, options.dumpFileName, options.apiLevel);
+        }
+
+        return !errorOccurred;
+    }
+
+    /**
      * Run!
      */
     public static void main(String[] args) throws IOException {
@@ -100,11 +142,6 @@
 
         baksmaliOptions options = new baksmaliOptions();
 
-        boolean disassemble = true;
-        boolean doDump = false;
-        String dumpFileName = null;
-        boolean setBootClassPath = false;
-
         String[] remainingArgs = commandLine.getArgs();
         Option[] clOptions = commandLine.getOptions();
 
@@ -185,7 +222,6 @@
                     if (bcp != null && bcp.charAt(0) == ':') {
                         options.addExtraClassPath(bcp);
                     } else {
-                        setBootClassPath = true;
                         options.setBootClassPath(bcp);
                     }
                     break;
@@ -217,12 +253,15 @@
                 case 'k':
                     options.checkPackagePrivateAccess = true;
                     break;
+                case 'n':
+                    options.normalizeVirtualMethods = true;
+                    break;
                 case 'N':
-                    disassemble = false;
+                    options.disassemble = false;
                     break;
                 case 'D':
-                    doDump = true;
-                    dumpFileName = commandLine.getOptionValue("D");
+                    options.dump = true;
+                    options.dumpFileName = commandLine.getOptionValue("D");
                     break;
                 case 'I':
                     options.ignoreErrors = true;
@@ -240,26 +279,29 @@
             return;
         }
 
-        if (options.jobs <= 0) {
-            options.jobs = Runtime.getRuntime().availableProcessors();
-            if (options.jobs > 6) {
-                options.jobs = 6;
-            }
-        }
-
-        String inputDexFileName = remainingArgs[0];
-
-        File dexFileFile = new File(inputDexFileName);
+        String inputDexPath = remainingArgs[0];
+        File dexFileFile = new File(inputDexPath);
         if (!dexFileFile.exists()) {
-            System.err.println("Can't find the file " + inputDexFileName);
+            System.err.println("Can't find the file " + inputDexPath);
             System.exit(1);
         }
 
         //Read in and parse the dex file
-        DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry,
-                options.apiLevel, options.experimental);
+        DexBackedDexFile dexFile = null;
+        try {
+            dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, options.apiLevel, options.experimental);
+        } catch (MultipleDexFilesException ex) {
+            System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
+                    "disassemble with the -e option", dexFileFile.getName()));
+            System.err.println("Valid entries include:");
 
-        if (dexFile.isOdexFile()) {
+            for (OatDexFile oatDexFile: ex.oatFile.getDexFiles()) {
+                System.err.println(oatDexFile.filename);
+            }
+            System.exit(1);
+        }
+
+        if (dexFile.hasOdexOpcodes()) {
             if (!options.deodex) {
                 System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
                 System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
@@ -270,34 +312,18 @@
             options.deodex = false;
         }
 
-        if (!setBootClassPath && (options.deodex || options.registerInfo != 0)) {
-            if (dexFile instanceof DexBackedOdexFile) {
-                options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
-            } else {
-                options.bootClassPathEntries = getDefaultBootClassPathForApi(options.apiLevel,
-                        options.experimental);
+        if (options.dump) {
+            if (options.dumpFileName == null) {
+                options.dumpFileName =  inputDexPath + ".dump";
             }
         }
 
-        if (options.customInlineDefinitions == null && dexFile instanceof DexBackedOdexFile) {
-            options.inlineResolver =
-                    InlineMethodResolver.createInlineMethodResolver(
-                            ((DexBackedOdexFile)dexFile).getOdexVersion());
-        }
-
-        boolean errorOccurred = false;
-        if (disassemble) {
-            errorOccurred = !baksmali.disassembleDexFile(dexFile, options);
-        }
-
-        if (doDump) {
-            if (dumpFileName == null) {
-                dumpFileName = commandLine.getOptionValue(inputDexFileName + ".dump");
+        try {
+            if (!run(options, dexFile)) {
+                System.exit(1);
             }
-            dump.dump(dexFile, dumpFileName, options.apiLevel, options.experimental);
-        }
-
-        if (errorOccurred) {
+        } catch (IllegalArgumentException ex) {
+            System.err.println(ex.getMessage());
             System.exit(1);
         }
     }
@@ -391,9 +417,9 @@
                 .create("r");
 
         Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
-                .withDescription("the bootclasspath jars to use, for analysis. Defaults to " +
-                        "core.jar:ext.jar:framework.jar:android.policy.jar:services.jar. If the value begins with a " +
-                        ":, it will be appended to the default bootclasspath instead of replacing it")
+                .withDescription("A colon-separated list of bootclasspath jar/oat files to use for analysis. Add an " +
+                        "initial colon to specify that the jars/oats should be appended to the default bootclasspath " +
+                        "instead of replacing it")
                 .hasOptionalArg()
                 .withArgName("BOOTCLASSPATH")
                 .create("c");
@@ -445,6 +471,10 @@
                         "4.2.1.")
                 .create("k");
 
+        Option normalizeVirtualMethods = OptionBuilder.withLongOpt("normalize-virtual-methods")
+                .withDescription("Normalize virtual method references to the reference the base method.")
+                .create("n");
+
         Option dumpOption = OptionBuilder.withLongOpt("dump-to")
                 .withDescription("dumps the given dex file into a single annotated dump file named FILE" +
                         " (<dexfile>.dump by default), along with the normal disassembly")
@@ -494,6 +524,7 @@
         basicOptions.addOption(noImplicitReferencesOption);
         basicOptions.addOption(dexEntryOption);
         basicOptions.addOption(checkPackagePrivateAccessOption);
+        basicOptions.addOption(normalizeVirtualMethods);
 
         debugOptions.addOption(dumpOption);
         debugOptions.addOption(ignoreErrorsOption);
@@ -547,8 +578,7 @@
                     "/system/framework/services.jar",
                     "/system/framework/apache-xml.jar",
                     "/system/framework/filterfw.jar");
-
-        } else {
+        } else if (apiLevel < 21) {
             // this is correct as of api 17/4.2.2
             return Lists.newArrayList(
                     "/system/framework/core.jar",
@@ -561,6 +591,22 @@
                     "/system/framework/android.policy.jar",
                     "/system/framework/services.jar",
                     "/system/framework/apache-xml.jar");
+        } else { // api >= 21
+            // TODO: verify, add new ones?
+            return Lists.newArrayList(
+                    "/system/framework/core-libart.jar",
+                    "/system/framework/conscrypt.jar",
+                    "/system/framework/okhttp.jar",
+                    "/system/framework/core-junit.jar",
+                    "/system/framework/bouncycastle.jar",
+                    "/system/framework/ext.jar",
+                    "/system/framework/framework.jar",
+                    "/system/framework/telephony-common.jar",
+                    "/system/framework/voip-common.jar",
+                    "/system/framework/ims-common.jar",
+                    "/system/framework/mms-common.jar",
+                    "/system/framework/android.policy.jar",
+                    "/system/framework/apache-xml.jar");
         }
     }
 }
diff --git a/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java b/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java
index 725032a..2bb04dd 100644
--- a/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java
+++ b/baksmali/src/test/java/org/jf/baksmali/AnalysisTest.java
@@ -106,8 +106,8 @@
                     className.substring(1, className.length() - 1));
             String smaliContents = readResource(smaliPath);
 
-            Assert.assertEquals(TextUtils.normalizeNewlines(smaliContents),
-                    TextUtils.normalizeNewlines(stringWriter.toString()));
+            Assert.assertEquals(TextUtils.normalizeWhitespace(smaliContents),
+                    TextUtils.normalizeWhitespace((stringWriter.toString())));
         }
     }
 
diff --git a/baksmali/src/test/java/org/jf/baksmali/DexTest.java b/baksmali/src/test/java/org/jf/baksmali/DexTest.java
new file mode 100644
index 0000000..5a4db65
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/DexTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.dexbacked.DexBackedDexFile;
+import org.junit.Assert;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A base test class for performing a test using a dex file as input
+ */
+/**
+ * A base test class for performing a disassembly on a dex file and verifying the results
+ *
+ * The test accepts a single-class dex file as input. By default, the input dex file should be a resource at
+ * [testDir]/[testName]Input.dex
+ */
+public abstract class DexTest {
+    protected final String testDir;
+
+    protected DexTest(@Nonnull String testDir) {
+        this.testDir = testDir;
+    }
+
+    protected DexTest() {
+        this.testDir = this.getClass().getSimpleName();
+    }
+
+    @Nonnull
+    protected String getInputFilename(@Nonnull String testName) {
+        return String.format("%s%s%sInput.dex", testDir, File.separatorChar, testName);
+    }
+
+    @Nonnull
+    protected DexBackedDexFile getInputDexFile(@Nonnull String testName, @Nonnull baksmaliOptions options) {
+        try {
+            // Load file from resources as a stream
+            byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
+            return new DexBackedDexFile(Opcodes.forApi(options.apiLevel), inputBytes);
+        } catch (IOException ex) {
+            Assert.fail();
+        }
+        return null;
+    }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
index 35304f7..1a34e8c 100644
--- a/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
+++ b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
@@ -32,7 +32,6 @@
 package org.jf.baksmali;
 
 import com.google.common.collect.Iterables;
-import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.dexbacked.DexBackedDexFile;
 import org.jf.dexlib2.iface.ClassDef;
 import org.junit.Assert;
@@ -50,21 +49,7 @@
  * By default, the input and output files should be resources at [testDir]/[testName]Input.dex
  * and [testDir]/[testName]Output.smali respectively
  */
-public class DisassemblyTest {
-    protected final String testDir;
-
-    protected DisassemblyTest(@Nonnull String testDir) {
-        this.testDir = testDir;
-    }
-
-    protected DisassemblyTest() {
-        this.testDir = this.getClass().getSimpleName();
-    }
-
-    @Nonnull
-    protected String getInputFilename(@Nonnull String testName) {
-        return String.format("%s%s%sInput.dex", testDir, File.separatorChar, testName);
-    }
+public class DisassemblyTest extends DexTest {
 
     @Nonnull
     protected String getOutputFilename(@Nonnull String testName) {
@@ -77,22 +62,13 @@
 
     protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options) {
         try {
-            // Load file from resources as a stream
-            String inputFilename = getInputFilename(testName);
-            byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
-
-            DexBackedDexFile inputDex = new DexBackedDexFile(new Opcodes(options.apiLevel, false), inputBytes);
+            DexBackedDexFile inputDex = getInputDexFile(testName, options);
             Assert.assertEquals(1, inputDex.getClassCount());
             ClassDef inputClass = Iterables.getFirst(inputDex.getClasses(), null);
             Assert.assertNotNull(inputClass);
             String input = BaksmaliTestUtils.getNormalizedSmali(inputClass, options, true);
 
-            String output;
-            if (getOutputFilename(testName).equals(inputFilename)) {
-                output = input;
-            } else {
-                output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName));
-            }
+            String output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName));
             output = BaksmaliTestUtils.normalizeSmali(output, true);
 
             // Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
diff --git a/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java b/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java
new file mode 100644
index 0000000..78fabc0
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import com.google.common.collect.Lists;
+import org.jf.dexlib2.analysis.ClassPath;
+import org.jf.dexlib2.analysis.ClassProto;
+import org.jf.dexlib2.analysis.DexClassProvider;
+import org.jf.dexlib2.iface.DexFile;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FieldGapOrderTest extends DexTest {
+    @Test
+    public void testOldOrder() {
+        DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions());
+        Assert.assertEquals(3, dexFile.getClasses().size());
+
+        ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), false, 66);
+        ClassProto classProto = (ClassProto)classPath.getClass("LGapOrder;");
+        Assert.assertEquals("r1", classProto.getFieldByOffset(12).getName());
+        Assert.assertEquals("r2", classProto.getFieldByOffset(16).getName());
+        Assert.assertEquals("d", classProto.getFieldByOffset(24).getName());
+        Assert.assertEquals("s", classProto.getFieldByOffset(36).getName());
+        Assert.assertEquals("i", classProto.getFieldByOffset(32).getName());
+    }
+
+    @Test
+    public void testNewOrder() {
+        DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions());
+        Assert.assertEquals(3, dexFile.getClasses().size());
+
+        ClassPath classPath = new ClassPath(Lists.newArrayList(new DexClassProvider(dexFile)), false, 67);
+        ClassProto classProto = (ClassProto)classPath.getClass("LGapOrder;");
+        Assert.assertEquals("s", classProto.getFieldByOffset(10).getName());
+        Assert.assertEquals("r1", classProto.getFieldByOffset(12).getName());
+        Assert.assertEquals("r2", classProto.getFieldByOffset(16).getName());
+        Assert.assertEquals("i", classProto.getFieldByOffset(20).getName());
+        Assert.assertEquals("d", classProto.getFieldByOffset(24).getName());
+    }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/ParamListMethodNameTest.java b/baksmali/src/test/java/org/jf/baksmali/ParamListMethodNameTest.java
new file mode 100644
index 0000000..42f7239
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/ParamListMethodNameTest.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.junit.Test;
+
+public class ParamListMethodNameTest extends IdenticalRoundtripTest {
+
+    @Test
+    public void testParamListMethodName() {
+        runTest("ParamListMethodName");
+    }
+}
diff --git a/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex b/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex
new file mode 100644
index 0000000..4e59351
--- /dev/null
+++ b/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex
Binary files differ
diff --git a/baksmali/src/test/resources/ParamListMethodNameTest/ParamListMethodName.smali b/baksmali/src/test/resources/ParamListMethodNameTest/ParamListMethodName.smali
new file mode 100644
index 0000000..8571715
--- /dev/null
+++ b/baksmali/src/test/resources/ParamListMethodNameTest/ParamListMethodName.smali
@@ -0,0 +1,5 @@
+.class Lblah;
+.super Ljava/lang/Object;
+
+.method public abstract II()V
+.end method
\ No newline at end of file
diff --git a/build.gradle b/build.gradle
index edf156f..80ac25f 100644
--- a/build.gradle
+++ b/build.gradle
@@ -31,9 +31,7 @@
 
 apply plugin: 'idea'
 
-version = '2.0.8'
-
-def jarVersion = version
+version = '2.1.2'
 
 if (!('release' in gradle.startParameter.taskNames)) {
     def versionSuffix
@@ -51,12 +49,7 @@
         versionSuffix = 'dev'
     }
 
-    def baseVersion = version
-    version = baseVersion + '-' + versionSuffix
-
-    // use something like module-1.2.3-dev.jar for the jar name, rather than the full
-    // module-1.2.3-001afe02-dirty.jar
-    jarVersion = baseVersion + '-dev'
+    version += "-${versionSuffix}"
 } else {
     if (System.env.JDK6_HOME == null && !JavaVersion.current().isJava6()) {
         throw new InvalidUserDataException("bzzzzzzzt. Release builds must be performed with java 6. " +
@@ -69,6 +62,10 @@
 task release() {
 }
 
+task(install) << {
+    println "Installing version: ${version}"
+}
+
 // The projects that get pushed to maven
 def maven_release_projects = ['smali', 'baksmali', 'dexlib2', 'util']
 
@@ -100,24 +97,22 @@
     version = parent.version
 
     ext {
-        depends = [guava: 'com.google.guava:guava:18.0',
-                   findbugs: 'com.google.code.findbugs:jsr305:1.3.9',
-                   junit: 'junit:junit:4.6',
-                   antlr_runtime: 'org.antlr:antlr-runtime:3.5.2',
-                   antlr: 'org.antlr:antlr:3.5.2',
-                   stringtemplate: 'org.antlr:stringtemplate:3.2.1',
-                   commons_cli: 'commons-cli:commons-cli:1.2',
-                   jflex: 'de.jflex:jflex:1.4.3',
-                   jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2',
-                   proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
-                   dx: 'com.google.android.tools:dx:1.7'
+        depends = [
+                guava: 'com.google.guava:guava:18.0',
+                findbugs: 'com.google.code.findbugs:jsr305:1.3.9',
+                junit: 'junit:junit:4.6',
+                antlr_runtime: 'org.antlr:antlr-runtime:3.5.2',
+                antlr: 'org.antlr:antlr:3.5.2',
+                stringtemplate: 'org.antlr:stringtemplate:3.2.1',
+                commons_cli: 'commons-cli:commons-cli:1.2',
+                jflex: 'de.jflex:jflex:1.4.3',
+                jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2',
+                proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
+                dx: 'com.google.android.tools:dx:1.7',
+                gson: 'com.google.code.gson:gson:2.3.1'
          ]
     }
 
-    jar {
-        version = jarVersion
-    }
-
     repositories {
         mavenCentral()
     }
@@ -202,5 +197,5 @@
 }
 
 task wrapper(type: Wrapper) {
-    gradleVersion = '2.3'
+    gradleVersion = '2.11'
 }
\ No newline at end of file
diff --git a/dexlib2/OatVersions.txt b/dexlib2/OatVersions.txt
new file mode 100644
index 0000000..8aa9ea9
--- /dev/null
+++ b/dexlib2/OatVersions.txt
@@ -0,0 +1,24 @@
+7642cfc90fc9c3ebfd8e3b5041915705c93b5cf0 - 56
+ - first version with all stability fixes needed for deodexing
+14691c5e786e8c2c5734f687e4c96217340771be - 57
+1558b577907b613864e98f05862543557263e864 - 58
+f3251d12dfa387493dbde4c4148a633802f5f7e3 - 59
+706cae36209932f258b2fe2e396f31d2dd7d585e - 58 (revert of f3251d12)
+d7cbf8a6629942e7bd315ffae7e1c77b082f3e11 - 60
+ - return-void-barrier -> return-void-no-barrier
+1412dfa4adcd511902e510fa0c948b168ab5840c - 61 (re-commit of f3251d12)
+9d6bf69ad3012a9d843268fdd5325b6719b6d5f2 - 62
+0de1133ba600f299b3d67938f650720d9f859eb2 - 63
+07785bb98dc8bbe192970e0f4c2cafd338a8dc68 - 64
+fa2c054b28d4b540c1b3651401a7a091282a015f - 65
+7070ccd8b6439477eafeea7ed3736645d78e003f - 64 (revert of fa2c054b)
+7bf2b4f1d08050f80782217febac55c8cfc5e4ef - 65 (re-commit of fa2c054b)
+0b71357fb52be9bb06d35396a3042b4381b01041 - 66
+fab6788358dfb64e5c370611ddbbbffab0ed0553 - 67
+- Change in FieldGap priority queue ordering
+1aee900d5a0b3a8d78725a7551356bda0d8554e1 - 68
+54b62480636ae846d705fc180c7bd6cd08ec1e42 - 69
+6e2d5747d00697a25251d25dd33b953e54709507 - 68 (revert of 54b62480)
+0747466fca310eedea5fc49e37d54f240a0b3c0f - 69 (re-commit of 54b62480)
+501fd635a557645ab05f893c56e1f358e21bab82 - 70
+99170c636dfae4908b102347cfe9f92bad1881cc - 71
\ No newline at end of file
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java
index 113b60a..60488ba 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java
@@ -31,51 +31,56 @@
 
 package org.jf.dexlib2;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.io.ByteStreams;
 import org.jf.dexlib2.dexbacked.DexBackedDexFile;
 import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
+import org.jf.dexlib2.dexbacked.OatFile;
+import org.jf.dexlib2.dexbacked.OatFile.NotAnOatFileException;
+import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
 import org.jf.dexlib2.iface.DexFile;
 import org.jf.dexlib2.writer.pool.DexPool;
 import org.jf.util.ExceptionWithContext;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import java.io.*;
+import java.util.List;
 import java.util.zip.ZipEntry;
 import java.util.zip.ZipFile;
 
 public final class DexFileFactory {
     @Nonnull
-    public static DexBackedDexFile loadDexFile(String path, int api)
-            throws IOException {
+    public static DexBackedDexFile loadDexFile(@Nonnull String path, int api) throws IOException {
         return loadDexFile(path, api, false);
     }
 
     @Nonnull
-    public static DexBackedDexFile loadDexFile(String path, int api, boolean experimental)
+    public static DexBackedDexFile loadDexFile(@Nonnull String path, int api, boolean experimental)
             throws IOException {
-        return loadDexFile(new File(path), "classes.dex", new Opcodes(api, experimental));
+        return loadDexFile(new File(path), "classes.dex", Opcodes.forApi(api, experimental));
     }
 
     @Nonnull
-    public static DexBackedDexFile loadDexFile(File dexFile, int api) throws IOException {
+    public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, int api) throws IOException {
         return loadDexFile(dexFile, api, false);
     }
 
     @Nonnull
-    public static DexBackedDexFile loadDexFile(File dexFile, int api, boolean experimental)
+    public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, int api, boolean experimental)
             throws IOException {
-        return loadDexFile(dexFile, "classes.dex", new Opcodes(api, experimental));
+        return loadDexFile(dexFile, null, Opcodes.forApi(api, experimental));
     }
 
     @Nonnull
-    public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api,
+    public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, @Nullable String dexEntry, int api,
             boolean experimental) throws IOException {
-        return loadDexFile(dexFile, dexEntry, new Opcodes(api, experimental));
+        return loadDexFile(dexFile, dexEntry, Opcodes.forApi(api, experimental));
     }
 
     @Nonnull
-    public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry,
-            @Nonnull Opcodes opcodes) throws IOException {
+    public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, @Nullable String dexEntry,
+                                               @Nonnull Opcodes opcodes) throws IOException {
         ZipFile zipFile = null;
         boolean isZipFile = false;
         try {
@@ -83,16 +88,18 @@
             // if we get here, it's safe to assume we have a zip file
             isZipFile = true;
 
-            ZipEntry zipEntry = zipFile.getEntry(dexEntry);
+            String zipEntryName = MoreObjects.firstNonNull(dexEntry, "classes.dex");
+            ZipEntry zipEntry = zipFile.getEntry(zipEntryName);
             if (zipEntry == null) {
-                throw new NoClassesDexException("zip file %s does not contain a classes.dex file", dexFile.getName());
+                throw new DexFileNotFound("zip file %s does not contain a %s file", dexFile.getName(), zipEntryName);
             }
             long fileLength = zipEntry.getSize();
             if (fileLength < 40) {
-                throw new ExceptionWithContext(
-                        "The " + dexEntry + " file in %s is too small to be a valid dex file", dexFile.getName());
+                throw new ExceptionWithContext("The %s file in %s is too small to be a valid dex file",
+                        zipEntryName, dexFile.getName());
             } else if (fileLength > Integer.MAX_VALUE) {
-                throw new ExceptionWithContext("The " + dexEntry + " file in %s is too large to read in", dexFile.getName());
+                throw new ExceptionWithContext("The %s file in %s is too large to read in",
+                        zipEntryName, dexFile.getName());
             }
             byte[] dexBytes = new byte[(int)fileLength];
             ByteStreams.readFully(zipFile.getInputStream(zipEntry), dexBytes);
@@ -127,30 +134,93 @@
             } catch (DexBackedOdexFile.NotAnOdexFile ex) {
                 // just eat it
             }
+
+            OatFile oatFile = null;
+            try {
+                oatFile = OatFile.fromInputStream(inputStream);
+            } catch (NotAnOatFileException ex) {
+                // just eat it
+            }
+
+            if (oatFile != null) {
+                if (oatFile.isSupportedVersion() == OatFile.UNSUPPORTED) {
+                    throw new UnsupportedOatVersionException(oatFile);
+                }
+
+                List<OatDexFile> oatDexFiles = oatFile.getDexFiles();
+
+                if (oatDexFiles.size() == 0) {
+                    throw new DexFileNotFound("Oat file %s contains no dex files", dexFile.getName());
+                }
+
+                if (dexEntry == null) {
+                    if (oatDexFiles.size() > 1) {
+                        throw new MultipleDexFilesException(oatFile);
+                    }
+                    return oatDexFiles.get(0);
+                } else {
+                    // first check for an exact match
+                    for (OatDexFile oatDexFile : oatFile.getDexFiles()) {
+                        if (oatDexFile.filename.equals(dexEntry)) {
+                            return oatDexFile;
+                        }
+                    }
+
+                    if (!dexEntry.contains("/")) {
+                        for (OatDexFile oatDexFile : oatFile.getDexFiles()) {
+                            File oatEntryFile = new File(oatDexFile.filename);
+                            if (oatEntryFile.getName().equals(dexEntry)) {
+                                return oatDexFile;
+                            }
+                        }
+                    }
+
+                    throw new DexFileNotFound("oat file %s does not contain a dex file named %s",
+                            dexFile.getName(), dexEntry);
+                }
+            }
         } finally {
             inputStream.close();
         }
 
-        throw new ExceptionWithContext("%s is not an apk, dex file or odex file.", dexFile.getPath());
+        throw new ExceptionWithContext("%s is not an apk, dex, odex or oat file.", dexFile.getPath());
     }
 
-    public static void writeDexFile(String path, DexFile dexFile) throws IOException {
+    public static void writeDexFile(@Nonnull String path, @Nonnull DexFile dexFile) throws IOException {
         DexPool.writeTo(path, dexFile);
     }
 
     private DexFileFactory() {}
 
-    public static class NoClassesDexException extends ExceptionWithContext {
-        public NoClassesDexException(Throwable cause) {
+    public static class DexFileNotFound extends ExceptionWithContext {
+        public DexFileNotFound(@Nullable Throwable cause) {
             super(cause);
         }
 
-        public NoClassesDexException(Throwable cause, String message, Object... formatArgs) {
+        public DexFileNotFound(@Nullable Throwable cause, @Nullable String message, Object... formatArgs) {
             super(cause, message, formatArgs);
         }
 
-        public NoClassesDexException(String message, Object... formatArgs) {
+        public DexFileNotFound(@Nullable String message, Object... formatArgs) {
             super(message, formatArgs);
         }
     }
+
+    public static class MultipleDexFilesException extends ExceptionWithContext {
+        @Nonnull public final OatFile oatFile;
+
+        public MultipleDexFilesException(@Nonnull OatFile oatFile) {
+            super("Oat file has multiple dex files.");
+            this.oatFile = oatFile;
+        }
+    }
+
+    public static class UnsupportedOatVersionException extends ExceptionWithContext {
+        @Nonnull public final OatFile oatFile;
+
+        public UnsupportedOatVersionException(@Nonnull OatFile oatFile) {
+            super("Unsupported oat version: %d", oatFile.getOatVersion());
+            this.oatFile = oatFile;
+        }
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java
index 3b082ee..3a64235 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java
@@ -31,271 +31,289 @@
 
 package org.jf.dexlib2;
 
+import com.google.common.collect.ImmutableRangeMap;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Range;
+import com.google.common.collect.RangeMap;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+
 public enum Opcode
 {
-    NOP((short)0x00, "nop", ReferenceType.NONE, Format.Format10x, Opcode.CAN_CONTINUE),
-    MOVE((short)0x01, "move", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_FROM16((short)0x02, "move/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_16((short)0x03, "move/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_WIDE((short)0x04, "move-wide", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MOVE_WIDE_FROM16((short)0x05, "move-wide/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MOVE_WIDE_16((short)0x06, "move-wide/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MOVE_OBJECT((short)0x07, "move-object", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_OBJECT_FROM16((short)0x08, "move-object/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_OBJECT_16((short)0x09, "move-object/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_RESULT((short)0x0a, "move-result", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_RESULT_WIDE((short)0x0b, "move-result-wide", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MOVE_RESULT_OBJECT((short)0x0c, "move-result-object", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MOVE_EXCEPTION((short)0x0d, "move-exception", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    RETURN_VOID((short)0x0e, "return-void", ReferenceType.NONE, Format.Format10x),
-    RETURN((short)0x0f, "return", ReferenceType.NONE, Format.Format11x),
-    RETURN_WIDE((short)0x10, "return-wide", ReferenceType.NONE, Format.Format11x),
-    RETURN_OBJECT((short)0x11, "return-object", ReferenceType.NONE, Format.Format11x),
-    CONST_4((short)0x12, "const/4", ReferenceType.NONE, Format.Format11n, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CONST_16((short)0x13, "const/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CONST((short)0x14, "const", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CONST_HIGH16((short)0x15, "const/high16", ReferenceType.NONE, Format.Format21ih, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CONST_WIDE_16((short)0x16, "const-wide/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    CONST_WIDE_32((short)0x17, "const-wide/32", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    CONST_WIDE((short)0x18, "const-wide", ReferenceType.NONE, Format.Format51l, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    CONST_WIDE_HIGH16((short)0x19, "const-wide/high16", ReferenceType.NONE, Format.Format21lh, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    CONST_STRING((short)0x1a, "const-string", ReferenceType.STRING, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0x1b),
-    CONST_STRING_JUMBO((short)0x1b, "const-string/jumbo", ReferenceType.STRING, Format.Format31c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CONST_CLASS((short)0x1c, "const-class", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MONITOR_ENTER((short)0x1d, "monitor-enter", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    MONITOR_EXIT((short)0x1e, "monitor-exit", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    CHECK_CAST((short)0x1f, "check-cast", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    INSTANCE_OF((short)0x20, "instance-of", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    ARRAY_LENGTH((short)0x21, "array-length", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    NEW_INSTANCE((short)0x22, "new-instance", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    NEW_ARRAY((short)0x23, "new-array", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    FILLED_NEW_ARRAY((short)0x24, "filled-new-array", ReferenceType.TYPE, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    FILLED_NEW_ARRAY_RANGE((short)0x25, "filled-new-array/range", ReferenceType.TYPE, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    FILL_ARRAY_DATA((short)0x26, "fill-array-data", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
-    THROW((short)0x27, "throw", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW),
-    GOTO((short)0x28, "goto", ReferenceType.NONE, Format.Format10t),
-    GOTO_16((short)0x29, "goto/16", ReferenceType.NONE, Format.Format20t),
-    GOTO_32((short)0x2a, "goto/32", ReferenceType.NONE, Format.Format30t),
-    PACKED_SWITCH((short)0x2b, "packed-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
-    SPARSE_SWITCH((short)0x2c, "sparse-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
-    CMPL_FLOAT((short)0x2d, "cmpl-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CMPG_FLOAT((short)0x2e, "cmpg-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CMPL_DOUBLE((short)0x2f, "cmpl-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CMPG_DOUBLE((short)0x30, "cmpg-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    CMP_LONG((short)0x31, "cmp-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IF_EQ((short)0x32, "if-eq", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
-    IF_NE((short)0x33, "if-ne", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
-    IF_LT((short)0x34, "if-lt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
-    IF_GE((short)0x35, "if-ge", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
-    IF_GT((short)0x36, "if-gt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
-    IF_LE((short)0x37, "if-le", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
-    IF_EQZ((short)0x38, "if-eqz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
-    IF_NEZ((short)0x39, "if-nez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
-    IF_LTZ((short)0x3a, "if-ltz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
-    IF_GEZ((short)0x3b, "if-gez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
-    IF_GTZ((short)0x3c, "if-gtz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
-    IF_LEZ((short)0x3d, "if-lez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
-    AGET((short)0x44, "aget", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AGET_WIDE((short)0x45, "aget-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    AGET_OBJECT((short)0x46, "aget-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AGET_BOOLEAN((short)0x47, "aget-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AGET_BYTE((short)0x48, "aget-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AGET_CHAR((short)0x49, "aget-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AGET_SHORT((short)0x4a, "aget-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    APUT((short)0x4b, "aput", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    APUT_WIDE((short)0x4c, "aput-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    APUT_OBJECT((short)0x4d, "aput-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    APUT_BOOLEAN((short)0x4e, "aput-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    APUT_BYTE((short)0x4f, "aput-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    APUT_CHAR((short)0x50, "aput-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    APUT_SHORT((short)0x51, "aput-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IGET((short)0x52, "iget", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IGET_WIDE((short)0x53, "iget-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    IGET_OBJECT((short)0x54, "iget-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IGET_BOOLEAN((short)0x55, "iget-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IGET_BYTE((short)0x56, "iget-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IGET_CHAR((short)0x57, "iget-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IGET_SHORT((short)0x58, "iget-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IPUT((short)0x59, "iput", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_WIDE((short)0x5a, "iput-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_OBJECT((short)0x5b, "iput-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_BOOLEAN((short)0x5c, "iput-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_BYTE((short)0x5d, "iput-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_CHAR((short)0x5e, "iput-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_SHORT((short)0x5f, "iput-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SGET((short)0x60, "sget", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SGET_WIDE((short)0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SGET_OBJECT((short)0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SGET_BOOLEAN((short)0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SGET_BYTE((short)0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SGET_CHAR((short)0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SGET_SHORT((short)0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SPUT((short)0x67, "sput", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SPUT_WIDE((short)0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SPUT_OBJECT((short)0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SPUT_BOOLEAN((short)0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SPUT_BYTE((short)0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SPUT_CHAR((short)0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SPUT_SHORT((short)0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    INVOKE_VIRTUAL((short)0x6e, "invoke-virtual", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_SUPER((short)0x6f, "invoke-super", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_DIRECT((short)0x70, "invoke-direct", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
-    INVOKE_STATIC((short)0x71, "invoke-static", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_INTERFACE((short)0x72, "invoke-interface", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_VIRTUAL_RANGE((short)0x74, "invoke-virtual/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_SUPER_RANGE((short)0x75, "invoke-super/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_DIRECT_RANGE((short)0x76, "invoke-direct/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
-    INVOKE_STATIC_RANGE((short)0x77, "invoke-static/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_INTERFACE_RANGE((short)0x78, "invoke-interface/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    NEG_INT((short)0x7b, "neg-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    NOT_INT((short)0x7c, "not-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    NEG_LONG((short)0x7d, "neg-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    NOT_LONG((short)0x7e, "not-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    NEG_FLOAT((short)0x7f, "neg-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    NEG_DOUBLE((short)0x80, "neg-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    INT_TO_LONG((short)0x81, "int-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    INT_TO_FLOAT((short)0x82, "int-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    INT_TO_DOUBLE((short)0x83, "int-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    LONG_TO_INT((short)0x84, "long-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    LONG_TO_FLOAT((short)0x85, "long-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    LONG_TO_DOUBLE((short)0x86, "long-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    FLOAT_TO_INT((short)0x87, "float-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    FLOAT_TO_LONG((short)0x88, "float-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    FLOAT_TO_DOUBLE((short)0x89, "float-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    DOUBLE_TO_INT((short)0x8a, "double-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    DOUBLE_TO_LONG((short)0x8b, "double-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    DOUBLE_TO_FLOAT((short)0x8c, "double-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    INT_TO_BYTE((short)0x8d, "int-to-byte", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    INT_TO_CHAR((short)0x8e, "int-to-char", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    INT_TO_SHORT((short)0x8f, "int-to-short", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    ADD_INT((short)0x90, "add-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SUB_INT((short)0x91, "sub-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MUL_INT((short)0x92, "mul-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    DIV_INT((short)0x93, "div-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    REM_INT((short)0x94, "rem-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AND_INT((short)0x95, "and-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    OR_INT((short)0x96, "or-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    XOR_INT((short)0x97, "xor-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SHL_INT((short)0x98, "shl-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SHR_INT((short)0x99, "shr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    USHR_INT((short)0x9a, "ushr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    ADD_LONG((short)0x9b, "add-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SUB_LONG((short)0x9c, "sub-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MUL_LONG((short)0x9d, "mul-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    DIV_LONG((short)0x9e, "div-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    REM_LONG((short)0x9f, "rem-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    AND_LONG((short)0xa0, "and-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    OR_LONG((short)0xa1, "or-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    XOR_LONG((short)0xa2, "xor-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SHL_LONG((short)0xa3, "shl-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SHR_LONG((short)0xa4, "shr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    USHR_LONG((short)0xa5, "ushr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    ADD_FLOAT((short)0xa6, "add-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SUB_FLOAT((short)0xa7, "sub-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MUL_FLOAT((short)0xa8, "mul-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    DIV_FLOAT((short)0xa9, "div-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    REM_FLOAT((short)0xaa, "rem-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    ADD_DOUBLE((short)0xab, "add-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SUB_DOUBLE((short)0xac, "sub-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MUL_DOUBLE((short)0xad, "mul-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    DIV_DOUBLE((short)0xae, "div-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    REM_DOUBLE((short)0xaf, "rem-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    ADD_INT_2ADDR((short)0xb0, "add-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SUB_INT_2ADDR((short)0xb1, "sub-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MUL_INT_2ADDR((short)0xb2, "mul-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    DIV_INT_2ADDR((short)0xb3, "div-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    REM_INT_2ADDR((short)0xb4, "rem-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AND_INT_2ADDR((short)0xb5, "and-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    OR_INT_2ADDR((short)0xb6, "or-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    XOR_INT_2ADDR((short)0xb7, "xor-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SHL_INT_2ADDR((short)0xb8, "shl-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SHR_INT_2ADDR((short)0xb9, "shr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    USHR_INT_2ADDR((short)0xba, "ushr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    ADD_LONG_2ADDR((short)0xbb, "add-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SUB_LONG_2ADDR((short)0xbc, "sub-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MUL_LONG_2ADDR((short)0xbd, "mul-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    DIV_LONG_2ADDR((short)0xbe, "div-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    REM_LONG_2ADDR((short)0xbf, "rem-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    AND_LONG_2ADDR((short)0xc0, "and-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    OR_LONG_2ADDR((short)0xc1, "or-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    XOR_LONG_2ADDR((short)0xc2, "xor-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SHL_LONG_2ADDR((short)0xc3, "shl-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SHR_LONG_2ADDR((short)0xc4, "shr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    USHR_LONG_2ADDR((short)0xc5, "ushr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    ADD_FLOAT_2ADDR((short)0xc6, "add-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SUB_FLOAT_2ADDR((short)0xc7, "sub-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MUL_FLOAT_2ADDR((short)0xc8, "mul-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    DIV_FLOAT_2ADDR((short)0xc9, "div-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    REM_FLOAT_2ADDR((short)0xca, "rem-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    ADD_DOUBLE_2ADDR((short)0xcb, "add-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SUB_DOUBLE_2ADDR((short)0xcc, "sub-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    MUL_DOUBLE_2ADDR((short)0xcd, "mul-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    DIV_DOUBLE_2ADDR((short)0xce, "div-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    REM_DOUBLE_2ADDR((short)0xcf, "rem-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    ADD_INT_LIT16((short)0xd0, "add-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    RSUB_INT((short)0xd1, "rsub-int", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MUL_INT_LIT16((short)0xd2, "mul-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    DIV_INT_LIT16((short)0xd3, "div-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    REM_INT_LIT16((short)0xd4, "rem-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AND_INT_LIT16((short)0xd5, "and-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    OR_INT_LIT16((short)0xd6, "or-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    XOR_INT_LIT16((short)0xd7, "xor-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    ADD_INT_LIT8((short)0xd8, "add-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    RSUB_INT_LIT8((short)0xd9, "rsub-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    MUL_INT_LIT8((short)0xda, "mul-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    DIV_INT_LIT8((short)0xdb, "div-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    REM_INT_LIT8((short)0xdc, "rem-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    AND_INT_LIT8((short)0xdd, "and-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    OR_INT_LIT8((short)0xde, "or-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    XOR_INT_LIT8((short)0xdf, "xor-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SHL_INT_LIT8((short)0xe0, "shl-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SHR_INT_LIT8((short)0xe1, "shr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    USHR_INT_LIT8((short)0xe2, "ushr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    NOP(0x00, "nop", ReferenceType.NONE, Format.Format10x, Opcode.CAN_CONTINUE),
+    MOVE(0x01, "move", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_FROM16(0x02, "move/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_16(0x03, "move/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_WIDE(0x04, "move-wide", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MOVE_WIDE_FROM16(0x05, "move-wide/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MOVE_WIDE_16(0x06, "move-wide/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MOVE_OBJECT(0x07, "move-object", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_OBJECT_FROM16(0x08, "move-object/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_OBJECT_16(0x09, "move-object/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_RESULT(0x0a, "move-result", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_RESULT_WIDE(0x0b, "move-result-wide", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MOVE_RESULT_OBJECT(0x0c, "move-result-object", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MOVE_EXCEPTION(0x0d, "move-exception", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    RETURN_VOID(0x0e, "return-void", ReferenceType.NONE, Format.Format10x),
+    RETURN(0x0f, "return", ReferenceType.NONE, Format.Format11x),
+    RETURN_WIDE(0x10, "return-wide", ReferenceType.NONE, Format.Format11x),
+    RETURN_OBJECT(0x11, "return-object", ReferenceType.NONE, Format.Format11x),
+    CONST_4(0x12, "const/4", ReferenceType.NONE, Format.Format11n, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CONST_16(0x13, "const/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CONST(0x14, "const", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CONST_HIGH16(0x15, "const/high16", ReferenceType.NONE, Format.Format21ih, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CONST_WIDE_16(0x16, "const-wide/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    CONST_WIDE_32(0x17, "const-wide/32", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    CONST_WIDE(0x18, "const-wide", ReferenceType.NONE, Format.Format51l, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    CONST_WIDE_HIGH16(0x19, "const-wide/high16", ReferenceType.NONE, Format.Format21lh, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    CONST_STRING(0x1a, "const-string", ReferenceType.STRING, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CONST_STRING_JUMBO(0x1b, "const-string/jumbo", ReferenceType.STRING, Format.Format31c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CONST_CLASS(0x1c, "const-class", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MONITOR_ENTER(0x1d, "monitor-enter", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    MONITOR_EXIT(0x1e, "monitor-exit", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    CHECK_CAST(0x1f, "check-cast", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    INSTANCE_OF(0x20, "instance-of", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    ARRAY_LENGTH(0x21, "array-length", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    NEW_INSTANCE(0x22, "new-instance", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    NEW_ARRAY(0x23, "new-array", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    FILLED_NEW_ARRAY(0x24, "filled-new-array", ReferenceType.TYPE, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    FILLED_NEW_ARRAY_RANGE(0x25, "filled-new-array/range", ReferenceType.TYPE, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    FILL_ARRAY_DATA(0x26, "fill-array-data", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
+    THROW(0x27, "throw", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW),
+    GOTO(0x28, "goto", ReferenceType.NONE, Format.Format10t),
+    GOTO_16(0x29, "goto/16", ReferenceType.NONE, Format.Format20t),
+    GOTO_32(0x2a, "goto/32", ReferenceType.NONE, Format.Format30t),
+    PACKED_SWITCH(0x2b, "packed-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
+    SPARSE_SWITCH(0x2c, "sparse-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
+    CMPL_FLOAT(0x2d, "cmpl-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CMPG_FLOAT(0x2e, "cmpg-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CMPL_DOUBLE(0x2f, "cmpl-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CMPG_DOUBLE(0x30, "cmpg-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    CMP_LONG(0x31, "cmp-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IF_EQ(0x32, "if-eq", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
+    IF_NE(0x33, "if-ne", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
+    IF_LT(0x34, "if-lt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
+    IF_GE(0x35, "if-ge", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
+    IF_GT(0x36, "if-gt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
+    IF_LE(0x37, "if-le", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
+    IF_EQZ(0x38, "if-eqz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
+    IF_NEZ(0x39, "if-nez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
+    IF_LTZ(0x3a, "if-ltz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
+    IF_GEZ(0x3b, "if-gez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
+    IF_GTZ(0x3c, "if-gtz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
+    IF_LEZ(0x3d, "if-lez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
+    AGET(0x44, "aget", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AGET_WIDE(0x45, "aget-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    AGET_OBJECT(0x46, "aget-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AGET_BOOLEAN(0x47, "aget-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AGET_BYTE(0x48, "aget-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AGET_CHAR(0x49, "aget-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AGET_SHORT(0x4a, "aget-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    APUT(0x4b, "aput", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    APUT_WIDE(0x4c, "aput-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    APUT_OBJECT(0x4d, "aput-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    APUT_BOOLEAN(0x4e, "aput-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    APUT_BYTE(0x4f, "aput-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    APUT_CHAR(0x50, "aput-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    APUT_SHORT(0x51, "aput-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IGET(0x52, "iget", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_WIDE(0x53, "iget-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    IGET_OBJECT(0x54, "iget-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_BOOLEAN(0x55, "iget-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_BYTE(0x56, "iget-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_CHAR(0x57, "iget-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_SHORT(0x58, "iget-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IPUT(0x59, "iput", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_WIDE(0x5a, "iput-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_OBJECT(0x5b, "iput-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_BOOLEAN(0x5c, "iput-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_BYTE(0x5d, "iput-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_CHAR(0x5e, "iput-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_SHORT(0x5f, "iput-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    SGET(0x60, "sget", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SGET_WIDE(0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SGET_OBJECT(0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SGET_BOOLEAN(0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SGET_BYTE(0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SGET_CHAR(0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SGET_SHORT(0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT(0x67, "sput", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_WIDE(0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_OBJECT(0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_BOOLEAN(0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_BYTE(0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_CHAR(0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_SHORT(0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    INVOKE_VIRTUAL(0x6e, "invoke-virtual", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_SUPER(0x6f, "invoke-super", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_DIRECT(0x70, "invoke-direct", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
+    INVOKE_STATIC(0x71, "invoke-static", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_INTERFACE(0x72, "invoke-interface", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_VIRTUAL_RANGE(0x74, "invoke-virtual/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_SUPER_RANGE(0x75, "invoke-super/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_DIRECT_RANGE(0x76, "invoke-direct/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
+    INVOKE_STATIC_RANGE(0x77, "invoke-static/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_INTERFACE_RANGE(0x78, "invoke-interface/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    NEG_INT(0x7b, "neg-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    NOT_INT(0x7c, "not-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    NEG_LONG(0x7d, "neg-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    NOT_LONG(0x7e, "not-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    NEG_FLOAT(0x7f, "neg-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    NEG_DOUBLE(0x80, "neg-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    INT_TO_LONG(0x81, "int-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    INT_TO_FLOAT(0x82, "int-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    INT_TO_DOUBLE(0x83, "int-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    LONG_TO_INT(0x84, "long-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    LONG_TO_FLOAT(0x85, "long-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    LONG_TO_DOUBLE(0x86, "long-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    FLOAT_TO_INT(0x87, "float-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    FLOAT_TO_LONG(0x88, "float-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    FLOAT_TO_DOUBLE(0x89, "float-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    DOUBLE_TO_INT(0x8a, "double-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    DOUBLE_TO_LONG(0x8b, "double-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    DOUBLE_TO_FLOAT(0x8c, "double-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    INT_TO_BYTE(0x8d, "int-to-byte", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    INT_TO_CHAR(0x8e, "int-to-char", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    INT_TO_SHORT(0x8f, "int-to-short", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    ADD_INT(0x90, "add-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SUB_INT(0x91, "sub-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MUL_INT(0x92, "mul-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    DIV_INT(0x93, "div-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    REM_INT(0x94, "rem-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AND_INT(0x95, "and-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    OR_INT(0x96, "or-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    XOR_INT(0x97, "xor-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SHL_INT(0x98, "shl-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SHR_INT(0x99, "shr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    USHR_INT(0x9a, "ushr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    ADD_LONG(0x9b, "add-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SUB_LONG(0x9c, "sub-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MUL_LONG(0x9d, "mul-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    DIV_LONG(0x9e, "div-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    REM_LONG(0x9f, "rem-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    AND_LONG(0xa0, "and-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    OR_LONG(0xa1, "or-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    XOR_LONG(0xa2, "xor-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SHL_LONG(0xa3, "shl-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SHR_LONG(0xa4, "shr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    USHR_LONG(0xa5, "ushr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    ADD_FLOAT(0xa6, "add-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SUB_FLOAT(0xa7, "sub-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MUL_FLOAT(0xa8, "mul-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    DIV_FLOAT(0xa9, "div-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    REM_FLOAT(0xaa, "rem-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    ADD_DOUBLE(0xab, "add-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SUB_DOUBLE(0xac, "sub-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MUL_DOUBLE(0xad, "mul-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    DIV_DOUBLE(0xae, "div-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    REM_DOUBLE(0xaf, "rem-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    ADD_INT_2ADDR(0xb0, "add-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SUB_INT_2ADDR(0xb1, "sub-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MUL_INT_2ADDR(0xb2, "mul-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    DIV_INT_2ADDR(0xb3, "div-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    REM_INT_2ADDR(0xb4, "rem-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AND_INT_2ADDR(0xb5, "and-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    OR_INT_2ADDR(0xb6, "or-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    XOR_INT_2ADDR(0xb7, "xor-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SHL_INT_2ADDR(0xb8, "shl-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SHR_INT_2ADDR(0xb9, "shr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    USHR_INT_2ADDR(0xba, "ushr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    ADD_LONG_2ADDR(0xbb, "add-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SUB_LONG_2ADDR(0xbc, "sub-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MUL_LONG_2ADDR(0xbd, "mul-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    DIV_LONG_2ADDR(0xbe, "div-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    REM_LONG_2ADDR(0xbf, "rem-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    AND_LONG_2ADDR(0xc0, "and-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    OR_LONG_2ADDR(0xc1, "or-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    XOR_LONG_2ADDR(0xc2, "xor-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SHL_LONG_2ADDR(0xc3, "shl-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SHR_LONG_2ADDR(0xc4, "shr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    USHR_LONG_2ADDR(0xc5, "ushr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    ADD_FLOAT_2ADDR(0xc6, "add-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SUB_FLOAT_2ADDR(0xc7, "sub-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MUL_FLOAT_2ADDR(0xc8, "mul-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    DIV_FLOAT_2ADDR(0xc9, "div-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    REM_FLOAT_2ADDR(0xca, "rem-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    ADD_DOUBLE_2ADDR(0xcb, "add-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    SUB_DOUBLE_2ADDR(0xcc, "sub-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    MUL_DOUBLE_2ADDR(0xcd, "mul-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    DIV_DOUBLE_2ADDR(0xce, "div-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    REM_DOUBLE_2ADDR(0xcf, "rem-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    ADD_INT_LIT16(0xd0, "add-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    RSUB_INT(0xd1, "rsub-int", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MUL_INT_LIT16(0xd2, "mul-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    DIV_INT_LIT16(0xd3, "div-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    REM_INT_LIT16(0xd4, "rem-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AND_INT_LIT16(0xd5, "and-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    OR_INT_LIT16(0xd6, "or-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    XOR_INT_LIT16(0xd7, "xor-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    ADD_INT_LIT8(0xd8, "add-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    RSUB_INT_LIT8(0xd9, "rsub-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    MUL_INT_LIT8(0xda, "mul-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    DIV_INT_LIT8(0xdb, "div-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    REM_INT_LIT8(0xdc, "rem-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    AND_INT_LIT8(0xdd, "and-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    OR_INT_LIT8(0xde, "or-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    XOR_INT_LIT8(0xdf, "xor-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SHL_INT_LIT8(0xe0, "shl-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    SHR_INT_LIT8(0xe1, "shr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    USHR_INT_LIT8(0xe2, "ushr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
 
-    IGET_VOLATILE((short)0xe3, "iget-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IPUT_VOLATILE((short)0xe4, "iput-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SGET_VOLATILE((short)0xe5, "sget-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SPUT_VOLATILE((short)0xe6, "sput-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IGET_OBJECT_VOLATILE((short)0xe7, "iget-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IGET_WIDE_VOLATILE((short)0xe8, "iget-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    IPUT_WIDE_VOLATILE((short)0xe9, "iput-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SGET_WIDE_VOLATILE((short)0xea, "sget-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    SPUT_WIDE_VOLATILE((short)0xeb, "sput-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IGET_VOLATILE(firstApi(0xe3, 9), "iget-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IPUT_VOLATILE(firstApi(0xe4, 9), "iput-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    SGET_VOLATILE(firstApi(0xe5, 9), "sget-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_VOLATILE(firstApi(0xe6, 9), "sput-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+    IGET_OBJECT_VOLATILE(firstApi(0xe7, 9), "iget-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_WIDE_VOLATILE(firstApi(0xe8, 9), "iget-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    IPUT_WIDE_VOLATILE(firstApi(0xe9, 9), "iput-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    SGET_WIDE_VOLATILE(firstApi(0xea, 9), "sget-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_WIDE_VOLATILE(firstApi(0xeb, 9), "sput-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
 
-    THROW_VERIFICATION_ERROR((short)0xed, "throw-verification-error", minApi(5), ReferenceType.NONE, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW),
-    EXECUTE_INLINE((short)0xee, "execute-inline", ReferenceType.NONE,  Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    EXECUTE_INLINE_RANGE((short)0xef, "execute-inline/range", minApi(8), ReferenceType.NONE,  Format.Format3rmi,  Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_DIRECT_EMPTY((short)0xf0, "invoke-direct-empty", maxApi(13), ReferenceType.METHOD,  Format.Format35c, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
-    INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", minApi(14), ReferenceType.METHOD,  Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
-    RETURN_VOID_BARRIER((short)0xf1, "return-void-barrier", minApi(11), ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
-    IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", maxApi(22), ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
-    IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", maxApi(22), ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    IPUT_QUICK((short)0xf5, "iput-quick", maxApi(22), ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", maxApi(22), ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", maxApi(22), ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", maxApi(22), ReferenceType.NONE,  Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", maxApi(22), ReferenceType.NONE,  Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_SUPER_QUICK((short)0xfa, "invoke-super-quick", ReferenceType.NONE,  Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
-    INVOKE_SUPER_QUICK_RANGE((short)0xfb, "invoke-super-quick/range", ReferenceType.NONE,  Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    THROW_VERIFICATION_ERROR(firstApi(0xed, 5), "throw-verification-error", ReferenceType.NONE, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW),
+    EXECUTE_INLINE(allApis(0xee), "execute-inline", ReferenceType.NONE,  Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    EXECUTE_INLINE_RANGE(firstApi(0xef, 8), "execute-inline/range", ReferenceType.NONE,  Format.Format3rmi,  Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_DIRECT_EMPTY(lastApi(0xf0, 13), "invoke-direct-empty", ReferenceType.METHOD,  Format.Format35c, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
+    INVOKE_OBJECT_INIT_RANGE(firstApi(0xf0, 14), "invoke-object-init/range", ReferenceType.METHOD,  Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
+    RETURN_VOID_BARRIER(combine(firstApi(0xf1, 11), lastArtVersion(0x73, 59)), "return-void-barrier", ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
+    RETURN_VOID_NO_BARRIER(firstArtVersion(0x73, 60), "return-void-no-barrier", ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
+    IGET_QUICK(combine(allApis(0xf2), allArtVersions(0xe3)), "iget-quick", ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_WIDE_QUICK(combine(allApis(0xf3), allArtVersions(0xe4)), "iget-wide-quick", ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+    IGET_OBJECT_QUICK(combine(allApis(0xf4), allArtVersions(0xe5)), "iget-object-quick", ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IPUT_QUICK(combine(allApis(0xf5), allArtVersions(0xe6)), "iput-quick", ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_WIDE_QUICK(combine(allApis(0xf6), allArtVersions(0xe7)), "iput-wide-quick", ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_OBJECT_QUICK(combine(allApis(0xf7), allArtVersions(0xe8)), "iput-object-quick", ReferenceType.NONE,  Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_BOOLEAN_QUICK(allArtVersions(0xeb), "iput-boolean-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR),
+    IPUT_BYTE_QUICK(allArtVersions(0xec), "iput-byte-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR),
+    IPUT_CHAR_QUICK(allArtVersions(0xed), "iput-char-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR),
+    IPUT_SHORT_QUICK(allArtVersions(0xee), "iput-short-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.QUICK_FIELD_ACCESSOR),
+    IGET_BOOLEAN_QUICK(allArtVersions(0xef), "iget-boolean-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_BYTE_QUICK(allArtVersions(0xf0), "iget-byte-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_CHAR_QUICK(allArtVersions(0xf1), "iget-char-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    IGET_SHORT_QUICK(allArtVersions(0xf2), "iget-short-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.QUICK_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+    
+    INVOKE_VIRTUAL_QUICK(combine(allApis(0xf8), allArtVersions(0xe9)), "invoke-virtual-quick", ReferenceType.NONE,  Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_VIRTUAL_QUICK_RANGE(combine(allApis(0xf9), allArtVersions(0xea)), "invoke-virtual-quick/range", ReferenceType.NONE,  Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_SUPER_QUICK(allApis(0xfa), "invoke-super-quick", ReferenceType.NONE,  Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
+    INVOKE_SUPER_QUICK_RANGE(allApis(0xfb), "invoke-super-quick/range", ReferenceType.NONE,  Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
 
-    IPUT_OBJECT_VOLATILE((short)0xfc, "iput-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
-    SGET_OBJECT_VOLATILE((short)0xfd, "sget-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
-    SPUT_OBJECT_VOLATILE((short)0xfe, "sput-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    IPUT_OBJECT_VOLATILE(firstApi(0xfc, 9), "iput-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+    SGET_OBJECT_VOLATILE(firstApi(0xfd, 9), "sget-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+    SPUT_OBJECT_VOLATILE(firstApi(0xfe, 9), "sput-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
 
-    PACKED_SWITCH_PAYLOAD((short)0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
-    SPARSE_SWITCH_PAYLOAD((short)0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
-    ARRAY_PAYLOAD((short)0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0),
+    PACKED_SWITCH_PAYLOAD(0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
+    SPARSE_SWITCH_PAYLOAD(0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
+    ARRAY_PAYLOAD(0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0),
 
     // Reuse the deprecated f3-ff opcodes in Art:
-    INVOKE_LAMBDA((short)0xf3, "invoke-lambda", minApi(23), ReferenceType.NONE, Format.Format25x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.EXPERIMENTAL),
+    INVOKE_LAMBDA(allArtVersions(0xf3),"invoke-lambda", ReferenceType.NONE, Format.Format25x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.EXPERIMENTAL),
     // TODO: What about JUMBO support if the string ID is too large?
-    CAPTURE_VARIABLE((short)0xf5, "capture-variable", minApi(23), ReferenceType.STRING, Format.Format21c, Opcode.EXPERIMENTAL),
-    CREATE_LAMBDA((short)0xf6, "create-lambda", minApi(23), ReferenceType.METHOD, Format.Format21c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
+    CAPTURE_VARIABLE(allArtVersions(0xf5), "capture-variable", ReferenceType.STRING, Format.Format21c, Opcode.EXPERIMENTAL),
+    CREATE_LAMBDA(allArtVersions(0xf6), "create-lambda", ReferenceType.METHOD, Format.Format21c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
     // TODO: do we need a capture/liberate wide?
-    LIBERATE_VARIABLE((short)0xf7, "liberate-variable", minApi(23), ReferenceType.STRING, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
-    BOX_LAMBDA((short)0xf8, "box-lambda", minApi(23), ReferenceType.NONE, Format.Format22x, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
-    UNBOX_LAMBDA((short)0xf9, "unbox-lambda", minApi(23), ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL);
+    LIBERATE_VARIABLE(allArtVersions(0xf7), "liberate-variable", ReferenceType.STRING, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
+    BOX_LAMBDA(allArtVersions(0xf8), "box-lambda", ReferenceType.NONE, Format.Format22x, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
+    UNBOX_LAMBDA(allArtVersions(0xf9), "unbox-lambda", ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL);
 
     //if the instruction can throw an exception
     public static final int CAN_THROW = 0x1;
@@ -309,12 +327,12 @@
     public static final int SETS_REGISTER = 0x10;
     //if the instruction sets the value of it's first register to a wide type
     public static final int SETS_WIDE_REGISTER = 0x20;
-    //if the instruction is an odexed iget-quick/iput-quick instruction
-    public static final int ODEXED_INSTANCE_QUICK = 0x40;
-    //if the instruction is an odexed iget-volatile/iput-volatile instruction
-    public static final int ODEXED_INSTANCE_VOLATILE = 0x80;
-    //if the instruction is an odexed sget-volatile/sput-volatile instruction
-    public static final int ODEXED_STATIC_VOLATILE = 0x100;
+    //if the instruction is an iget-quick/iput-quick instruction
+    public static final int QUICK_FIELD_ACCESSOR = 0x40;
+    //if the instruction is a *get-volatile/*put-volatile instruction
+    public static final int VOLATILE_FIELD_ACCESSOR = 0x80;
+    //if the instruction is a static sget-*/sput-*instruction
+    public static final int STATIC_FIELD_ACCESSOR = 0x100;
     //if the instruction is a jumbo instruction
     public static final int JUMBO_OPCODE = 0x200;
     //if the instruction can initialize an uninitialized object reference
@@ -332,58 +350,81 @@
         return api << 16;
     }
 
-    public final short value;
+    // values and minApis provide a mapping of api -> bytecode value.
+    // the apis in minApis are guaranteed to be
+    public final RangeMap<Integer, Short> apiToValueMap;
+    public final RangeMap<Integer, Short> artVersionToValueMap;
+
     public final String name;
-    // high 16-bits is the max api, low 16-bits is the min api
-    public final int apiConstraints;
     public final int referenceType;
     public final Format format;
     public final int flags;
 
-    Opcode(short opcodeValue, String opcodeName, int referenceType, Format format) {
-        this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, 0, (short)-1);
+    Opcode(int opcodeValue, String opcodeName, int referenceType, Format format) {
+        this(opcodeValue, opcodeName, referenceType, format, 0);
     }
 
-    Opcode(short opcodeValue, String opcodeName, int referenceType, Format format, int flags) {
-        this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, flags, (short)-1);
+    Opcode(int opcodeValue, String opcodeName, int referenceType, Format format, int flags) {
+        this(allVersions(opcodeValue), opcodeName, referenceType, format, flags);
     }
 
-    Opcode(short opcodeValue, String opcodeName, int referenceType, Format format, int flags, short jumboOpcodeValue) {
-        this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, flags, jumboOpcodeValue);
-    }
+    Opcode(List<VersionConstraint> versionConstraints, String opcodeName, int referenceType, Format format, int flags) {
+        ImmutableRangeMap.Builder<Integer, Short> apiToValueBuilder = ImmutableRangeMap.builder();
+        ImmutableRangeMap.Builder<Integer, Short> artVersionToValueBuilder = ImmutableRangeMap.builder();
 
-    Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format) {
-        this(opcodeValue, opcodeName, apiConstraints, referenceType, format, 0, (short)-1);
-    }
+        for (VersionConstraint versionConstraint : versionConstraints) {
+            if (!versionConstraint.apiRange.isEmpty()) {
+                apiToValueBuilder.put(versionConstraint.apiRange, (short)versionConstraint.opcodeValue);
+            }
+            if (!versionConstraint.artVersionRange.isEmpty()) {
+                artVersionToValueBuilder.put(versionConstraint.artVersionRange, (short)versionConstraint.opcodeValue);
+            }
+        }
 
-    Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags) {
-        this(opcodeValue, opcodeName, apiConstraints, referenceType, format, flags, (short)-1);
-    }
-
-    Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags,
-           short jumboOpcodeValue) {
-        this.value = opcodeValue;
+        this.apiToValueMap = apiToValueBuilder.build();
+        this.artVersionToValueMap = artVersionToValueBuilder.build();
         this.name = opcodeName;
-        this.apiConstraints = apiConstraints;
         this.referenceType = referenceType;
         this.format = format;
         this.flags = flags;
-        // TODO: implement jumbo opcodes for dexlib2 and uncomment
-        // this.jumboOpcode = jumboOpcodeValue;
     }
 
-    /**
-     * @return the minimum api level that can use this opcode (inclusive)
-     */
-    public int getMinApi() {
-        return apiConstraints & 0xFFFF;
+    private static List<VersionConstraint> firstApi(int opcodeValue, int api) {
+        return Lists.newArrayList(new VersionConstraint(Range.atLeast(api), Range.openClosed(0, 0), opcodeValue));
     }
 
-    /**
-     * @return the maximum api level that can to use this opcode (inclusive)
-     */
-    public int getMaxApi() {
-        return apiConstraints >>> 16;
+    private static List<VersionConstraint> lastApi(int opcodeValue, int api) {
+        Range range;
+        return Lists.newArrayList(new VersionConstraint(Range.atMost(api), Range.openClosed(0, 0), opcodeValue));
+    }
+
+    private static List<VersionConstraint> firstArtVersion(int opcodeValue, int artVersion) {
+        return Lists.newArrayList(new VersionConstraint(Range.openClosed(0, 0), Range.atLeast(artVersion), opcodeValue));
+    }
+
+    private static List<VersionConstraint> lastArtVersion(int opcodeValue, int artVersion) {
+        return Lists.newArrayList(new VersionConstraint(Range.openClosed(0, 0), Range.atMost(artVersion), opcodeValue));
+    }
+
+    private static List<VersionConstraint> allVersions(int opcodeValue) {
+        return Lists.newArrayList(new VersionConstraint(Range.<Integer>all(), Range.<Integer>all(), opcodeValue));
+    }
+
+    private static List<VersionConstraint> allApis(int opcodeValue) {
+        return Lists.newArrayList(new VersionConstraint(Range.<Integer>all(), Range.openClosed(0, 0), opcodeValue));
+    }
+
+    private static List<VersionConstraint> allArtVersions(int opcodeValue) {
+        return Lists.newArrayList(new VersionConstraint(Range.openClosed(0, 0), Range.<Integer>all(), opcodeValue));
+    }
+
+    @SuppressWarnings("unchecked")
+    private static List<VersionConstraint> combine(List<VersionConstraint>... versionConstraints) {
+        List<VersionConstraint> combinedList = Lists.newArrayList();
+        for (List<VersionConstraint> versionConstraintList: versionConstraints) {
+            combinedList.addAll(versionConstraintList);
+        }
+        return combinedList;
     }
 
     public final boolean canThrow() {
@@ -410,16 +451,16 @@
         return (flags & SETS_WIDE_REGISTER) != 0;
     }
 
-    public final boolean isOdexedInstanceQuick() {
-        return (flags & ODEXED_INSTANCE_QUICK) != 0;
+    public final boolean isQuickFieldaccessor() {
+        return (flags & QUICK_FIELD_ACCESSOR) != 0;
     }
 
-    public final boolean isOdexedInstanceVolatile() {
-        return (flags & ODEXED_INSTANCE_VOLATILE) != 0;
+    public final boolean isVolatileFieldAccessor() {
+        return (flags & VOLATILE_FIELD_ACCESSOR) != 0;
     }
 
-    public final boolean isOdexedStaticVolatile() {
-        return (flags & ODEXED_STATIC_VOLATILE) != 0;
+    public final boolean isStaticFieldAccessor() {
+        return (flags & STATIC_FIELD_ACCESSOR) != 0;
     }
 
     public final boolean isJumboOpcode() {
@@ -433,4 +474,17 @@
     public final boolean isExperimental() {
         return (flags & EXPERIMENTAL) != 0;
     }
+
+    private static class VersionConstraint {
+        @Nonnull public final Range<Integer> apiRange;
+        @Nonnull public final Range<Integer> artVersionRange;
+        public final int opcodeValue;
+
+        public VersionConstraint(@Nonnull Range<Integer> apiRange, @Nonnull Range<Integer> artVersionRange,
+                                 int opcodeValue) {
+            this.apiRange = apiRange;
+            this.artVersionRange = artVersionRange;
+            this.opcodeValue = opcodeValue;
+        }
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java b/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java
index e718e27..17f8013 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java
@@ -32,35 +32,90 @@
 package org.jf.dexlib2;
 
 import com.google.common.collect.Maps;
+import com.google.common.collect.RangeMap;
 
+import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
+import java.util.EnumMap;
 import java.util.HashMap;
 
 public class Opcodes {
-    private final Opcode[] opcodesByValue;
-    private final HashMap<String, Opcode> opcodesByName;
 
+    /**
+     * Either the api level for dalvik opcodes, or the art version for art opcodes
+     */
+    public final int api;
+    public final int artVersion;
+    @Nonnull private final Opcode[] opcodesByValue = new Opcode[255];
+    @Nonnull private final EnumMap<Opcode, Short> opcodeValues;
+    @Nonnull private final HashMap<String, Opcode> opcodesByName;
+
+    @Nonnull
+    public static Opcodes forApi(int api) {
+        return new Opcodes(api, VersionMap.mapApiToArtVersion(api), false);
+    }
+
+    @Nonnull
+    public static Opcodes forApi(int api, boolean experimental) {
+        return new Opcodes(api, VersionMap.mapApiToArtVersion(api), experimental);
+    }
+
+    @Nonnull
+    public static Opcodes forArtVersion(int artVersion) {
+        return forArtVersion(artVersion, false);
+    }
+
+    @Nonnull
+    public static Opcodes forArtVersion(int artVersion, boolean experimental) {
+        return new Opcodes(VersionMap.mapArtVersionToApi(artVersion), artVersion, experimental);
+    }
+
+    @Deprecated
     public Opcodes(int api) {
         this(api, false);
     }
 
+    @Deprecated
     public Opcodes(int api, boolean experimental) {
-        opcodesByValue = new Opcode[256];
+        this(api, VersionMap.mapApiToArtVersion(api), experimental);
+    }
+
+    private Opcodes(int api, int artVersion, boolean experimental) {
+        this.api = api;
+        this.artVersion = artVersion;
+
+        opcodeValues = new EnumMap<Opcode, Short>(Opcode.class);
         opcodesByName = Maps.newHashMap();
 
+        int version;
+        if (isArt()) {
+            version = artVersion;
+        } else {
+            version = api;
+        }
+
         for (Opcode opcode: Opcode.values()) {
-            if (!opcode.format.isPayloadFormat) {
-                if (api <= opcode.getMaxApi() && api >= opcode.getMinApi() &&
-                        (experimental || !opcode.isExperimental())) {
-                    opcodesByValue[opcode.value] = opcode;
-                    opcodesByName.put(opcode.name.toLowerCase(), opcode);
+            RangeMap<Integer, Short> versionToValueMap;
+
+            if (isArt()) {
+                versionToValueMap = opcode.artVersionToValueMap;
+            } else {
+                versionToValueMap = opcode.apiToValueMap;
+            }
+
+            Short opcodeValue = versionToValueMap.get(version);
+            if (opcodeValue != null && (!opcode.isExperimental() || experimental)) {
+                if (!opcode.format.isPayloadFormat) {
+                    opcodesByValue[opcodeValue] = opcode;
                 }
+                opcodeValues.put(opcode, opcodeValue);
+                opcodesByName.put(opcode.name.toLowerCase(), opcode);
             }
         }
     }
 
     @Nullable
-    public Opcode getOpcodeByName(String opcodeName) {
+    public Opcode getOpcodeByName(@Nonnull String opcodeName) {
         return opcodesByName.get(opcodeName.toLowerCase());
     }
 
@@ -80,4 +135,13 @@
                 return null;
         }
     }
+
+    @Nullable
+    public Short getOpcodeValue(@Nonnull Opcode opcode) {
+        return opcodeValues.get(opcode);
+    }
+
+    public boolean isArt() {
+        return artVersion != VersionMap.NO_VERSION;
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java b/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java
new file mode 100644
index 0000000..42802bc
--- /dev/null
+++ b/dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib2;
+
+public class VersionMap {
+    public static final int NO_VERSION = -1;
+
+    public static int mapArtVersionToApi(int artVersion) {
+        // TODO: implement this
+        return 20;
+    }
+
+    public static int mapApiToArtVersion(int api) {
+        // TODO: implement this
+        if (api < 20) {
+            return NO_VERSION;
+        } else {
+            return 56;
+        }
+    }
+}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java
index 30cc906..f6fc95a 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java
@@ -31,12 +31,15 @@
 
 package org.jf.dexlib2.analysis;
 
+import com.google.common.base.Objects;
+import com.google.common.collect.Maps;
 import org.jf.dexlib2.iface.instruction.*;
 import org.jf.dexlib2.iface.reference.MethodReference;
 import org.jf.dexlib2.iface.reference.Reference;
 import org.jf.util.ExceptionWithContext;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
 import java.util.*;
 
 public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
@@ -71,6 +74,12 @@
     protected final RegisterType[] postRegisterMap;
 
     /**
+     * This contains optional register type overrides for register types from predecessors
+     */
+    @Nullable
+    protected Map<PredecessorOverrideKey, RegisterType> predecessorRegisterOverrides = null;
+
+    /**
      * When deodexing, we might need to deodex this instruction multiple times, when we merge in new register
      * information. When this happens, we need to restore the original (odexed) instruction, so we can deodex it again
      */
@@ -101,6 +110,17 @@
         return Collections.unmodifiableSortedSet(predecessors);
     }
 
+    public RegisterType getPredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber) {
+        if (predecessorRegisterOverrides != null) {
+            RegisterType override = predecessorRegisterOverrides.get(
+                    new PredecessorOverrideKey(predecessor, registerNumber));
+            if (override != null) {
+                return override;
+            }
+        }
+        return predecessor.postRegisterMap[registerNumber];
+    }
+
     protected boolean addPredecessor(AnalyzedInstruction predecessor) {
         return predecessors.add(predecessor);
     }
@@ -169,12 +189,19 @@
      * register is a destination register for this instruction, or if the pre-instruction register type didn't change
      * after merging in the given register type
      */
-    protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions) {
+    protected boolean mergeRegister(int registerNumber, RegisterType registerType, BitSet verifiedInstructions,
+                                    boolean override) {
         assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
         assert registerType != null;
 
         RegisterType oldRegisterType = preRegisterMap[registerNumber];
-        RegisterType mergedRegisterType = oldRegisterType.merge(registerType);
+
+        RegisterType mergedRegisterType;
+        if (override) {
+            mergedRegisterType = getMergedPreRegisterTypeFromPredecessors(registerNumber);
+        } else {
+            mergedRegisterType = oldRegisterType.merge(registerType);
+        }
 
         if (mergedRegisterType.equals(oldRegisterType)) {
             return false;
@@ -193,39 +220,82 @@
 
     /**
      * Iterates over the predecessors of this instruction, and merges all the post-instruction register types for the
-     * given register. Any dead, unreachable, or odexed predecessor is ignored
+     * given register. Any dead, unreachable, or odexed predecessor is ignored. This takes into account any overridden
+     * predecessor register types
+     *
      * @param registerNumber the register number
      * @return The register type resulting from merging the post-instruction register types from all predecessors
      */
-    protected RegisterType mergePreRegisterTypeFromPredecessors(int registerNumber) {
+    protected RegisterType getMergedPreRegisterTypeFromPredecessors(int registerNumber) {
         RegisterType mergedRegisterType = null;
         for (AnalyzedInstruction predecessor: predecessors) {
-            RegisterType predecessorRegisterType = predecessor.postRegisterMap[registerNumber];
-            assert predecessorRegisterType != null;
-            mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
+            RegisterType predecessorRegisterType = getPredecessorRegisterType(predecessor, registerNumber);
+            if (predecessorRegisterType != null) {
+                if (mergedRegisterType == null) {
+                    mergedRegisterType = predecessorRegisterType;
+                } else {
+                    mergedRegisterType = predecessorRegisterType.merge(mergedRegisterType);
+                }
+            }
         }
         return mergedRegisterType;
     }
+    /**
+     * Sets the "post-instruction" register type as indicated.
+     * @param registerNumber Which register to set
+     * @param registerType The "post-instruction" register type
+     * @return true if the given register type is different than the existing post-instruction register type
+     */
+    protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
+        assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
+        assert registerType != null;
 
-    /*
-      * Sets the "post-instruction" register type as indicated.
-      * @param registerNumber Which register to set
-      * @param registerType The "post-instruction" register type
-      * @returns true if the given register type is different than the existing post-instruction register type
-      */
-     protected boolean setPostRegisterType(int registerNumber, RegisterType registerType) {
-         assert registerNumber >= 0 && registerNumber < postRegisterMap.length;
-         assert registerType != null;
+        RegisterType oldRegisterType = postRegisterMap[registerNumber];
+        if (oldRegisterType.equals(registerType)) {
+            return false;
+        }
 
-         RegisterType oldRegisterType = postRegisterMap[registerNumber];
-         if (oldRegisterType.equals(registerType)) {
-             return false;
-         }
+        postRegisterMap[registerNumber] = registerType;
+        return true;
+    }
 
-         postRegisterMap[registerNumber] = registerType;
-         return true;
-     }
+    /**
+     * Adds an override for a register type from a predecessor.
+     *
+     * This is used to set the register type for only one branch from a conditional jump.
+     *
+     * @param predecessor Which predecessor is being overriden
+     * @param registerNumber The register number of the register being overriden
+     * @param registerType The overridden register type
+     * @param verifiedInstructions
+     *
+     * @return true if the post-instruction register type for this instruction changed as a result of this override
+     */
+    protected boolean overridePredecessorRegisterType(@Nonnull AnalyzedInstruction predecessor, int registerNumber,
+                                                      @Nonnull RegisterType registerType, BitSet verifiedInstructions) {
+        if (predecessorRegisterOverrides == null) {
+            predecessorRegisterOverrides = Maps.newHashMap();
+        }
+        predecessorRegisterOverrides.put(new PredecessorOverrideKey(predecessor, registerNumber), registerType);
 
+        RegisterType mergedType = getMergedPreRegisterTypeFromPredecessors(registerNumber);
+
+        if (preRegisterMap[registerNumber].equals(mergedType)) {
+            return false;
+        }
+
+        preRegisterMap[registerNumber] = mergedType;
+        verifiedInstructions.clear(instructionIndex);
+
+        if (!setsRegister(registerNumber)) {
+            if (!postRegisterMap[registerNumber].equals(mergedType)) {
+                postRegisterMap[registerNumber] = mergedType;
+                return true;
+            }
+        }
+
+        return false;
+    }
 
     protected boolean isInvokeInit() {
         if (instruction == null || !instruction.getOpcode().canInitializeReference()) {
@@ -328,5 +398,27 @@
             return 1;
         }
     }
+
+    private static class PredecessorOverrideKey {
+        public final AnalyzedInstruction analyzedInstruction;
+        public final int registerNumber;
+
+        public PredecessorOverrideKey(AnalyzedInstruction analyzedInstruction, int registerNumber) {
+            this.analyzedInstruction = analyzedInstruction;
+            this.registerNumber = registerNumber;
+        }
+
+        @Override public boolean equals(Object o) {
+            if (this == o) return true;
+            if (o == null || getClass() != o.getClass()) return false;
+            PredecessorOverrideKey that = (PredecessorOverrideKey)o;
+            return com.google.common.base.Objects.equal(registerNumber, that.registerNumber) &&
+                    Objects.equal(analyzedInstruction, that.analyzedInstruction);
+        }
+
+        @Override public int hashCode() {
+            return Objects.hashCode(analyzedInstruction, registerNumber);
+        }
+    }
 }
 
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java
new file mode 100644
index 0000000..775a819
--- /dev/null
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib2.analysis;
+
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.analysis.util.TypeProtoUtils;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.Method;
+import org.jf.dexlib2.util.MethodUtil;
+import org.jf.dexlib2.util.TypeUtils;
+
+import javax.annotation.Nonnull;
+
+public class AnalyzedMethodUtil {
+    public static boolean canAccess(@Nonnull TypeProto type, @Nonnull Method virtualMethod, boolean checkPackagePrivate,
+                                    boolean checkProtected, boolean checkClass) {
+        if (checkPackagePrivate && MethodUtil.isPackagePrivate(virtualMethod)) {
+            String otherPackage = TypeUtils.getPackage(virtualMethod.getDefiningClass());
+            String thisPackage = TypeUtils.getPackage(type.getType());
+            if (!otherPackage.equals(thisPackage)) {
+                return false;
+            }
+        }
+
+        if (checkProtected && (virtualMethod.getAccessFlags() & AccessFlags.PROTECTED.getValue()) != 0) {
+            if (!TypeProtoUtils.extendsFrom(type, virtualMethod.getDefiningClass())) {
+                return false;
+            }
+        }
+
+        if (checkClass) {
+            ClassPath classPath = type.getClassPath();
+            ClassDef methodClassDef = classPath.getClassDef(virtualMethod.getDefiningClass());
+            if (!TypeUtils.canAccessClass(type.getType(), methodClassDef)) {
+                return false;
+            }
+        }
+
+        return true;
+    }
+}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
index 8fcfc8c..4aa9a5e 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java
@@ -32,6 +32,7 @@
 package org.jf.dexlib2.analysis;
 
 import com.google.common.base.Strings;
+import org.jf.dexlib2.iface.Method;
 import org.jf.dexlib2.iface.reference.FieldReference;
 import org.jf.dexlib2.iface.reference.MethodReference;
 import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
@@ -160,7 +161,11 @@
 
     @Override
     @Nullable
-    public MethodReference getMethodByVtableIndex(int vtableIndex) {
+    public Method getMethodByVtableIndex(int vtableIndex) {
         return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
     }
+
+    @Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+        return classPath.getClass("Ljava/lang/Object;").findMethodIndexInVtable(method);
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
index bd9cfb1..9f9e396 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
@@ -31,15 +31,20 @@
 
 package org.jf.dexlib2.analysis;
 
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
 import com.google.common.cache.CacheBuilder;
 import com.google.common.cache.CacheLoader;
 import com.google.common.cache.LoadingCache;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
-import com.google.common.collect.Maps;
 import org.jf.dexlib2.DexFileFactory;
+import org.jf.dexlib2.DexFileFactory.DexFileNotFound;
+import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.analysis.reflection.ReflectionClassDef;
+import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
 import org.jf.dexlib2.iface.ClassDef;
 import org.jf.dexlib2.iface.DexFile;
 import org.jf.dexlib2.immutable.ImmutableDexFile;
@@ -49,48 +54,45 @@
 import java.io.File;
 import java.io.IOException;
 import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.HashMap;
+import java.util.Arrays;
+import java.util.List;
 import java.util.regex.Matcher;
 import java.util.regex.Pattern;
 
 public class ClassPath {
     @Nonnull private final TypeProto unknownClass;
-    @Nonnull private HashMap<String, ClassDef> availableClasses = Maps.newHashMap();
-    private boolean checkPackagePrivateAccess;
+    @Nonnull private List<ClassProvider> classProviders;
+    private final boolean checkPackagePrivateAccess;
+    public final int oatVersion;
+
+    public static final int NOT_ART = -1;
 
     /**
-     * Creates a new ClassPath instance that can load classes from the given dex files
+     * Creates a new ClassPath instance that can load classes from the given providers
      *
-     * @param classPath An array of DexFile objects. When loading a class, these dex files will be searched in order
+     * @param classProviders An iterable of ClassProviders. When loading a class, these providers will be searched in
+     *                       order
      */
-    public ClassPath(DexFile... classPath) throws IOException {
-        this(Lists.newArrayList(classPath), 15);
+    public ClassPath(ClassProvider... classProviders) throws IOException {
+        this(Arrays.asList(classProviders), false, NOT_ART);
     }
 
     /**
-     * Creates a new ClassPath instance that can load classes from the given dex files
+     * Creates a new ClassPath instance that can load classes from the given providers
      *
-     * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order
-     * @param api API level
+     * @param classProviders An iterable of ClassProviders. When loading a class, these providers will be searched in
+     *                       order
+     * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by
+     *                                  default
+     * @param oatVersion The applicable oat version, or NOT_ART
      */
-    public ClassPath(@Nonnull Iterable<DexFile> classPath, int api) {
-        this(Lists.newArrayList(classPath), api == 17);
-    }
-
-    /**
-     * Creates a new ClassPath instance that can load classes from the given dex files
-     *
-     * @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order
-     * @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by default
-     */
-    public ClassPath(@Nonnull Iterable<DexFile> classPath, boolean checkPackagePrivateAccess) {
+    public ClassPath(@Nonnull Iterable<? extends ClassProvider> classProviders, boolean checkPackagePrivateAccess,
+                     int oatVersion) {
         // add fallbacks for certain special classes that must be present
-        Iterable<DexFile> dexFiles = Iterables.concat(classPath, Lists.newArrayList(getBasicClasses()));
-
         unknownClass = new UnknownClassProto(this);
         loadedClasses.put(unknownClass.getType(), unknownClass);
         this.checkPackagePrivateAccess = checkPackagePrivateAccess;
+        this.oatVersion = oatVersion;
 
         loadPrimitiveType("Z");
         loadPrimitiveType("B");
@@ -102,33 +104,31 @@
         loadPrimitiveType("D");
         loadPrimitiveType("L");
 
-        for (DexFile dexFile: dexFiles) {
-            for (ClassDef classDef: dexFile.getClasses()) {
-                ClassDef prev = availableClasses.get(classDef.getType());
-                if (prev == null) {
-                    availableClasses.put(classDef.getType(), classDef);
-                }
-            }
-        }
+        this.classProviders = Lists.newArrayList(classProviders);
+        this.classProviders.add(getBasicClasses());
     }
 
     private void loadPrimitiveType(String type) {
         loadedClasses.put(type, new PrimitiveProto(this, type));
     }
 
-    private static DexFile getBasicClasses() {
+    private static ClassProvider getBasicClasses() {
         // fallbacks for some special classes that we assume are present
-        return new ImmutableDexFile(ImmutableSet.of(
+        return new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19), ImmutableSet.of(
                 new ReflectionClassDef(Class.class),
                 new ReflectionClassDef(Cloneable.class),
                 new ReflectionClassDef(Object.class),
                 new ReflectionClassDef(Serializable.class),
                 new ReflectionClassDef(String.class),
-                new ReflectionClassDef(Throwable.class)));
+                new ReflectionClassDef(Throwable.class))));
+    }
+
+    public boolean isArt() {
+        return oatVersion != NOT_ART;
     }
 
     @Nonnull
-    public TypeProto getClass(CharSequence type) {
+    public TypeProto getClass(@Nonnull CharSequence type) {
         return loadedClasses.getUnchecked(type.toString());
     }
 
@@ -146,11 +146,13 @@
 
     @Nonnull
     public ClassDef getClassDef(String type) {
-        ClassDef ret = availableClasses.get(type);
-        if (ret == null) {
-            throw new UnresolvedClassException("Could not resolve class %s", type);
+        for (ClassProvider provider: classProviders) {
+            ClassDef classDef = provider.getClassDef(type);
+            if (classDef != null) {
+                return classDef;
+            }
         }
-        return ret;
+        throw new UnresolvedClassException("Could not resolve class %s", type);
     }
 
     @Nonnull
@@ -171,23 +173,52 @@
     @Nonnull
     public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
                                           int api, boolean checkPackagePrivateAccess, boolean experimental) {
-        ArrayList<DexFile> dexFiles = Lists.newArrayList();
+        List<ClassProvider> providers = Lists.newArrayList();
+
+        int oatVersion = NOT_ART;
 
         for (String classPathEntry: classPath) {
-            try {
-                dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
-            } catch (ExceptionWithContext e){}
+            List<? extends DexFile> classPathDexFiles =
+                    loadClassPathEntry(classPathDirs, classPathEntry, api, experimental);
+            if (oatVersion == NOT_ART) {
+                for (DexFile classPathDexFile: classPathDexFiles) {
+                    if (classPathDexFile instanceof OatDexFile) {
+                        oatVersion = ((OatDexFile)classPathDexFile).getOatVersion();
+                        break;
+                    }
+                }
+            }
+            for (DexFile classPathDexFile: classPathDexFiles) {
+                providers.add(new DexClassProvider(classPathDexFile));
+            }
         }
-        dexFiles.add(dexFile);
-        return new ClassPath(dexFiles, checkPackagePrivateAccess);
+        providers.add(new DexClassProvider(dexFile));
+        return new ClassPath(providers, checkPackagePrivateAccess, oatVersion);
+    }
+
+    @Nonnull
+    public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
+                                          int api, boolean checkPackagePrivateAccess, boolean experimental,
+                                          int oatVersion) {
+        List<ClassProvider> providers = Lists.newArrayList();
+
+        for (String classPathEntry: classPath) {
+            List<? extends DexFile> classPathDexFiles =
+                    loadClassPathEntry(classPathDirs, classPathEntry, api, experimental);
+            for (DexFile classPathDexFile: classPathDexFiles) {
+                providers.add(new DexClassProvider(classPathDexFile));
+            }
+        }
+        providers.add(new DexClassProvider(dexFile));
+        return new ClassPath(providers, checkPackagePrivateAccess, oatVersion);
     }
 
     private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
 
     @Nonnull
-    private static DexFile loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
-                                              @Nonnull String bootClassPathEntry, int api,
-                                              boolean experimental) {
+    private static List<? extends DexFile> loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
+                                                              @Nonnull String bootClassPathEntry, int api,
+                                                              boolean experimental) {
         File rawEntry = new File(bootClassPathEntry);
         // strip off the path - we only care about the filename
         String entryName = rawEntry.getName();
@@ -213,7 +244,15 @@
         }
 
         for (String classPathDir: classPathDirs) {
-            for (String ext: new String[]{"", ".odex", ".jar", ".apk", ".zip"}) {
+            String[] extensions;
+
+            if (entryName.endsWith(".oat")) {
+                extensions = new String[] { ".oat" };
+            } else {
+                extensions = new String[] { "", ".odex", ".jar", ".apk", ".zip" };
+            }
+
+            for (String ext: extensions) {
                 File file = new File(classPathDir, baseEntryName + ext);
 
                 if (file.exists() && file.isFile()) {
@@ -222,9 +261,11 @@
                                 "warning: cannot open %s for reading. Will continue looking.", file.getPath()));
                     } else {
                         try {
-                            return DexFileFactory.loadDexFile(file, api, experimental);
-                        } catch (DexFileFactory.NoClassesDexException ex) {
+                            return ImmutableList.of(DexFileFactory.loadDexFile(file, api, experimental));
+                        } catch (DexFileNotFound ex) {
                             // ignore and continue
+                        } catch (MultipleDexFilesException ex) {
+                            return ex.oatFile.getDexFiles();
                         } catch (Exception ex) {
                             throw ExceptionWithContext.withContext(ex,
                                     "Error while reading boot class path entry \"%s\"", bootClassPathEntry);
@@ -235,4 +276,16 @@
         }
         throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry);
     }
+
+    private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
+            new Supplier<OdexedFieldInstructionMapper>() {
+                @Override public OdexedFieldInstructionMapper get() {
+                    return new OdexedFieldInstructionMapper(isArt());
+                }
+            });
+
+    @Nonnull
+    public OdexedFieldInstructionMapper getFieldInstructionMapper() {
+        return fieldInstructionMapperSupplier.get();
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
index f4f1dc7..e407de7 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
@@ -37,6 +37,7 @@
 import com.google.common.collect.Iterables;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
+import com.google.common.primitives.Ints;
 import org.jf.dexlib2.AccessFlags;
 import org.jf.dexlib2.analysis.util.TypeProtoUtils;
 import org.jf.dexlib2.iface.ClassDef;
@@ -45,21 +46,24 @@
 import org.jf.dexlib2.iface.reference.FieldReference;
 import org.jf.dexlib2.iface.reference.MethodReference;
 import org.jf.dexlib2.immutable.ImmutableMethod;
+import org.jf.dexlib2.util.MethodUtil;
+import org.jf.util.AlignmentUtils;
 import org.jf.util.ExceptionWithContext;
 import org.jf.util.SparseArray;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.LinkedHashMap;
-import java.util.List;
+import java.util.*;
 
 /**
  * A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields
  * and their offsets.
  */
 public class ClassProto implements TypeProto {
+    private static final byte REFERENCE = 0;
+    private static final byte WIDE = 1;
+    private static final byte OTHER = 2;
+
     @Nonnull protected final ClassPath classPath;
     @Nonnull protected final String type;
 
@@ -345,7 +349,7 @@
 
     @Override
     @Nullable
-    public MethodReference getMethodByVtableIndex(int vtableIndex) {
+    public Method getMethodByVtableIndex(int vtableIndex) {
         List<Method> vtable = getVtable();
         if (vtableIndex < 0 || vtableIndex >= vtable.size()) {
             return null;
@@ -354,21 +358,35 @@
         return vtable.get(vtableIndex);
     }
 
-    @Nonnull SparseArray<FieldReference> getInstanceFields() {
-        return instanceFieldsSupplier.get();
+    public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+        List<Method> vtable = getVtable();
+        for (int i=0; i<vtable.size(); i++) {
+            Method candidate = vtable.get(i);
+            if (MethodUtil.methodSignaturesMatch(candidate, method)) {
+                if (!classPath.shouldCheckPackagePrivateAccess() ||
+                        AnalyzedMethodUtil.canAccess(this, candidate, true, false, false)) {
+                    return i;
+                }
+            }
+        }
+        return -1;
     }
 
-    @Nonnull private final Supplier<SparseArray<FieldReference>> instanceFieldsSupplier =
+    @Nonnull SparseArray<FieldReference> getInstanceFields() {
+        if (classPath.isArt()) {
+            return artInstanceFieldsSupplier.get();
+        } else {
+            return dalvikInstanceFieldsSupplier.get();
+        }
+    }
+
+    @Nonnull private final Supplier<SparseArray<FieldReference>> dalvikInstanceFieldsSupplier =
             Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
                 @Override public SparseArray<FieldReference> get() {
                     //This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
                     //arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
                     //See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
 
-                    final byte REFERENCE = 0;
-                    final byte WIDE = 1;
-                    final byte OTHER = 2;
-
                     ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
                     final int fieldCount = fields.size();
                     //the "type" for each field in fields. 0=reference,1=wide,2=other
@@ -519,19 +537,6 @@
                     return fields;
                 }
 
-                private byte getFieldType(@Nonnull FieldReference field) {
-                    switch (field.getType().charAt(0)) {
-                        case '[':
-                        case 'L':
-                            return 0; //REFERENCE
-                        case 'J':
-                        case 'D':
-                            return 1; //WIDE
-                        default:
-                            return 2; //OTHER
-                    }
-                }
-
                 private void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) {
                     byte tempType = fieldTypes[position1];
                     fieldTypes[position1] = fieldTypes[position2];
@@ -542,23 +547,215 @@
                 }
             });
 
+    private static abstract class FieldGap implements Comparable<FieldGap> {
+        public final int offset;
+        public final int size;
+
+        public static FieldGap newFieldGap(int offset, int size, int oatVersion) {
+            if (oatVersion >= 67) {
+                return new FieldGap(offset, size) {
+                    @Override public int compareTo(FieldGap o) {
+                        int result = Ints.compare(o.size, size);
+                        if (result != 0) {
+                            return result;
+                        }
+                        return Ints.compare(offset, o.offset);
+                    }
+                };
+            } else {
+                return new FieldGap(offset, size) {
+                    @Override public int compareTo(FieldGap o) {
+                        int result = Ints.compare(size, o.size);
+                        if (result != 0) {
+                            return result;
+                        }
+                        return Ints.compare(o.offset, offset);
+                    }
+                };
+            }
+        }
+
+        private FieldGap(int offset, int size) {
+            this.offset = offset;
+            this.size = size;
+        }
+    }
+
+    @Nonnull private final Supplier<SparseArray<FieldReference>> artInstanceFieldsSupplier =
+            Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
+
+                @Override public SparseArray<FieldReference> get() {
+                    // We need to follow the same algorithm that art uses to arrange fields, so that we end up with the
+                    // same field offsets, which is needed for deodexing.
+                    // See LinkFields() in art/runtime/class_linker.cc
+
+                    PriorityQueue<FieldGap> gaps = new PriorityQueue<FieldGap>();
+
+                    SparseArray<FieldReference> linkedFields = new SparseArray<FieldReference>();
+                    ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
+
+                    int fieldOffset = 0;
+                    String superclassType = getSuperclass();
+                    if (superclassType != null) {
+                        // TODO: what to do if superclass doesn't exist?
+                        ClassProto superclass = (ClassProto) classPath.getClass(superclassType);
+                        SparseArray<FieldReference> superFields = superclass.getInstanceFields();
+                        FieldReference field = null;
+                        int lastOffset = 0;
+                        for (int i=0; i<superFields.size(); i++) {
+                            int offset = superFields.keyAt(i);
+                            field = superFields.valueAt(i);
+                            linkedFields.put(offset, field);
+                            lastOffset = offset;
+                        }
+                        if (field != null) {
+                            fieldOffset = lastOffset + getFieldSize(field);
+                        }
+                    }
+
+                    for (Field field: fields) {
+                        int fieldSize = getFieldSize(field);
+
+                        if (!AlignmentUtils.isAligned(fieldOffset, fieldSize)) {
+                            int oldOffset = fieldOffset;
+                            fieldOffset = AlignmentUtils.alignOffset(fieldOffset, fieldSize);
+                            addFieldGap(oldOffset, fieldOffset, gaps);
+                        }
+
+                        FieldGap gap = gaps.peek();
+                        if (gap != null && gap.size >= fieldSize) {
+                            gaps.poll();
+                            linkedFields.put(gap.offset, field);
+                            if (gap.size > fieldSize) {
+                                addFieldGap(gap.offset + fieldSize, gap.offset + gap.size, gaps);
+                            }
+                        } else {
+                            linkedFields.append(fieldOffset, field);
+                            fieldOffset += fieldSize;
+                        }
+                    }
+
+                    return linkedFields;
+                }
+
+                private void addFieldGap(int gapStart, int gapEnd, @Nonnull PriorityQueue<FieldGap> gaps) {
+                    int offset = gapStart;
+
+                    while (offset < gapEnd) {
+                        int remaining = gapEnd - offset;
+
+                        if ((remaining >= 4) && (offset % 4 == 0)) {
+                            gaps.add(FieldGap.newFieldGap(offset, 4, classPath.oatVersion));
+                            offset += 4;
+                        } else if (remaining >= 2 && (offset % 2 == 0)) {
+                            gaps.add(FieldGap.newFieldGap(offset, 2, classPath.oatVersion));
+                            offset += 2;
+                        } else {
+                            gaps.add(FieldGap.newFieldGap(offset, 1, classPath.oatVersion));
+                            offset += 1;
+                        }
+                    }
+                }
+
+                @Nonnull
+                private ArrayList<Field> getSortedInstanceFields(@Nonnull ClassDef classDef) {
+                    ArrayList<Field> fields = Lists.newArrayList(classDef.getInstanceFields());
+                    Collections.sort(fields, new Comparator<Field>() {
+                        @Override public int compare(Field field1, Field field2) {
+                            int result = Ints.compare(getFieldSortOrder(field1), getFieldSortOrder(field2));
+                            if (result != 0) {
+                                return result;
+                            }
+
+                            result = field1.getName().compareTo(field2.getName());
+                            if (result != 0) {
+                                return result;
+                            }
+                            return field1.getType().compareTo(field2.getType());
+                        }
+                    });
+                    return fields;
+                }
+
+                private int getFieldSortOrder(@Nonnull FieldReference field) {
+                    // The sort order is based on type size (except references are first), and then based on the
+                    // enum value of the primitive type for types of equal size. See: Primitive::Type enum
+                    // in art/runtime/primitive.h
+                    switch (field.getType().charAt(0)) {
+                        /* reference */
+                        case '[':
+                        case 'L':
+                            return 0;
+                        /* 64 bit */
+                        case 'J':
+                            return 1;
+                        case 'D':
+                            return 2;
+                        /* 32 bit */
+                        case 'I':
+                            return 3;
+                        case 'F':
+                            return 4;
+                        /* 16 bit */
+                        case 'C':
+                            return 5;
+                        case 'S':
+                            return 6;
+                        /* 8 bit */
+                        case 'Z':
+                            return 7;
+                        case 'B':
+                            return 8;
+                    }
+                    throw new ExceptionWithContext("Invalid field type: %s", field.getType());
+                }
+
+                private int getFieldSize(@Nonnull FieldReference field) {
+                    return getTypeSize(field.getType().charAt(0));
+                }
+            });
+
     private int getNextFieldOffset() {
         SparseArray<FieldReference> instanceFields = getInstanceFields();
         if (instanceFields.size() == 0) {
-            return 8;
+            return classPath.isArt() ? 0 : 8;
         }
 
         int lastItemIndex = instanceFields.size()-1;
         int fieldOffset = instanceFields.keyAt(lastItemIndex);
         FieldReference lastField = instanceFields.valueAt(lastItemIndex);
 
-        switch (lastField.getType().charAt(0)) {
+        if (classPath.isArt()) {
+            return fieldOffset + getTypeSize(lastField.getType().charAt(0));
+        } else {
+            switch (lastField.getType().charAt(0)) {
+                case 'J':
+                case 'D':
+                    return fieldOffset + 8;
+                default:
+                    return fieldOffset + 4;
+            }
+        }
+    }
+
+    private static int getTypeSize(char type) {
+        switch (type) {
             case 'J':
             case 'D':
-                return fieldOffset + 8;
-            default:
-                return fieldOffset + 4;
+                return 8;
+            case '[':
+            case 'L':
+            case 'I':
+            case 'F':
+                return 4;
+            case 'C':
+            case 'S':
+                return 2;
+            case 'B':
+            case 'Z':
+                return 1;
         }
+        throw new ExceptionWithContext("Invalid type: %s", type);
     }
 
     @Nonnull List<Method> getVtable() {
@@ -626,8 +823,9 @@
             outer: for (Method virtualMethod: methods) {
                 for (int i=0; i<vtable.size(); i++) {
                     Method superMethod = vtable.get(i);
-                    if (methodSignaturesMatch(superMethod, virtualMethod)) {
-                        if (!classPath.shouldCheckPackagePrivateAccess() || canAccess(superMethod)) {
+                    if (MethodUtil.methodSignaturesMatch(superMethod, virtualMethod)) {
+                        if (!classPath.shouldCheckPackagePrivateAccess() ||
+                                AnalyzedMethodUtil.canAccess(ClassProto.this, superMethod, true, false, false)) {
                             if (replaceExisting) {
                                 vtable.set(i, virtualMethod);
                             }
@@ -639,36 +837,18 @@
                 vtable.add(virtualMethod);
             }
         }
-
-        private boolean methodSignaturesMatch(@Nonnull Method a, @Nonnull Method b) {
-            return (a.getName().equals(b.getName()) &&
-                    a.getReturnType().equals(b.getReturnType()) &&
-                    a.getParameters().equals(b.getParameters()));
-        }
-
-        private boolean canAccess(@Nonnull Method virtualMethod) {
-            if (!methodIsPackagePrivate(virtualMethod.getAccessFlags())) {
-                return true;
-            }
-
-            String otherPackage = getPackage(virtualMethod.getDefiningClass());
-            String ourPackage = getPackage(getClassDef().getType());
-            return otherPackage.equals(ourPackage);
-        }
-
-        @Nonnull
-        private String getPackage(@Nonnull String classType) {
-            int lastSlash = classType.lastIndexOf('/');
-            if (lastSlash < 0) {
-                return "";
-            }
-            return classType.substring(1, lastSlash);
-        }
-
-        private boolean methodIsPackagePrivate(int accessFlags) {
-            return (accessFlags & (AccessFlags.PRIVATE.getValue() |
-                    AccessFlags.PROTECTED.getValue() |
-                    AccessFlags.PUBLIC.getValue())) == 0;
-        }
     });
+
+    private static byte getFieldType(@Nonnull FieldReference field) {
+        switch (field.getType().charAt(0)) {
+            case '[':
+            case 'L':
+                return 0; //REFERENCE
+            case 'J':
+            case 'D':
+                return 1; //WIDE
+            default:
+                return 2; //OTHER
+        }
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java
new file mode 100644
index 0000000..7c823ff
--- /dev/null
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib2.analysis;
+
+import org.jf.dexlib2.iface.ClassDef;
+
+import javax.annotation.Nullable;
+
+public interface ClassProvider {
+    @Nullable ClassDef getClassDef(String type);
+}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java
new file mode 100644
index 0000000..c460cc3
--- /dev/null
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib2.analysis;
+
+import com.google.common.collect.Maps;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.DexFile;
+
+import javax.annotation.Nullable;
+import java.util.Map;
+
+public class DexClassProvider implements ClassProvider {
+    private final DexFile dexFile;
+    private Map<String, ClassDef> classMap = Maps.newHashMap();
+
+    public DexClassProvider(DexFile dexFile) {
+        this.dexFile = dexFile;
+
+        for (ClassDef classDef: dexFile.getClasses()) {
+            classMap.put(classDef.getType(), classDef);
+        }
+    }
+
+    @Nullable @Override public ClassDef getClassDef(String type) {
+        return classMap.get(type);
+    }
+}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
index 4402c62..f874f1b 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
@@ -49,6 +49,7 @@
 import org.jf.dexlib2.util.MethodUtil;
 import org.jf.dexlib2.util.ReferenceUtil;
 import org.jf.dexlib2.util.TypeUtils;
+import org.jf.dexlib2.writer.util.TryListBuilder;
 import org.jf.util.BitSetUtils;
 import org.jf.util.ExceptionWithContext;
 import org.jf.util.SparseArray;
@@ -72,6 +73,8 @@
     @Nonnull private final Method method;
     @Nonnull private final MethodImplementation methodImpl;
 
+    private final boolean normalizeVirtualMethods;
+
     private final int paramRegisterCount;
 
     @Nonnull private final ClassPath classPath;
@@ -93,9 +96,10 @@
     private final AnalyzedInstruction startOfMethod;
 
     public MethodAnalyzer(@Nonnull ClassPath classPath, @Nonnull Method method,
-                          @Nullable InlineMethodResolver inlineResolver) {
+                          @Nullable InlineMethodResolver inlineResolver, boolean normalizeVirtualMethods) {
         this.classPath = classPath;
         this.inlineResolver = inlineResolver;
+        this.normalizeVirtualMethods = normalizeVirtualMethods;
 
         this.method = method;
 
@@ -251,7 +255,7 @@
                 int objectRegisterNumber;
                 switch (instruction.getOpcode().format) {
                     case Format10x:
-                        analyzeReturnVoidBarrier(analyzedInstruction, false);
+                        analyzeOdexReturnVoid(analyzedInstruction, false);
                         continue;
                     case Format21c:
                     case Format22c:
@@ -333,6 +337,48 @@
                 registerType);
     }
 
+    private void propagateChanges(@Nonnull BitSet changedInstructions, int registerNumber, boolean override) {
+        //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction
+        //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on
+        //the next iteration of the while loop.
+        //This could also be done recursively, but in large methods it would likely cause very deep recursion.
+        while (!changedInstructions.isEmpty()) {
+            for (int instructionIndex=changedInstructions.nextSetBit(0);
+                 instructionIndex>=0;
+                 instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
+
+                changedInstructions.clear(instructionIndex);
+
+                propagateRegisterToSuccessors(analyzedInstructions.valueAt(instructionIndex), registerNumber,
+                        changedInstructions, override);
+            }
+        }
+    }
+
+    private void overridePredecessorRegisterTypeAndPropagateChanges(
+            @Nonnull AnalyzedInstruction analyzedInstruction, @Nonnull AnalyzedInstruction predecessor,
+            int registerNumber, @Nonnull RegisterType registerType) {
+        BitSet changedInstructions = new BitSet(analyzedInstructions.size());
+
+        if (!analyzedInstruction.overridePredecessorRegisterType(
+                predecessor, registerNumber, registerType, analyzedState)) {
+            return;
+        }
+        changedInstructions.set(analyzedInstruction.instructionIndex);
+
+        propagateChanges(changedInstructions, registerNumber, true);
+
+        if (registerType.category == RegisterType.LONG_LO) {
+            checkWidePair(registerNumber, analyzedInstruction);
+            overridePredecessorRegisterTypeAndPropagateChanges(analyzedInstruction, predecessor, registerNumber + 1,
+                    RegisterType.LONG_HI_TYPE);
+        } else if (registerType.category == RegisterType.DOUBLE_LO) {
+            checkWidePair(registerNumber, analyzedInstruction);
+            overridePredecessorRegisterTypeAndPropagateChanges(analyzedInstruction, predecessor, registerNumber + 1,
+                    RegisterType.DOUBLE_HI_TYPE);
+        }
+    }
+
     private void setPostRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction,
                                                         int registerNumber, @Nonnull RegisterType registerType) {
 
@@ -342,25 +388,9 @@
             return;
         }
 
-        propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions);
+        propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions, false);
 
-        //Using a for loop inside the while loop optimizes for the common case of the successors of an instruction
-        //occurring after the instruction. Any successors that occur prior to the instruction will be picked up on
-        //the next iteration of the while loop.
-        //This could also be done recursively, but in large methods it would likely cause very deep recursion,
-        //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly
-        //annoying.
-        while (!changedInstructions.isEmpty()) {
-            for (int instructionIndex=changedInstructions.nextSetBit(0);
-                     instructionIndex>=0;
-                     instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
-
-                changedInstructions.clear(instructionIndex);
-
-                propagateRegisterToSuccessors(analyzedInstructions.valueAt(instructionIndex), registerNumber,
-                        changedInstructions);
-            }
-        }
+        propagateChanges(changedInstructions, registerNumber, false);
 
         if (registerType.category == RegisterType.LONG_LO) {
             checkWidePair(registerNumber, analyzedInstruction);
@@ -372,10 +402,10 @@
     }
 
     private void propagateRegisterToSuccessors(@Nonnull AnalyzedInstruction instruction, int registerNumber,
-                                               @Nonnull BitSet changedInstructions) {
+                                               @Nonnull BitSet changedInstructions, boolean override) {
         RegisterType postRegisterType = instruction.getPostInstructionRegisterType(registerNumber);
         for (AnalyzedInstruction successor: instruction.successors) {
-            if (successor.mergeRegister(registerNumber, postRegisterType, analyzedState)) {
+            if (successor.mergeRegister(registerNumber, postRegisterType, analyzedState, override)) {
                 changedInstructions.set(successor.instructionIndex);
             }
         }
@@ -401,6 +431,7 @@
         //and is covered by a try block should be set to a list of the first instructions of each exception handler
         //for the try block covering the instruction
         List<? extends TryBlock<? extends ExceptionHandler>> tries = methodImpl.getTryBlocks();
+        tries = TryListBuilder.massageTryBlocks(tries);
         int triesIndex = 0;
         TryBlock currentTry = null;
         AnalyzedInstruction[] currentExceptionHandlers = null;
@@ -468,11 +499,19 @@
                 OffsetInstruction offsetInstruction = (OffsetInstruction)instruction.instruction;
 
                 if (instructionOpcode == Opcode.PACKED_SWITCH || instructionOpcode == Opcode.SPARSE_SWITCH) {
-                    SwitchPayload switchPayload = (SwitchPayload)analyzedInstructions.get(instructionCodeAddress +
-                                    offsetInstruction.getCodeOffset()).instruction;
+                    AnalyzedInstruction analyzedSwitchPayload = analyzedInstructions.get(
+                            instructionCodeAddress + offsetInstruction.getCodeOffset());
+                    if (analyzedSwitchPayload == null) {
+                        throw new AnalysisException("Invalid switch payload offset");
+                    }
+                    SwitchPayload switchPayload = (SwitchPayload)analyzedSwitchPayload.instruction;
+
                     for (SwitchElement switchElement: switchPayload.getSwitchElements()) {
                         AnalyzedInstruction targetInstruction = analyzedInstructions.get(instructionCodeAddress +
                                 switchElement.getOffset());
+                        if (targetInstruction == null) {
+                            throw new AnalysisException("Invalid switch target offset");
+                        }
 
                         addPredecessorSuccessor(instruction, targetInstruction, exceptionHandlers,
                                 instructionsToProcess);
@@ -578,7 +617,8 @@
             case RETURN_OBJECT:
                 return true;
             case RETURN_VOID_BARRIER:
-                analyzeReturnVoidBarrier(analyzedInstruction);
+            case RETURN_VOID_NO_BARRIER:
+                analyzeOdexReturnVoid(analyzedInstruction);
                 return true;
             case CONST_4:
             case CONST_16:
@@ -734,21 +774,32 @@
             case SPUT_OBJECT:
                 return true;
             case INVOKE_VIRTUAL:
+                analyzeInvokeVirtual(analyzedInstruction, false);
+                return true;
             case INVOKE_SUPER:
+                analyzeInvokeVirtual(analyzedInstruction, false);
                 return true;
             case INVOKE_DIRECT:
                 analyzeInvokeDirect(analyzedInstruction);
                 return true;
             case INVOKE_STATIC:
+                return true;
             case INVOKE_INTERFACE:
+                // TODO: normalize interfaces
+                return true;
             case INVOKE_VIRTUAL_RANGE:
+                analyzeInvokeVirtual(analyzedInstruction, true);
+                return true;
             case INVOKE_SUPER_RANGE:
+                analyzeInvokeVirtual(analyzedInstruction, true);
                 return true;
             case INVOKE_DIRECT_RANGE:
                 analyzeInvokeDirectRange(analyzedInstruction);
                 return true;
             case INVOKE_STATIC_RANGE:
+                return true;
             case INVOKE_INTERFACE_RANGE:
+                // TODO: normalize interfaces
                 return true;
             case NEG_INT:
             case NOT_INT:
@@ -955,6 +1006,14 @@
             case IPUT_QUICK:
             case IPUT_WIDE_QUICK:
             case IPUT_OBJECT_QUICK:
+            case IPUT_BOOLEAN_QUICK:
+            case IPUT_BYTE_QUICK:
+            case IPUT_CHAR_QUICK:
+            case IPUT_SHORT_QUICK:
+            case IGET_BOOLEAN_QUICK:
+            case IGET_BYTE_QUICK:
+            case IGET_CHAR_QUICK:
+            case IGET_SHORT_QUICK:
                 return analyzeIputIgetQuick(analyzedInstruction);
             case INVOKE_VIRTUAL_QUICK:
                 return analyzeInvokeVirtualQuick(analyzedInstruction, false, false);
@@ -1014,8 +1073,11 @@
     }
 
     private void analyzeMoveResult(@Nonnull AnalyzedInstruction analyzedInstruction) {
-        AnalyzedInstruction previousInstruction = analyzedInstructions.valueAt(analyzedInstruction.instructionIndex-1);
-        if (!previousInstruction.instruction.getOpcode().setsResult()) {
+        AnalyzedInstruction previousInstruction = null;
+        if (analyzedInstruction.instructionIndex > 0) {
+            previousInstruction = analyzedInstructions.valueAt(analyzedInstruction.instructionIndex-1);
+        }
+        if (previousInstruction == null || !previousInstruction.instruction.getOpcode().setsResult()) {
             throw new AnalysisException(analyzedInstruction.instruction.getOpcode().name + " must occur after an " +
                     "invoke-*/fill-new-array instruction");
         }
@@ -1061,11 +1123,11 @@
         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
     }
 
-    private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction) {
-        analyzeReturnVoidBarrier(analyzedInstruction, true);
+    private void analyzeOdexReturnVoid(AnalyzedInstruction analyzedInstruction) {
+        analyzeOdexReturnVoid(analyzedInstruction, true);
     }
 
-    private void analyzeReturnVoidBarrier(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
+    private void analyzeOdexReturnVoid(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
         Instruction10x deodexedInstruction = new ImmutableInstruction10x(Opcode.RETURN_VOID);
 
         analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
@@ -1109,6 +1171,44 @@
 
     private void analyzeInstanceOf(@Nonnull AnalyzedInstruction analyzedInstruction) {
         setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, RegisterType.BOOLEAN_TYPE);
+
+        int instructionIndex = analyzedInstruction.getInstructionIndex();
+        AnalyzedInstruction nextAnalyzedInstruction = analyzedInstructions.valueAt(instructionIndex + 1);
+
+        Instruction nextInstruction = nextAnalyzedInstruction.instruction;
+        if (nextInstruction.getOpcode() == Opcode.IF_EQZ) {
+            if (((Instruction21t)nextInstruction).getRegisterA() == analyzedInstruction.getDestinationRegister()) {
+                Reference reference = ((Instruction22c)analyzedInstruction.getInstruction()).getReference();
+                RegisterType registerType = RegisterType.getRegisterType(classPath, (TypeReference)reference);
+
+                if (registerType.type != null && !registerType.type.isInterface()) {
+                    int objectRegister = ((TwoRegisterInstruction)analyzedInstruction.getInstruction()).getRegisterB();
+
+                    RegisterType existingType = nextAnalyzedInstruction.getPostInstructionRegisterType(objectRegister);
+
+                    if (existingType.type != null) {
+                        boolean override = false;
+
+                        // Only override if we're going from an interface to a class, or are going to a narrower class
+                        if (existingType.type.isInterface()) {
+                            override = true;
+                        } else {
+                            TypeProto commonSuperclass = registerType.type.getCommonSuperclass(existingType.type);
+                            // only if it's a narrowing conversion
+                            if (commonSuperclass.getType().equals(existingType.type.getType())) {
+                                override = true;
+                            }
+                        }
+
+                        if (override) {
+                            overridePredecessorRegisterTypeAndPropagateChanges(
+                                    analyzedInstructions.valueAt(instructionIndex + 2), nextAnalyzedInstruction,
+                                    objectRegister, registerType);
+                        }
+                    }
+                }
+            }
+        }
     }
 
     private void analyzeArrayLength(@Nonnull AnalyzedInstruction analyzedInstruction) {
@@ -1536,12 +1636,12 @@
 
         ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
 
-        if (!canAccessClass(thisClass, classPath.getClassDef(resolvedField.getDefiningClass()))) {
+        if (!TypeUtils.canAccessClass(thisClass.getType(), classPath.getClassDef(resolvedField.getDefiningClass()))) {
 
             // the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
             // than resolvedField.getDefiningClass()), and walk up the class hierarchy.
             ClassDef fieldClass = classPath.getClassDef(objectRegisterTypeProto.getType());
-            while (!canAccessClass(thisClass, fieldClass)) {
+            while (!TypeUtils.canAccessClass(thisClass.getType(), fieldClass)) {
                 String superclass = fieldClass.getSuperclass();
                 if (superclass == null) {
                     throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s",
@@ -1564,8 +1664,8 @@
 
         String fieldType = resolvedField.getType();
 
-        Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
-                instruction.getOpcode());
+        Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
+                fieldType, instruction.getOpcode());
 
         Instruction22c deodexedInstruction = new ImmutableInstruction22c(opcode, (byte)instruction.getRegisterA(),
                 (byte)instruction.getRegisterB(), resolvedField);
@@ -1576,6 +1676,75 @@
         return true;
     }
 
+    private boolean analyzeInvokeVirtual(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isRange) {
+        MethodReference targetMethod;
+
+        if (!normalizeVirtualMethods) {
+            return true;
+        }
+
+        if (isRange) {
+            Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
+            targetMethod = (MethodReference)instruction.getReference();
+        } else {
+            Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
+            targetMethod = (MethodReference)instruction.getReference();
+        }
+
+        TypeProto typeProto = classPath.getClass(targetMethod.getDefiningClass());
+        int methodIndex;
+        try {
+            methodIndex = typeProto.findMethodIndexInVtable(targetMethod);
+        } catch (UnresolvedClassException ex) {
+            return true;
+        }
+
+        if (methodIndex < 0) {
+            return true;
+        }
+
+        Method replacementMethod = typeProto.getMethodByVtableIndex(methodIndex);
+        assert replacementMethod != null;
+        while (true) {
+            String superType = typeProto.getSuperclass();
+            if (superType == null) {
+                break;
+            }
+            typeProto = classPath.getClass(superType);
+            Method resolvedMethod = typeProto.getMethodByVtableIndex(methodIndex);
+            if (resolvedMethod == null) {
+                break;
+            }
+
+            if (!resolvedMethod.equals(replacementMethod)) {
+                if (!AnalyzedMethodUtil.canAccess(typeProto, replacementMethod, true, true, true)) {
+                    continue;
+                }
+
+                replacementMethod = resolvedMethod;
+            }
+        }
+
+        if (replacementMethod.equals(method)) {
+            return true;
+        }
+
+        Instruction deodexedInstruction;
+        if (isRange) {
+            Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
+            deodexedInstruction = new ImmutableInstruction3rc(instruction.getOpcode(), instruction.getStartRegister(),
+                    instruction.getRegisterCount(), replacementMethod);
+        } else {
+            Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
+            deodexedInstruction = new ImmutableInstruction35c(instruction.getOpcode(), instruction.getRegisterCount(),
+                    instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(),
+                    instruction.getRegisterF(), instruction.getRegisterG(), replacementMethod);
+        }
+
+        analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
+        return true;
+    }
+
     private boolean analyzeInvokeVirtualQuick(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isSuper,
                                               boolean isRange) {
         int methodIndex;
@@ -1616,7 +1785,7 @@
             }
 
             resolvedMethod = superType.getMethodByVtableIndex(methodIndex);
-        } else{
+        } else {
             resolvedMethod = objectRegisterTypeProto.getMethodByVtableIndex(methodIndex);
         }
 
@@ -1628,12 +1797,13 @@
         // no need to check class access for invoke-super. A class can obviously access its superclass.
         ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
 
-        if (!isSuper && !canAccessClass(thisClass, classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
+        if (!isSuper && !TypeUtils.canAccessClass(
+                thisClass.getType(), classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
 
             // the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
             // than resolvedMethod.getDefiningClass()), and walk up the class hierarchy.
             ClassDef methodClass = classPath.getClassDef(objectRegisterTypeProto.getType());
-            while (!canAccessClass(thisClass, methodClass)) {
+            while (!TypeUtils.canAccessClass(thisClass.getType(), methodClass)) {
                 String superclass = methodClass.getSuperclass();
                 if (superclass == null) {
                     throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
@@ -1689,24 +1859,6 @@
         return true;
     }
 
-    private boolean canAccessClass(@Nonnull ClassDef accessorClassDef, @Nonnull ClassDef accesseeClassDef) {
-        if (AccessFlags.PUBLIC.isSet(accesseeClassDef.getAccessFlags())) {
-            return true;
-        }
-
-        // Classes can only be public or package private. Any private or protected inner classes are actually
-        // package private.
-        return getPackage(accesseeClassDef.getType()).equals(getPackage(accessorClassDef.getType()));
-    }
-
-    private static String getPackage(String className) {
-        int lastSlash = className.lastIndexOf('/');
-        if (lastSlash < 0) {
-            return "";
-        }
-        return className.substring(1, lastSlash);
-    }
-
     private boolean analyzePutGetVolatile(@Nonnull AnalyzedInstruction analyzedInstruction) {
         return analyzePutGetVolatile(analyzedInstruction, true);
     }
@@ -1717,12 +1869,12 @@
 
         Opcode originalOpcode = analyzedInstruction.instruction.getOpcode();
 
-        Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
-                originalOpcode);
+        Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
+                fieldType, originalOpcode);
 
         Instruction deodexedInstruction;
 
-        if (originalOpcode.isOdexedStaticVolatile()) {
+        if (originalOpcode.isStaticFieldAccessor()) {
             OneRegisterInstruction instruction = (OneRegisterInstruction)analyzedInstruction.instruction;
             deodexedInstruction = new ImmutableInstruction21c(opcode, instruction.getRegisterA(), field);
         } else {
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java
index 49136c3..0ed1fef 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java
@@ -34,155 +34,147 @@
 import org.jf.dexlib2.Opcode;
 
 import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.HashMap;
+import java.util.Map;
 
 public class OdexedFieldInstructionMapper {
-    private static Opcode[][][][] opcodeMap = new Opcode[][][][] {
-            //get opcodes
-            new Opcode[][][] {
-                    //iget quick
-                    new Opcode[][] {
-                            //odexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IGET_QUICK,
-                                /*B*/   Opcode.IGET_QUICK,
-                                /*S*/   Opcode.IGET_QUICK,
-                                /*C*/   Opcode.IGET_QUICK,
-                                /*I,F*/ Opcode.IGET_QUICK,
-                                /*J,D*/ Opcode.IGET_WIDE_QUICK,
-                                /*L,[*/ Opcode.IGET_OBJECT_QUICK
-                            },
-                            //deodexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IGET_BOOLEAN,
-                                /*B*/   Opcode.IGET_BYTE,
-                                /*S*/   Opcode.IGET_SHORT,
-                                /*C*/   Opcode.IGET_CHAR,
-                                /*I,F*/ Opcode.IGET,
-                                /*J,D*/ Opcode.IGET_WIDE,
-                                /*L,[*/ Opcode.IGET_OBJECT
-                            }
-                    },
-                    //iget volatile
-                    new Opcode[][] {
-                            //odexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IGET_VOLATILE,
-                                /*B*/   Opcode.IGET_VOLATILE,
-                                /*S*/   Opcode.IGET_VOLATILE,
-                                /*C*/   Opcode.IGET_VOLATILE,
-                                /*I,F*/ Opcode.IGET_VOLATILE,
-                                /*J,D*/ Opcode.IGET_WIDE_VOLATILE,
-                                /*L,[*/ Opcode.IGET_OBJECT_VOLATILE
-                            },
-                            //deodexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IGET_BOOLEAN,
-                                /*B*/   Opcode.IGET_BYTE,
-                                /*S*/   Opcode.IGET_SHORT,
-                                /*C*/   Opcode.IGET_CHAR,
-                                /*I,F*/ Opcode.IGET,
-                                /*J,D*/ Opcode.IGET_WIDE,
-                                /*L,[*/ Opcode.IGET_OBJECT
-                            }
-                    },
-                    //sget volatile
-                    new Opcode[][] {
-                            //odexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.SGET_VOLATILE,
-                                /*B*/   Opcode.SGET_VOLATILE,
-                                /*S*/   Opcode.SGET_VOLATILE,
-                                /*C*/   Opcode.SGET_VOLATILE,
-                                /*I,F*/ Opcode.SGET_VOLATILE,
-                                /*J,D*/ Opcode.SGET_WIDE_VOLATILE,
-                                /*L,[*/ Opcode.SGET_OBJECT_VOLATILE
-                            },
-                            //deodexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.SGET_BOOLEAN,
-                                /*B*/   Opcode.SGET_BYTE,
-                                /*S*/   Opcode.SGET_SHORT,
-                                /*C*/   Opcode.SGET_CHAR,
-                                /*I,F*/ Opcode.SGET,
-                                /*J,D*/ Opcode.SGET_WIDE,
-                                /*L,[*/ Opcode.SGET_OBJECT
-                            }
-                    }
-            },
-            //put opcodes
-            new Opcode[][][] {
-                    //iput quick
-                    new Opcode[][] {
-                            //odexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IPUT_QUICK,
-                                /*B*/   Opcode.IPUT_QUICK,
-                                /*S*/   Opcode.IPUT_QUICK,
-                                /*C*/   Opcode.IPUT_QUICK,
-                                /*I,F*/ Opcode.IPUT_QUICK,
-                                /*J,D*/ Opcode.IPUT_WIDE_QUICK,
-                                /*L,[*/ Opcode.IPUT_OBJECT_QUICK
-                            },
-                            //deodexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IPUT_BOOLEAN,
-                                /*B*/   Opcode.IPUT_BYTE,
-                                /*S*/   Opcode.IPUT_SHORT,
-                                /*C*/   Opcode.IPUT_CHAR,
-                                /*I,F*/ Opcode.IPUT,
-                                /*J,D*/ Opcode.IPUT_WIDE,
-                                /*L,[*/ Opcode.IPUT_OBJECT
-                            }
-                    },
-                    //iput volatile
-                    new Opcode[][] {
-                            //odexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IPUT_VOLATILE,
-                                /*B*/   Opcode.IPUT_VOLATILE,
-                                /*S*/   Opcode.IPUT_VOLATILE,
-                                /*C*/   Opcode.IPUT_VOLATILE,
-                                /*I,F*/ Opcode.IPUT_VOLATILE,
-                                /*J,D*/ Opcode.IPUT_WIDE_VOLATILE,
-                                /*L,[*/ Opcode.IPUT_OBJECT_VOLATILE
-                            },
-                            //deodexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.IPUT_BOOLEAN,
-                                /*B*/   Opcode.IPUT_BYTE,
-                                /*S*/   Opcode.IPUT_SHORT,
-                                /*C*/   Opcode.IPUT_CHAR,
-                                /*I,F*/ Opcode.IPUT,
-                                /*J,D*/ Opcode.IPUT_WIDE,
-                                /*L,[*/ Opcode.IPUT_OBJECT
-                            }
-                    },
-                    //sput volatile
-                    new Opcode[][] {
-                            //odexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.SPUT_VOLATILE,
-                                /*B*/   Opcode.SPUT_VOLATILE,
-                                /*S*/   Opcode.SPUT_VOLATILE,
-                                /*C*/   Opcode.SPUT_VOLATILE,
-                                /*I,F*/ Opcode.SPUT_VOLATILE,
-                                /*J,D*/ Opcode.SPUT_WIDE_VOLATILE,
-                                /*L,[*/ Opcode.SPUT_OBJECT_VOLATILE
-                            },
-                            //deodexed
-                            new Opcode[] {
-                                /*Z*/   Opcode.SPUT_BOOLEAN,
-                                /*B*/   Opcode.SPUT_BYTE,
-                                /*S*/   Opcode.SPUT_SHORT,
-                                /*C*/   Opcode.SPUT_CHAR,
-                                /*I,F*/ Opcode.SPUT,
-                                /*J,D*/ Opcode.SPUT_WIDE,
-                                /*L,[*/ Opcode.SPUT_OBJECT
-                            }
-                    }
-            }
+
+    private static final int GET = 0;
+    private static final int PUT = 1;
+
+    private static final int INSTANCE = 0;
+    private static final int STATIC = 1;
+
+    private static final int PRIMITIVE = 0;
+    private static final int WIDE = 1;
+    private static final int REFERENCE = 2;
+
+    private static class FieldOpcode {
+        public final char type;
+        public final boolean isStatic;
+        @Nonnull public final Opcode normalOpcode;
+        @Nullable public final Opcode quickOpcode;
+        @Nullable public final Opcode volatileOpcode;
+
+        public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode,
+                           @Nullable Opcode volatileOpcode) {
+            this.type = type;
+            this.isStatic = false;
+            this.normalOpcode = normalOpcode;
+            this.quickOpcode = quickOpcode;
+            this.volatileOpcode = volatileOpcode;
+        }
+
+        public FieldOpcode(char type, boolean isStatic, @Nonnull Opcode normalOpcode, @Nullable Opcode volatileOpcode) {
+            this.type = type;
+            this.isStatic = isStatic;
+            this.normalOpcode = normalOpcode;
+            this.quickOpcode = null;
+            this.volatileOpcode = volatileOpcode;
+        }
+
+        public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nullable Opcode quickOpcode) {
+            this.type = type;
+            this.isStatic = false;
+            this.normalOpcode = normalOpcode;
+            this.quickOpcode = quickOpcode;
+            this.volatileOpcode = null;
+        }
+    }
+
+    private static final FieldOpcode[] dalvikFieldOpcodes = new FieldOpcode[] {
+            new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
+            new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
+            new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
+            new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
+            new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
+            new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
+            new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
+            new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
+            new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
+            new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
+
+            new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
+            new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
+            new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
+            new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
+            new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
+            new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
+            new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
+            new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
+            new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
+            new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
+
+            new FieldOpcode('Z', true, Opcode.SPUT_BOOLEAN, Opcode.SPUT_VOLATILE),
+            new FieldOpcode('B', true, Opcode.SPUT_BYTE, Opcode.SPUT_VOLATILE),
+            new FieldOpcode('S', true, Opcode.SPUT_SHORT, Opcode.SPUT_VOLATILE),
+            new FieldOpcode('C', true, Opcode.SPUT_CHAR, Opcode.SPUT_VOLATILE),
+            new FieldOpcode('I', true, Opcode.SPUT, Opcode.SPUT_VOLATILE),
+            new FieldOpcode('F', true, Opcode.SPUT, Opcode.SPUT_VOLATILE),
+            new FieldOpcode('J', true, Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE),
+            new FieldOpcode('D', true, Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE),
+            new FieldOpcode('L', true, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE),
+            new FieldOpcode('[', true, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE),
+
+            new FieldOpcode('Z', true, Opcode.SGET_BOOLEAN, Opcode.SGET_VOLATILE),
+            new FieldOpcode('B', true, Opcode.SGET_BYTE, Opcode.SGET_VOLATILE),
+            new FieldOpcode('S', true, Opcode.SGET_SHORT, Opcode.SGET_VOLATILE),
+            new FieldOpcode('C', true, Opcode.SGET_CHAR, Opcode.SGET_VOLATILE),
+            new FieldOpcode('I', true, Opcode.SGET, Opcode.SGET_VOLATILE),
+            new FieldOpcode('F', true, Opcode.SGET, Opcode.SGET_VOLATILE),
+            new FieldOpcode('J', true, Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE),
+            new FieldOpcode('D', true, Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE),
+            new FieldOpcode('L', true, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE),
+            new FieldOpcode('[', true, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE),
     };
 
+    private static final FieldOpcode[] artFieldOpcodes = new FieldOpcode[] {
+            new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_BOOLEAN_QUICK),
+            new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_BYTE_QUICK),
+            new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_SHORT_QUICK),
+            new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_CHAR_QUICK),
+            new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK),
+            new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK),
+            new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
+            new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
+            new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
+            new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
+
+            new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_BOOLEAN_QUICK),
+            new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_BYTE_QUICK),
+            new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_SHORT_QUICK),
+            new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_CHAR_QUICK),
+            new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK),
+            new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK),
+            new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
+            new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
+            new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK),
+            new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK)
+    };
+
+    private final FieldOpcode[][][] opcodeMap = new FieldOpcode[2][2][10];
+    private final Map<Opcode, Integer> opcodeValueTypeMap = new HashMap<Opcode, Integer>(30);
+
+    private static int getValueType(char type) {
+        switch (type) {
+            case 'Z':
+            case 'B':
+            case 'S':
+            case 'C':
+            case 'I':
+            case 'F':
+                return PRIMITIVE;
+            case 'J':
+            case 'D':
+                return WIDE;
+            case 'L':
+            case '[':
+                return REFERENCE;
+        }
+        throw new RuntimeException(String.format("Unknown type %s: ", type));
+    }
+
     private static int getTypeIndex(char type) {
         switch (type) {
             case 'Z':
@@ -194,47 +186,71 @@
             case 'C':
                 return 3;
             case 'I':
-            case 'F':
                 return 4;
-            case 'J':
-            case 'D':
+            case 'F':
                 return 5;
-            case 'L':
-            case '[':
+            case 'J':
                 return 6;
-            default:
+            case 'D':
+                return 7;
+            case 'L':
+                return 8;
+            case '[':
+                return 9;
         }
         throw new RuntimeException(String.format("Unknown type %s: ", type));
     }
 
-    private static int getOpcodeSubtype(@Nonnull Opcode opcode) {
-        if (opcode.isOdexedInstanceQuick()) {
-            return 0;
-        } else if (opcode.isOdexedInstanceVolatile()) {
-            return 1;
-        } else if (opcode.isOdexedStaticVolatile()) {
-            return 2;
+    private static boolean isGet(@Nonnull Opcode opcode) {
+        return (opcode.flags & Opcode.SETS_REGISTER) != 0;
+    }
+
+    private static boolean isStatic(@Nonnull Opcode opcode) {
+        return (opcode.flags & Opcode.STATIC_FIELD_ACCESSOR) != 0;
+    }
+
+    public OdexedFieldInstructionMapper(boolean isArt) {
+        FieldOpcode[] opcodes;
+        if (isArt) {
+            opcodes = artFieldOpcodes;
+        } else {
+            opcodes = dalvikFieldOpcodes;
         }
-        throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name));
+
+        for (FieldOpcode fieldOpcode: opcodes) {
+            opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT]
+                    [isStatic(fieldOpcode.normalOpcode)?STATIC:INSTANCE]
+                    [getTypeIndex(fieldOpcode.type)] = fieldOpcode;
+
+            if (fieldOpcode.quickOpcode != null) {
+                opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
+            }
+            if (fieldOpcode.volatileOpcode != null) {
+                opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type));
+            }
+        }
     }
 
     @Nonnull
-    static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
-        int opcodeType = odexedOpcode.setsRegister()?0:1;
-        int opcodeSubType = getOpcodeSubtype(odexedOpcode);
-        int typeIndex = getTypeIndex(fieldType.charAt(0));
+    public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
+        FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT]
+                [isStatic(odexedOpcode)?STATIC:INSTANCE]
+                [getTypeIndex(fieldType.charAt(0))];
 
-        Opcode correctOdexedOpcode, deodexedOpcode;
-
-        correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
-        deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
-
-        if (correctOdexedOpcode != odexedOpcode) {
+        if (!isCompatible(odexedOpcode, fieldOpcode.type)) {
             throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType,
                     odexedOpcode.name));
         }
 
-        return deodexedOpcode;
+        return fieldOpcode.normalOpcode;
+    }
+
+    private boolean isCompatible(Opcode opcode, char type) {
+        Integer valueType = opcodeValueTypeMap.get(opcode);
+        if (valueType == null) {
+            throw new RuntimeException("Unexpected opcode: " + opcode.name);
+        }
+        return valueType == getValueType(type);
     }
 }
 
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java
index 06ab8e1..2c28393 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java
@@ -31,6 +31,7 @@
 
 package org.jf.dexlib2.analysis;
 
+import org.jf.dexlib2.iface.Method;
 import org.jf.dexlib2.iface.reference.FieldReference;
 import org.jf.dexlib2.iface.reference.MethodReference;
 import org.jf.util.ExceptionWithContext;
@@ -65,7 +66,11 @@
 
     @Override
     @Nullable
-    public MethodReference getMethodByVtableIndex(int vtableIndex) {
+    public Method getMethodByVtableIndex(int vtableIndex) {
         return null;
     }
+
+    @Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+        return -1;
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java
index f6db239..776363b 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java
@@ -31,6 +31,7 @@
 
 package org.jf.dexlib2.analysis;
 
+import org.jf.dexlib2.iface.Method;
 import org.jf.dexlib2.iface.reference.FieldReference;
 import org.jf.dexlib2.iface.reference.MethodReference;
 
@@ -45,5 +46,6 @@
     @Nullable String getSuperclass();
     @Nonnull TypeProto getCommonSuperclass(@Nonnull TypeProto other);
     @Nullable FieldReference getFieldByOffset(int fieldOffset);
-    @Nullable MethodReference getMethodByVtableIndex(int vtableIndex);
+    @Nullable Method getMethodByVtableIndex(int vtableIndex);
+    int findMethodIndexInVtable(@Nonnull MethodReference method);
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java
index 38256fb..3287345 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java
@@ -31,6 +31,7 @@
 
 package org.jf.dexlib2.analysis;
 
+import org.jf.dexlib2.iface.Method;
 import org.jf.dexlib2.iface.reference.FieldReference;
 import org.jf.dexlib2.iface.reference.MethodReference;
 
@@ -75,7 +76,11 @@
 
     @Override
     @Nullable
-    public MethodReference getMethodByVtableIndex(int vtableIndex) {
+    public Method getMethodByVtableIndex(int vtableIndex) {
         return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
     }
+
+    @Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
+        return classPath.getClass("Ljava/lang/Object;").findMethodIndexInVtable(method);
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java
index 0313c7c..e2adf1c 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java
@@ -94,4 +94,16 @@
             return type.getClassPath().getUnknownClass();
         }
     }
+
+    public static boolean extendsFrom(@Nonnull TypeProto candidate, @Nonnull String possibleSuper) {
+        if (candidate.getType().equals(possibleSuper)) {
+            return true;
+        }
+        for (TypeProto superProto: getSuperclassChain(candidate)) {
+            if (superProto.getType().equals(possibleSuper)) {
+                return true;
+            }
+        }
+        return false;
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java b/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java
index 8498122..b1e5dbb 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java
@@ -482,6 +482,49 @@
         } while (true);
     }
 
+    private int mapCodeAddressToIndex(int codeAddress) {
+        float avgCodeUnitsPerInstruction = 1.9f;
+
+        int index = (int)(codeAddress/avgCodeUnitsPerInstruction);
+        if (index >= instructionList.size()) {
+            index = instructionList.size() - 1;
+        }
+
+        MethodLocation guessedLocation = instructionList.get(index);
+
+        if (guessedLocation.codeAddress == codeAddress) {
+            return index;
+        } else if (guessedLocation.codeAddress > codeAddress) {
+            do {
+                index--;
+            } while (instructionList.get(index).codeAddress > codeAddress);
+            return index;
+        } else {
+            do {
+                index++;
+            } while (index < instructionList.size() && instructionList.get(index).codeAddress <= codeAddress);
+            return index-1;
+        }
+    }
+
+    @Nonnull
+    public Label newLabelForAddress(int codeAddress) {
+        if (codeAddress < 0 || codeAddress > instructionList.get(instructionList.size()-1).codeAddress) {
+            throw new IndexOutOfBoundsException(String.format("codeAddress %d out of bounds", codeAddress));
+        }
+        MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddress));
+        return referent.addNewLabel();
+    }
+
+    @Nonnull
+    public Label newLabelForIndex(int instructionIndex) {
+        if (instructionIndex < 0 || instructionIndex >= instructionList.size()) {
+            throw new IndexOutOfBoundsException(String.format("instruction index %d out of bounds", instructionIndex));
+        }
+        MethodLocation referent = instructionList.get(instructionIndex);
+        return referent.addNewLabel();
+    }
+
     @Nonnull
     private Label newLabel(@Nonnull int[] codeAddressToIndex, int codeAddress) {
         MethodLocation referent = instructionList.get(mapCodeAddressToIndex(codeAddressToIndex, codeAddress));
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java
index 7d06bff..eeb2822 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java
@@ -37,13 +37,19 @@
 
 public class BaseDexBuffer {
     @Nonnull /* package private */ final byte[] buf;
+    /* package private */ final int baseOffset;
 
     public BaseDexBuffer(@Nonnull byte[] buf) {
+        this(buf, 0);
+    }
+    public BaseDexBuffer(@Nonnull byte[] buf, int offset) {
         this.buf = buf;
+        this.baseOffset = offset;
     }
 
     public int readSmallUint(int offset) {
         byte[] buf = this.buf;
+        offset += baseOffset;
         int result = (buf[offset] & 0xff) |
                 ((buf[offset+1] & 0xff) << 8) |
                 ((buf[offset+2] & 0xff) << 16) |
@@ -56,6 +62,7 @@
 
     public int readOptionalUint(int offset) {
         byte[] buf = this.buf;
+        offset += baseOffset;
         int result = (buf[offset] & 0xff) |
                 ((buf[offset+1] & 0xff) << 8) |
                 ((buf[offset+2] & 0xff) << 16) |
@@ -68,16 +75,18 @@
 
     public int readUshort(int offset) {
         byte[] buf = this.buf;
+        offset += baseOffset;
         return (buf[offset] & 0xff) |
                 ((buf[offset+1] & 0xff) << 8);
     }
 
     public int readUbyte(int offset) {
-        return buf[offset] & 0xff;
+        return buf[offset + baseOffset] & 0xff;
     }
 
     public long readLong(int offset) {
         byte[] buf = this.buf;
+        offset += baseOffset;
         return (buf[offset] & 0xff) |
                 ((buf[offset+1] & 0xff) << 8) |
                 ((buf[offset+2] & 0xff) << 16) |
@@ -88,8 +97,26 @@
                 (((long)buf[offset+7]) << 56);
     }
 
+    public int readLongAsSmallUint(int offset) {
+        byte[] buf = this.buf;
+        offset += baseOffset;
+        long result = (buf[offset] & 0xff) |
+                ((buf[offset+1] & 0xff) << 8) |
+                ((buf[offset+2] & 0xff) << 16) |
+                ((buf[offset+3] & 0xffL) << 24) |
+                ((buf[offset+4] & 0xffL) << 32) |
+                ((buf[offset+5] & 0xffL) << 40) |
+                ((buf[offset+6] & 0xffL) << 48) |
+                (((long)buf[offset+7]) << 56);
+        if (result < 0 || result > Integer.MAX_VALUE) {
+            throw new ExceptionWithContext("Encountered out-of-range ulong at offset 0x%x", offset);
+        }
+        return (int)result;
+    }
+
     public int readInt(int offset) {
         byte[] buf = this.buf;
+        offset += baseOffset;
         return (buf[offset] & 0xff) |
                 ((buf[offset+1] & 0xff) << 8) |
                 ((buf[offset+2] & 0xff) << 16) |
@@ -98,12 +125,13 @@
 
     public int readShort(int offset) {
         byte[] buf = this.buf;
+        offset += baseOffset;
         return (buf[offset] & 0xff) |
                 (buf[offset+1] << 8);
     }
 
     public int readByte(int offset) {
-        return buf[offset];
+        return buf[baseOffset + offset];
     }
 
     @Nonnull
@@ -115,4 +143,8 @@
     protected byte[] getBuf() {
         return buf;
     }
+
+    protected int getBaseOffset() {
+        return baseOffset;
+    }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java
index 96645b8..13c0a7b 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java
@@ -49,7 +49,7 @@
     public void setOffset(int offset) { this.offset = offset; }
 
     public int readSleb128() {
-        int end = offset;
+        int end = dexBuf.baseOffset + offset;
         int currentByteValue;
         int result;
         byte[] buf = dexBuf.buf;
@@ -84,7 +84,7 @@
             }
         }
 
-        offset = end;
+        offset = end - dexBuf.baseOffset;
         return result;
     }
 
@@ -93,7 +93,7 @@
     }
 
     private int readUleb128(boolean allowLarge) {
-        int end = offset;
+        int end = dexBuf.baseOffset + offset;
         int currentByteValue;
         int result;
         byte[] buf = dexBuf.buf;
@@ -129,7 +129,7 @@
             }
         }
 
-        offset = end;
+        offset = end - dexBuf.baseOffset;
         return result;
     }
 
@@ -150,7 +150,7 @@
      * @return The unsigned value, reinterpreted as a signed int
      */
     public int readBigUleb128() {
-        int end = offset;
+        int end = dexBuf.baseOffset + offset;
         int currentByteValue;
         int result;
         byte[] buf = dexBuf.buf;
@@ -179,12 +179,12 @@
             }
         }
 
-        offset = end;
+        offset = end - dexBuf.baseOffset;
         return result;
     }
 
     public void skipUleb128() {
-        int end = offset;
+        int end = dexBuf.baseOffset + offset;
         byte currentByteValue;
         byte[] buf = dexBuf.buf;
 
@@ -206,7 +206,7 @@
             }
         }
 
-        offset = end;
+        offset = end - dexBuf.baseOffset;
     }
 
     public int readSmallUint() {
@@ -285,7 +285,7 @@
     public int readByte(int offset) { return dexBuf.readByte(offset); }
 
     public int readSizedInt(int bytes) {
-        int o = offset;
+        int o = dexBuf.baseOffset + offset;
         byte[] buf = dexBuf.buf;
 
         int result;
@@ -311,12 +311,12 @@
             default:
                 throw new ExceptionWithContext("Invalid size %d for sized int at offset 0x%x", bytes, offset);
         }
-        offset = o + bytes;
+        offset = o + bytes - dexBuf.baseOffset;
         return result;
     }
 
     public int readSizedSmallUint(int bytes) {
-        int o = offset;
+        int o = dexBuf.baseOffset + offset;
         byte[] buf = dexBuf.buf;
 
         int result = 0;
@@ -341,12 +341,12 @@
             default:
                 throw new ExceptionWithContext("Invalid size %d for sized uint at offset 0x%x", bytes, offset);
         }
-        offset = o + bytes;
+        offset = o + bytes - dexBuf.baseOffset;
         return result;
     }
 
     public int readSizedRightExtendedInt(int bytes) {
-        int o = offset;
+        int o = dexBuf.baseOffset + offset;
         byte[] buf = dexBuf.buf;
 
         int result;
@@ -373,12 +373,12 @@
                 throw new ExceptionWithContext(
                         "Invalid size %d for sized, right extended int at offset 0x%x", bytes, offset);
         }
-        offset = o + bytes;
+        offset = o + bytes - dexBuf.baseOffset;
         return result;
     }
 
     public long readSizedRightExtendedLong(int bytes) {
-        int o = offset;
+        int o = dexBuf.baseOffset + offset;
         byte[] buf = dexBuf.buf;
 
         long result;
@@ -439,12 +439,12 @@
                 throw new ExceptionWithContext(
                         "Invalid size %d for sized, right extended long at offset 0x%x", bytes, offset);
         }
-        offset = o + bytes;
+        offset = o + bytes - dexBuf.baseOffset;
         return result;
     }
 
     public long readSizedLong(int bytes) {
-        int o = offset;
+        int o = dexBuf.baseOffset + offset;
         byte[] buf = dexBuf.buf;
 
         long result;
@@ -505,13 +505,14 @@
                 throw new ExceptionWithContext("Invalid size %d for sized long at offset 0x%x", bytes, offset);
         }
 
-        offset = o + bytes;
+        offset = o + bytes - dexBuf.baseOffset;
         return result;
     }
 
     public String readString(int utf16Length) {
         int[] ret = new int[1];
-        String value = Utf8Utils.utf8BytesWithUtf16LengthToString(dexBuf.buf, offset, utf16Length, ret);
+        String value = Utf8Utils.utf8BytesWithUtf16LengthToString(
+                dexBuf.buf, dexBuf.baseOffset + offset, utf16Length, ret);
         offset += ret[0];
         return value;
     }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java
index 1774096..32505ee 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java
@@ -46,7 +46,7 @@
 import java.util.Set;
 
 public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
-    private final Opcodes opcodes;
+    @Nonnull private final Opcodes opcodes;
 
     private final int stringCount;
     private final int stringStartOffset;
@@ -61,8 +61,8 @@
     private final int classCount;
     private final int classStartOffset;
 
-    private DexBackedDexFile(Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
-        super(buf);
+    private DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
+        super(buf, offset);
 
         this.opcodes = opcodes;
 
@@ -117,14 +117,20 @@
         return new DexBackedDexFile(opcodes, buf, 0, false);
     }
 
-    public Opcodes getOpcodes() {
+    @Override @Nonnull public Opcodes getOpcodes() {
         return opcodes;
     }
 
+    // Will only be true for a dalvik-style odex file
     public boolean isOdexFile() {
         return false;
     }
 
+    // Will be true for both a dalvik-style odex file, and an art-style odex file embedded in an oat file
+    public boolean hasOdexOpcodes() {
+        return false;
+    }
+
     @Nonnull
     @Override
     public Set<? extends DexBackedClassDef> getClasses() {
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java
index 1aa9c1e..12f19db 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java
@@ -33,7 +33,6 @@
 
 import com.google.common.io.ByteStreams;
 import org.jf.dexlib2.Opcodes;
-import org.jf.dexlib2.dexbacked.raw.HeaderItem;
 import org.jf.dexlib2.dexbacked.raw.OdexHeaderItem;
 import org.jf.dexlib2.dexbacked.util.VariableSizeList;
 
@@ -61,6 +60,10 @@
         return true;
     }
 
+    @Override public boolean hasOdexOpcodes() {
+        return true;
+    }
+
     public List<String> getDependencies() {
         final int dexOffset = OdexHeaderItem.getDexOffset(odexBuf);
         final int dependencyOffset = OdexHeaderItem.getDependenciesOffset(odexBuf) - dexOffset;
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java
new file mode 100644
index 0000000..dbeb67c
--- /dev/null
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java
@@ -0,0 +1,496 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib2.dexbacked;
+
+import com.google.common.io.ByteStreams;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.dexbacked.OatFile.SymbolTable.Symbol;
+import org.jf.dexlib2.dexbacked.raw.HeaderItem;
+import org.jf.util.AbstractForwardSequentialList;
+
+import javax.annotation.Nonnull;
+import java.io.EOFException;
+import java.io.IOException;
+import java.io.InputStream;
+import java.nio.charset.Charset;
+import java.util.AbstractList;
+import java.util.Iterator;
+import java.util.List;
+
+public class OatFile extends BaseDexBuffer {
+    private static final byte[] ELF_MAGIC = new byte[] { 0x7f, 'E', 'L', 'F' };
+    private static final byte[] OAT_MAGIC = new byte[] { 'o', 'a', 't', '\n' };
+    private static final int MIN_ELF_HEADER_SIZE = 52;
+
+    // These are the "known working" versions that I have manually inspected the source for.
+    // Later version may or may not work, depending on what changed.
+    private static final int MIN_OAT_VERSION = 56;
+    private static final int MAX_OAT_VERSION = 71;
+
+    public static final int UNSUPPORTED = 0;
+    public static final int SUPPORTED = 1;
+    public static final int UNKNOWN = 2;
+
+    private final boolean is64bit;
+    @Nonnull private final OatHeader oatHeader;
+    @Nonnull private final Opcodes opcodes;
+
+    public OatFile(@Nonnull byte[] buf) {
+        super(buf);
+
+        if (buf.length < MIN_ELF_HEADER_SIZE) {
+            throw new NotAnOatFileException();
+        }
+
+        verifyMagic(buf);
+
+        if (buf[4] == 1) {
+            is64bit = false;
+        } else if (buf[4] == 2) {
+            is64bit = true;
+        } else {
+            throw new InvalidOatFileException(String.format("Invalid word-size value: %x", buf[5]));
+        }
+
+        OatHeader oatHeader = null;
+        SymbolTable symbolTable = getSymbolTable();
+        for (Symbol symbol: symbolTable.getSymbols()) {
+            if (symbol.getName().equals("oatdata")) {
+                oatHeader = new OatHeader(symbol.getFileOffset());
+                break;
+            }
+        }
+
+        if (oatHeader == null) {
+            throw new InvalidOatFileException("Oat file has no oatdata symbol");
+        }
+        this.oatHeader = oatHeader;
+
+        if (!oatHeader.isValid()) {
+            throw new InvalidOatFileException("Invalid oat magic value");
+        }
+
+        this.opcodes = Opcodes.forArtVersion(oatHeader.getVersion());
+    }
+
+    private static void verifyMagic(byte[] buf) {
+        for (int i = 0; i < ELF_MAGIC.length; i++) {
+            if (buf[i] != ELF_MAGIC[i]) {
+                throw new NotAnOatFileException();
+            }
+        }
+    }
+
+    public static OatFile fromInputStream(@Nonnull InputStream is)
+            throws IOException {
+        if (!is.markSupported()) {
+            throw new IllegalArgumentException("InputStream must support mark");
+        }
+        is.mark(4);
+        byte[] partialHeader = new byte[4];
+        try {
+            ByteStreams.readFully(is, partialHeader);
+        } catch (EOFException ex) {
+            throw new NotAnOatFileException();
+        } finally {
+            is.reset();
+        }
+
+        verifyMagic(partialHeader);
+
+        is.reset();
+
+        byte[] buf = ByteStreams.toByteArray(is);
+        return new OatFile(buf);
+    }
+
+    public int getOatVersion() {
+        return oatHeader.getVersion();
+    }
+
+    public int isSupportedVersion() {
+        int version = getOatVersion();
+        if (version < MIN_OAT_VERSION) {
+            return UNSUPPORTED;
+        }
+        if (version <= MAX_OAT_VERSION) {
+            return SUPPORTED;
+        }
+        return UNKNOWN;
+    }
+
+    @Nonnull
+    public List<OatDexFile> getDexFiles() {
+        return new AbstractForwardSequentialList<OatDexFile>() {
+            @Override public int size() {
+                return oatHeader.getDexFileCount();
+            }
+
+            @Nonnull @Override public Iterator<OatDexFile> iterator() {
+                return new Iterator<OatDexFile>() {
+                    int index = 0;
+                    int offset = oatHeader.getDexListStart();
+
+                    @Override public boolean hasNext() {
+                        return index < size();
+                    }
+
+                    @Override public OatDexFile next() {
+                        int filenameLength = readSmallUint(offset);
+                        offset += 4;
+
+                        // TODO: what is the correct character encoding?
+                        String filename = new String(buf, offset, filenameLength, Charset.forName("US-ASCII"));
+                        offset += filenameLength;
+
+                        offset += 4; // checksum
+
+                        int dexOffset = readSmallUint(offset) + oatHeader.offset;
+                        offset += 4;
+
+                        int classCount = readSmallUint(dexOffset + HeaderItem.CLASS_COUNT_OFFSET);
+                        offset += 4 * classCount;
+
+                        index++;
+
+                        return new OatDexFile(dexOffset, filename);
+                    }
+
+                    @Override public void remove() {
+                        throw new UnsupportedOperationException();
+                    }
+                };
+            }
+        };
+    }
+
+    public class OatDexFile extends DexBackedDexFile {
+        @Nonnull public final String filename;
+
+        public OatDexFile(int offset, @Nonnull String filename) {
+            super(opcodes, OatFile.this.buf, offset);
+            this.filename = filename;
+        }
+
+        public int getOatVersion() {
+            return OatFile.this.getOatVersion();
+        }
+
+        @Override public boolean hasOdexOpcodes() {
+            return true;
+        }
+    }
+
+    private class OatHeader {
+        private final int offset;
+
+        public OatHeader(int offset) {
+            this.offset = offset;
+        }
+
+        public boolean isValid() {
+            for (int i=0; i<OAT_MAGIC.length; i++) {
+                if (buf[offset + i] != OAT_MAGIC[i]) {
+                    return false;
+                }
+            }
+
+            for (int i=4; i<7; i++) {
+                if (buf[offset + i] < '0' || buf[offset + i] > '9') {
+                    return false;
+                }
+            }
+
+            return buf[offset + 7] == 0;
+        }
+
+        public int getVersion() {
+            return Integer.valueOf(new String(buf, offset + 4, 3));
+        }
+
+        public int getDexFileCount() {
+            return readSmallUint(offset + 20);
+        }
+
+        public int getKeyValueStoreSize() {
+            int version = getVersion();
+            if (version < 56) {
+                throw new IllegalStateException("Unsupported oat version");
+            }
+            int fieldOffset = 17 * 4;
+            return readSmallUint(offset + fieldOffset);
+        }
+
+        public int getHeaderSize() {
+            int version = getVersion();
+             if (version >= 56) {
+                return 18*4 + getKeyValueStoreSize();
+            } else {
+                throw new IllegalStateException("Unsupported oat version");
+            }
+
+        }
+
+        public int getDexListStart() {
+            return offset + getHeaderSize();
+        }
+    }
+
+    @Nonnull
+    private List<SectionHeader> getSections() {
+        final int offset;
+        final int entrySize;
+        final int entryCount;
+        if (is64bit) {
+            offset = readLongAsSmallUint(40);
+            entrySize = readUshort(58);
+            entryCount = readUshort(60);
+        } else {
+            offset = readSmallUint(32);
+            entrySize = readUshort(46);
+            entryCount = readUshort(48);
+        }
+
+        if (offset + (entrySize * entryCount) > buf.length) {
+            throw new InvalidOatFileException("The ELF section headers extend past the end of the file");
+        }
+
+        return new AbstractList<SectionHeader>() {
+            @Override public SectionHeader get(int index) {
+                if (index < 0 || index >= entryCount) {
+                    throw new IndexOutOfBoundsException();
+                }
+                if (is64bit) {
+                    return new SectionHeader64Bit(offset + (index * entrySize));
+                } else {
+                    return new SectionHeader32Bit(offset + (index * entrySize));
+                }
+            }
+
+            @Override public int size() {
+                return entryCount;
+            }
+        };
+    }
+
+    @Nonnull
+    private SymbolTable getSymbolTable() {
+        for (SectionHeader header: getSections()) {
+            if (header.getType() == SectionHeader.TYPE_DYNAMIC_SYMBOL_TABLE) {
+                return new SymbolTable(header);
+            }
+        }
+        throw new InvalidOatFileException("Oat file has no symbol table");
+    }
+
+    @Nonnull
+    private StringTable getSectionNameStringTable() {
+        int index = readUshort(50);
+        if (index == 0) {
+            throw new InvalidOatFileException("There is no section name string table");
+        }
+
+        try {
+            return new StringTable(getSections().get(index));
+        } catch (IndexOutOfBoundsException ex) {
+            throw new InvalidOatFileException("The section index for the section name string table is invalid");
+        }
+    }
+
+    private abstract class SectionHeader {
+        protected final int offset;
+        public static final int TYPE_DYNAMIC_SYMBOL_TABLE = 11;
+        public SectionHeader(int offset) { this.offset = offset; }
+        @Nonnull public String getName() { return getSectionNameStringTable().getString(readSmallUint(offset)); }
+        public int getType() { return readInt(offset + 4); }
+        public abstract long getAddress();
+        public abstract int getOffset();
+        public abstract int getSize();
+        public abstract int getLink();
+        public abstract int getEntrySize();
+    }
+
+    private class SectionHeader32Bit extends SectionHeader {
+        public SectionHeader32Bit(int offset) { super(offset); }
+        @Override public long getAddress() { return readInt(offset + 12) & 0xFFFFFFFFL; }
+        @Override public int getOffset() { return readSmallUint(offset + 16); }
+        @Override public int getSize() { return readSmallUint(offset + 20); }
+        @Override public int getLink() { return readSmallUint(offset + 24); }
+        @Override public int getEntrySize() { return readSmallUint(offset + 36); }
+    }
+
+    private class SectionHeader64Bit extends SectionHeader {
+        public SectionHeader64Bit(int offset) { super(offset); }
+        @Override public long getAddress() { return readLong(offset + 16); }
+        @Override public int getOffset() { return readLongAsSmallUint(offset + 24); }
+        @Override public int getSize() { return readLongAsSmallUint(offset + 32); }
+        @Override public int getLink() { return readSmallUint(offset + 40); }
+        @Override public int getEntrySize() { return readLongAsSmallUint(offset + 56); }
+    }
+
+    class SymbolTable {
+        @Nonnull private final StringTable stringTable;
+        private final int offset;
+        private final int entryCount;
+        private final int entrySize;
+
+        public SymbolTable(@Nonnull SectionHeader header) {
+            try {
+                this.stringTable = new StringTable(getSections().get(header.getLink()));
+            } catch (IndexOutOfBoundsException ex) {
+                throw new InvalidOatFileException("String table section index is invalid");
+            }
+            this.offset = header.getOffset();
+            this.entrySize = header.getEntrySize();
+            this.entryCount = header.getSize() / entrySize;
+
+            if (offset + entryCount * entrySize > buf.length) {
+                throw new InvalidOatFileException("Symbol table extends past end of file");
+            }
+        }
+
+        @Nonnull
+        public List<Symbol> getSymbols() {
+            return new AbstractList<Symbol>() {
+                @Override public Symbol get(int index) {
+                    if (index < 0 || index >= entryCount) {
+                        throw new IndexOutOfBoundsException();
+                    }
+                    if (is64bit) {
+                        return new Symbol64(offset + index * entrySize);
+                    } else {
+                        return new Symbol32(offset + index * entrySize);
+                    }
+                }
+
+                @Override public int size() {
+                    return entryCount;
+                }
+            };
+        }
+
+        public abstract class Symbol {
+            protected final int offset;
+            public Symbol(int offset) { this.offset = offset; }
+            @Nonnull public abstract String getName();
+            public abstract long getValue();
+            public abstract int getSize();
+            public abstract int getSectionIndex();
+
+            public int getFileOffset() {
+                SectionHeader sectionHeader;
+                try {
+                    sectionHeader = getSections().get(getSectionIndex());
+                } catch (IndexOutOfBoundsException ex) {
+                    throw new InvalidOatFileException("Section index for symbol is out of bounds");
+                }
+
+                long sectionAddress = sectionHeader.getAddress();
+                int sectionOffset = sectionHeader.getOffset();
+                int sectionSize = sectionHeader.getSize();
+
+                long symbolAddress = getValue();
+
+                if (symbolAddress < sectionAddress || symbolAddress >= sectionAddress + sectionSize) {
+                    throw new InvalidOatFileException("symbol address lies outside it's associated section");
+                }
+
+                long fileOffset = (sectionOffset + (getValue() - sectionAddress));
+                assert fileOffset <= Integer.MAX_VALUE;
+                return (int)fileOffset;
+            }
+        }
+
+        public class Symbol32 extends Symbol {
+            public Symbol32(int offset) { super(offset); }
+
+            @Nonnull
+            public String getName() { return stringTable.getString(readSmallUint(offset)); }
+            public long getValue() { return readSmallUint(offset + 4); }
+            public int getSize() { return readSmallUint(offset + 8); }
+            public int getSectionIndex() { return readUshort(offset + 14); }
+        }
+
+        public class Symbol64 extends Symbol {
+            public Symbol64(int offset) { super(offset); }
+
+            @Nonnull
+            public String getName() { return stringTable.getString(readSmallUint(offset)); }
+            public long getValue() { return readLong(offset + 8); }
+            public int getSize() { return readLongAsSmallUint(offset + 16); }
+            public int getSectionIndex() { return readUshort(offset + 6); }
+        }
+    }
+
+    private class StringTable {
+        private final int offset;
+        private final int size;
+
+        public StringTable(@Nonnull SectionHeader header) {
+            this.offset = header.getOffset();
+            this.size = header.getSize();
+
+            if (offset + size > buf.length) {
+                throw new InvalidOatFileException("String table extends past end of file");
+            }
+        }
+
+        @Nonnull
+        public String getString(int index) {
+            if (index >= size) {
+                throw new InvalidOatFileException("String index is out of bounds");
+            }
+
+            int start = offset + index;
+            int end = start;
+            while (buf[end] != 0) {
+                end++;
+                if (end >= offset + size) {
+                    throw new InvalidOatFileException("String extends past end of string table");
+                }
+            }
+
+            return new String(buf, start, end-start, Charset.forName("US-ASCII"));
+        }
+
+    }
+
+    public static class InvalidOatFileException extends RuntimeException {
+        public InvalidOatFileException(String message) {
+            super(message);
+        }
+    }
+
+    public static class NotAnOatFileException extends RuntimeException {
+        public NotAnOatFileException() {}
+    }
+}
\ No newline at end of file
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java
index 3499ead..531ff46 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java
@@ -42,9 +42,14 @@
 public class HeaderItem {
     public static final int ITEM_SIZE = 0x70;
 
+    /**
+     * The magic numbers for dex files.
+     *
+     * They are: "dex\n035\0" and "dex\n037\0".
+     */
     public static final byte[][] MAGIC_VALUES= new byte[][] {
             new byte[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00},
-            new byte[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x36, 0x00}};
+            new byte[]{0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x37, 0x00}};
 
     public static final int LITTLE_ENDIAN_TAG = 0x12345678;
     public static final int BIG_ENDIAN_TAG = 0x78563412;
@@ -225,6 +230,21 @@
         return "Invalid";
     }
 
+
+    /**
+     * Get the higest magic number supported by Android for this api level.
+     * @return The dex file magic number
+     */
+    public static byte[] getMagicForApi(int api) {
+        if (api < 24) {
+            // Prior to Android N we only support dex version 035.
+            return HeaderItem.MAGIC_VALUES[0];
+        } else {
+            // On android N and later we support dex version 037.
+            return HeaderItem.MAGIC_VALUES[1];
+        }
+    }
+
     private static int getVersion(byte[] buf, int offset) {
         if (buf.length - offset < 8) {
             return 0;
@@ -241,7 +261,7 @@
                 }
             }
             if (matches) {
-                return i==0?35:36;
+                return i==0?35:37;
             }
         }
         return 0;
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java
index 1fac80b..204a29d 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java
@@ -59,7 +59,7 @@
 
     @Nonnull
     public byte[] readByteRange(int start, int length) {
-        return Arrays.copyOfRange(getBuf(), start, start+length);
+        return Arrays.copyOfRange(getBuf(), getBaseOffset() + start, getBaseOffset() + start + length);
     }
 
     public int getMapOffset() {
@@ -94,6 +94,7 @@
     }
 
     public void writeAnnotations(@Nonnull Writer out, @Nonnull AnnotatedBytes annotatedBytes) throws IOException {
+        // TODO: need to pass in the offset
         annotatedBytes.writeAnnotations(out, getBuf());
     }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java
index 45707f0..474bfb1 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java
@@ -31,6 +31,8 @@
 
 package org.jf.dexlib2.iface;
 
+import org.jf.dexlib2.Opcodes;
+
 import javax.annotation.Nonnull;
 import java.util.Set;
 
@@ -46,4 +48,11 @@
      * @return A set of the classes defined in this dex file
      */
     @Nonnull Set<? extends ClassDef> getClasses();
+
+    /**
+     * Get the Opcodes associated with this dex file
+     *
+     * @return The Opcodes instance representing the possible opcodes that can be encountered in this dex file
+     */
+    @Nonnull Opcodes getOpcodes();
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java
index e911eb3..2112bd0 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java
@@ -32,6 +32,7 @@
 package org.jf.dexlib2.immutable;
 
 import com.google.common.collect.ImmutableSet;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.iface.ClassDef;
 import org.jf.dexlib2.iface.DexFile;
 import org.jf.util.ImmutableUtils;
@@ -42,21 +43,37 @@
 
 public class ImmutableDexFile implements DexFile {
     @Nonnull protected final ImmutableSet<? extends ImmutableClassDef> classes;
+    @Nonnull private final Opcodes opcodes;
 
+    @Deprecated
     public ImmutableDexFile(@Nullable Collection<? extends ClassDef> classes) {
         this.classes = ImmutableClassDef.immutableSetOf(classes);
+        this.opcodes = Opcodes.forApi(19);
     }
 
+    @Deprecated
     public ImmutableDexFile(@Nullable ImmutableSet<? extends ImmutableClassDef> classes) {
         this.classes = ImmutableUtils.nullToEmptySet(classes);
+        this.opcodes = Opcodes.forApi(19);
+    }
+
+    public ImmutableDexFile(@Nonnull Opcodes opcodes, @Nullable Collection<? extends ClassDef> classes) {
+        this.classes = ImmutableClassDef.immutableSetOf(classes);
+        this.opcodes = opcodes;
+    }
+
+    public ImmutableDexFile(@Nonnull Opcodes opcodes, @Nullable ImmutableSet<? extends ImmutableClassDef> classes) {
+        this.classes = ImmutableUtils.nullToEmptySet(classes);
+        this.opcodes = opcodes;
     }
 
     public static ImmutableDexFile of(DexFile dexFile) {
         if (dexFile instanceof ImmutableDexFile) {
             return (ImmutableDexFile)dexFile;
         }
-        return new ImmutableDexFile(dexFile.getClasses());
+        return new ImmutableDexFile(dexFile.getOpcodes(), dexFile.getClasses());
     }
 
     @Nonnull @Override public ImmutableSet<? extends ImmutableClassDef> getClasses() { return classes; }
+    @Nonnull @Override public Opcodes getOpcodes() { return opcodes; }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java
index e482cd0..839ac09 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java
@@ -31,6 +31,7 @@
 
 package org.jf.dexlib2.rewriter;
 
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.iface.*;
 import org.jf.dexlib2.iface.debug.DebugItem;
 import org.jf.dexlib2.iface.instruction.Instruction;
@@ -116,6 +117,10 @@
         @Override @Nonnull public Set<? extends ClassDef> getClasses() {
             return RewriterUtils.rewriteSet(getClassDefRewriter(), dexFile.getClasses());
         }
+
+        @Nonnull @Override public Opcodes getOpcodes() {
+            return dexFile.getOpcodes();
+        }
     }
 
     @Nonnull @Override public Rewriter<ClassDef> getClassDefRewriter() { return classDefRewriter; }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java b/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
index 631b892..dc978da 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java
@@ -35,6 +35,7 @@
 import org.jf.dexlib2.AccessFlags;
 import org.jf.dexlib2.iface.Method;
 import org.jf.dexlib2.iface.reference.MethodReference;
+import org.jf.util.CharSequenceUtils;
 
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
@@ -68,6 +69,12 @@
         return methodReference.getName().equals("<init>");
     }
 
+    public static boolean isPackagePrivate(@Nonnull Method method) {
+        return (method.getAccessFlags() & (AccessFlags.PRIVATE.getValue() |
+                AccessFlags.PROTECTED.getValue() |
+                AccessFlags.PUBLIC.getValue())) == 0;
+    }
+
     public static int getParameterRegisterCount(@Nonnull Method method) {
         return getParameterRegisterCount(method, MethodUtil.isStatic(method));
     }
@@ -109,5 +116,11 @@
         return sb.toString();
     }
 
+    public static boolean methodSignaturesMatch(@Nonnull MethodReference a, @Nonnull MethodReference b) {
+        return (a.getName().equals(b.getName()) &&
+                a.getReturnType().equals(b.getReturnType()) &&
+                CharSequenceUtils.listEquals(a.getParameterTypes(), b.getParameterTypes()));
+    }
+
     private MethodUtil() {}
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java
index aa428b0..674d518 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java
@@ -33,17 +33,19 @@
 
 package org.jf.dexlib2.util;
 
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.iface.instruction.Instruction;
 import org.jf.dexlib2.iface.instruction.OneRegisterInstruction;
 import org.jf.dexlib2.iface.instruction.WideLiteralInstruction;
 
+import javax.annotation.Nonnull;
 import java.util.List;
 
 public class SyntheticAccessorFSM {
     
-// line 42 "SyntheticAccessorFSM.rl"
+// line 43 "SyntheticAccessorFSM.rl"
     
-// line 47 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
+// line 48 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
 private static byte[] init__SyntheticAccessorFSM_actions_0()
 {
 	return new byte [] {
@@ -187,7 +189,7 @@
 static final int SyntheticAccessorFSM_en_main = 1;
 
 
-// line 43 "SyntheticAccessorFSM.rl"
+// line 44 "SyntheticAccessorFSM.rl"
 
     // math type constants
     public static final int ADD = SyntheticAccessorResolver.ADD_ASSIGNMENT;
@@ -211,7 +213,13 @@
     public static final int NEGATIVE_ONE = -1;
     public static final int OTHER = 0;
 
-    public static int test(List<? extends Instruction> instructions) {
+    @Nonnull private final Opcodes opcodes;
+
+    public SyntheticAccessorFSM(@Nonnull Opcodes opcodes) {
+        this.opcodes = opcodes;
+    }
+
+    public int test(List<? extends Instruction> instructions) {
         int accessorType = -1;
         int cs, p = 0;
         int pe = instructions.size();
@@ -231,12 +239,12 @@
         int returnRegister = -1;
 
         
-// line 235 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
+// line 242 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
 	{
 	cs = SyntheticAccessorFSM_start;
 	}
 
-// line 240 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
+// line 247 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
 	{
 	int _klen;
 	int _trans = 0;
@@ -270,9 +278,9 @@
 				break;
 
 			_mid = _lower + ((_upper-_lower) >> 1);
-			if ( ( instructions.get(p).getOpcode().value) < _SyntheticAccessorFSM_trans_keys[_mid] )
+			if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) < _SyntheticAccessorFSM_trans_keys[_mid] )
 				_upper = _mid - 1;
-			else if ( ( instructions.get(p).getOpcode().value) > _SyntheticAccessorFSM_trans_keys[_mid] )
+			else if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) > _SyntheticAccessorFSM_trans_keys[_mid] )
 				_lower = _mid + 1;
 			else {
 				_trans += (_mid - _keys);
@@ -293,9 +301,9 @@
 				break;
 
 			_mid = _lower + (((_upper-_lower) >> 1) & ~1);
-			if ( ( instructions.get(p).getOpcode().value) < _SyntheticAccessorFSM_trans_keys[_mid] )
+			if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) < _SyntheticAccessorFSM_trans_keys[_mid] )
 				_upper = _mid - 2;
-			else if ( ( instructions.get(p).getOpcode().value) > _SyntheticAccessorFSM_trans_keys[_mid+1] )
+			else if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) > _SyntheticAccessorFSM_trans_keys[_mid+1] )
 				_lower = _mid + 2;
 			else {
 				_trans += ((_mid - _keys)>>1);
@@ -317,19 +325,19 @@
 			switch ( _SyntheticAccessorFSM_actions[_acts++] )
 			{
 	case 0:
-// line 93 "SyntheticAccessorFSM.rl"
+// line 100 "SyntheticAccessorFSM.rl"
 	{
                 putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
             }
 	break;
 	case 1:
-// line 100 "SyntheticAccessorFSM.rl"
+// line 107 "SyntheticAccessorFSM.rl"
 	{
                 constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral();
             }
 	break;
 	case 2:
-// line 104 "SyntheticAccessorFSM.rl"
+// line 111 "SyntheticAccessorFSM.rl"
 	{
                 mathType = INT;
                 mathOp = ADD;
@@ -337,146 +345,146 @@
             }
 	break;
 	case 3:
-// line 110 "SyntheticAccessorFSM.rl"
+// line 117 "SyntheticAccessorFSM.rl"
 	{ mathType = INT; }
 	break;
 	case 4:
-// line 111 "SyntheticAccessorFSM.rl"
+// line 118 "SyntheticAccessorFSM.rl"
 	{ mathType = LONG; }
 	break;
 	case 5:
-// line 112 "SyntheticAccessorFSM.rl"
+// line 119 "SyntheticAccessorFSM.rl"
 	{ mathType = FLOAT; }
 	break;
 	case 6:
-// line 113 "SyntheticAccessorFSM.rl"
+// line 120 "SyntheticAccessorFSM.rl"
 	{mathType = DOUBLE; }
 	break;
 	case 7:
-// line 113 "SyntheticAccessorFSM.rl"
+// line 120 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = ADD;
             }
 	break;
 	case 8:
-// line 116 "SyntheticAccessorFSM.rl"
+// line 123 "SyntheticAccessorFSM.rl"
 	{ mathType = INT; }
 	break;
 	case 9:
-// line 117 "SyntheticAccessorFSM.rl"
+// line 124 "SyntheticAccessorFSM.rl"
 	{ mathType = LONG; }
 	break;
 	case 10:
-// line 118 "SyntheticAccessorFSM.rl"
+// line 125 "SyntheticAccessorFSM.rl"
 	{ mathType = FLOAT; }
 	break;
 	case 11:
-// line 119 "SyntheticAccessorFSM.rl"
+// line 126 "SyntheticAccessorFSM.rl"
 	{mathType = DOUBLE; }
 	break;
 	case 12:
-// line 119 "SyntheticAccessorFSM.rl"
+// line 126 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = SUB;
             }
 	break;
 	case 13:
-// line 123 "SyntheticAccessorFSM.rl"
+// line 130 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = MUL;
             }
 	break;
 	case 14:
-// line 127 "SyntheticAccessorFSM.rl"
+// line 134 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = DIV;
             }
 	break;
 	case 15:
-// line 131 "SyntheticAccessorFSM.rl"
+// line 138 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = REM;
             }
 	break;
 	case 16:
-// line 134 "SyntheticAccessorFSM.rl"
+// line 141 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = AND;
             }
 	break;
 	case 17:
-// line 137 "SyntheticAccessorFSM.rl"
+// line 144 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = OR;
             }
 	break;
 	case 18:
-// line 140 "SyntheticAccessorFSM.rl"
+// line 147 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = XOR;
             }
 	break;
 	case 19:
-// line 143 "SyntheticAccessorFSM.rl"
+// line 150 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = SHL;
             }
 	break;
 	case 20:
-// line 146 "SyntheticAccessorFSM.rl"
+// line 153 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = SHR;
             }
 	break;
 	case 21:
-// line 149 "SyntheticAccessorFSM.rl"
+// line 156 "SyntheticAccessorFSM.rl"
 	{
                 mathOp = USHR;
             }
 	break;
 	case 22:
-// line 155 "SyntheticAccessorFSM.rl"
+// line 162 "SyntheticAccessorFSM.rl"
 	{
                 returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
             }
 	break;
 	case 23:
-// line 161 "SyntheticAccessorFSM.rl"
+// line 168 "SyntheticAccessorFSM.rl"
 	{
                 accessorType = SyntheticAccessorResolver.GETTER; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
 	case 24:
-// line 165 "SyntheticAccessorFSM.rl"
+// line 172 "SyntheticAccessorFSM.rl"
 	{
                 accessorType = SyntheticAccessorResolver.SETTER; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
 	case 25:
-// line 169 "SyntheticAccessorFSM.rl"
+// line 176 "SyntheticAccessorFSM.rl"
 	{
                 accessorType = SyntheticAccessorResolver.METHOD; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
 	case 26:
-// line 173 "SyntheticAccessorFSM.rl"
+// line 180 "SyntheticAccessorFSM.rl"
 	{
                 accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
             }
 	break;
 	case 27:
-// line 177 "SyntheticAccessorFSM.rl"
+// line 184 "SyntheticAccessorFSM.rl"
 	{
                 accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
             }
 	break;
 	case 28:
-// line 185 "SyntheticAccessorFSM.rl"
+// line 192 "SyntheticAccessorFSM.rl"
 	{
                 accessorType = mathOp; { p += 1; _goto_targ = 5; if (true)  continue _goto;}
             }
 	break;
-// line 480 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
+// line 487 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
 			}
 		}
 	}
@@ -496,7 +504,7 @@
 	break; }
 	}
 
-// line 198 "SyntheticAccessorFSM.rl"
+// line 205 "SyntheticAccessorFSM.rl"
 
 
         return accessorType;
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java
index a46a18f..7808f84 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java
@@ -35,6 +35,7 @@
 import com.google.common.collect.ImmutableMap;
 import com.google.common.collect.Maps;
 import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.iface.ClassDef;
 import org.jf.dexlib2.iface.Method;
 import org.jf.dexlib2.iface.MethodImplementation;
@@ -68,10 +69,12 @@
     public static final int SHR_ASSIGNMENT = 16;
     public static final int USHR_ASSIGNMENT = 17;
 
+    private final SyntheticAccessorFSM syntheticAccessorFSM;
     private final Map<String, ClassDef> classDefMap;
     private final Map<String, AccessedMember> resolvedAccessors = Maps.newConcurrentMap();
 
-    public SyntheticAccessorResolver(Iterable<? extends ClassDef> classDefs) {
+    public SyntheticAccessorResolver(@Nonnull Opcodes opcodes, @Nonnull Iterable<? extends ClassDef> classDefs) {
+        this.syntheticAccessorFSM = new SyntheticAccessorFSM(opcodes);
         ImmutableMap.Builder<String, ClassDef> builder = ImmutableMap.builder();
 
         for (ClassDef classDef: classDefs) {
@@ -124,7 +127,8 @@
 
         List<Instruction> instructions = ImmutableList.copyOf(matchedMethodImpl.getInstructions());
 
-        int accessType = SyntheticAccessorFSM.test(instructions);
+
+        int accessType = syntheticAccessorFSM.test(instructions);
 
         if (accessType >= 0) {
             AccessedMember member =
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java b/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
index 02890b8..6be21af 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java
@@ -31,6 +31,8 @@
 
 package org.jf.dexlib2.util;
 
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.iface.ClassDef;
 import org.jf.dexlib2.iface.reference.TypeReference;
 
 import javax.annotation.Nonnull;
@@ -49,5 +51,24 @@
         return type.length() == 1;
     }
 
+    @Nonnull
+    public static String getPackage(@Nonnull String type) {
+        int lastSlash = type.lastIndexOf('/');
+        if (lastSlash < 0) {
+            return "";
+        }
+        return type.substring(1, lastSlash);
+    }
+
+    public static boolean canAccessClass(@Nonnull String accessorType, @Nonnull ClassDef accesseeClassDef) {
+        if (AccessFlags.PUBLIC.isSet(accesseeClassDef.getAccessFlags())) {
+            return true;
+        }
+
+        // Classes can only be public or package private. Any private or protected inner classes are actually
+        // package private.
+        return getPackage(accesseeClassDef.getType()).equals(getPackage(accessorType));
+    }
+
     private TypeUtils() {}
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java
index be23978..4e81f7f 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java
@@ -37,6 +37,7 @@
 import com.google.common.collect.Ordering;
 import org.jf.dexlib2.AccessFlags;
 import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.ReferenceType;
 import org.jf.dexlib2.base.BaseAnnotation;
 import org.jf.dexlib2.base.BaseAnnotationElement;
@@ -94,7 +95,7 @@
     public static final int NO_INDEX = -1;
     public static final int NO_OFFSET = 0;
 
-    protected final int api;
+    protected final Opcodes opcodes;
 
     protected int stringIndexSectionOffset = NO_OFFSET;
     protected int typeSectionOffset = NO_OFFSET;
@@ -134,7 +135,7 @@
     protected final AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, EncodedValue> annotationSection;
     protected final AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection;
 
-    protected DexWriter(int api,
+    protected DexWriter(Opcodes opcodes,
                         StringSection<StringKey, StringRef> stringSection,
                         TypeSection<StringKey, TypeKey, TypeRef> typeSection,
                         ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection,
@@ -146,7 +147,8 @@
                         AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement,
                                 EncodedValue> annotationSection,
                         AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection) {
-        this.api = api;
+        this.opcodes = opcodes;
+
         this.stringSection = stringSection;
         this.typeSection = typeSection;
         this.protoSection = protoSection;
@@ -943,7 +945,7 @@
             writer.writeInt(debugItemOffset);
 
             InstructionWriter instructionWriter =
-                    InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
+                    InstructionWriter.makeInstructionWriter(opcodes, writer, stringSection, typeSection, fieldSection,
                             methodSection);
 
             writer.writeInt(codeUnitCount);
@@ -1219,8 +1221,8 @@
     }
 
     private void writeHeader(@Nonnull DexDataWriter writer, int dataOffset, int fileSize) throws IOException {
-        // always write the 035 version, there's no reason to use the 036 version for now
-        writer.write(HeaderItem.MAGIC_VALUES[0]);
+        // Write the appropriate header.
+        writer.write(HeaderItem.getMagicForApi(opcodes.api));
 
         // checksum placeholder
         writer.writeInt(0);
@@ -1266,6 +1268,6 @@
         // Workaround for a crash in Dalvik VM before Jelly Bean MR1 (4.2)
         // which is triggered by NO_OFFSET in parameter annotation list.
         // (https://code.google.com/p/android/issues/detail?id=35304)
-        return (api < 17);
+        return (opcodes.api < 17);
     }
 }
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java
index c9aa73a..f16256c 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java
@@ -33,6 +33,8 @@
 
 import com.google.common.collect.Ordering;
 import com.google.common.primitives.Ints;
+import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.ReferenceType;
 import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
 import org.jf.dexlib2.iface.instruction.SwitchElement;
@@ -50,6 +52,7 @@
 
 public class InstructionWriter<StringRef extends StringReference, TypeRef extends TypeReference,
         FieldRefKey extends FieldReference, MethodRefKey extends MethodReference> {
+    @Nonnull private final Opcodes opcodes;
     @Nonnull private final DexDataWriter writer;
     @Nonnull private final StringSection<?, StringRef> stringSection;
     @Nonnull private final TypeSection<?, ?, TypeRef> typeSection;
@@ -59,20 +62,23 @@
     @Nonnull static <StringRef extends StringReference, TypeRef extends TypeReference, FieldRefKey extends FieldReference, MethodRefKey extends MethodReference>
             InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey>
             makeInstructionWriter(
+                @Nonnull Opcodes opcodes,
                 @Nonnull DexDataWriter writer,
                 @Nonnull StringSection<?, StringRef> stringSection,
                 @Nonnull TypeSection<?, ?, TypeRef> typeSection,
                 @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection,
                 @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection) {
         return new InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey>(
-                writer, stringSection, typeSection, fieldSection, methodSection);
+                opcodes, writer, stringSection, typeSection, fieldSection, methodSection);
     }
 
-    InstructionWriter(@Nonnull DexDataWriter writer,
+    InstructionWriter(@Nonnull Opcodes opcodes,
+                      @Nonnull DexDataWriter writer,
                       @Nonnull StringSection<?, StringRef> stringSection,
                       @Nonnull TypeSection<?, ?, TypeRef> typeSection,
                       @Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection,
                       @Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection) {
+        this.opcodes = opcodes;
         this.writer = writer;
         this.stringSection = stringSection;
         this.typeSection = typeSection;
@@ -80,9 +86,17 @@
         this.methodSection = methodSection;
     }
 
+    private short getOpcodeValue(Opcode opcode) {
+        Short value = opcodes.getOpcodeValue(opcode);
+        if (value == null) {
+            throw new ExceptionWithContext("Instruction %s is invalid for api %d", opcode.name, opcodes.api);
+        }
+        return value;
+    }
+
     public void write(@Nonnull Instruction10t instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getCodeOffset());
         } catch (IOException ex) {
             throw new RuntimeException(ex);
@@ -91,7 +105,7 @@
 
     public void write(@Nonnull Instruction10x instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(0);
         } catch (IOException ex) {
             throw new RuntimeException(ex);
@@ -100,7 +114,7 @@
 
     public void write(@Nonnull Instruction11n instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral()));
         } catch (IOException ex) {
             throw new RuntimeException(ex);
@@ -109,7 +123,7 @@
 
     public void write(@Nonnull Instruction11x instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
         } catch (IOException ex) {
             throw new RuntimeException(ex);
@@ -118,7 +132,7 @@
 
     public void write(@Nonnull Instruction12x instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
         } catch (IOException ex) {
             throw new RuntimeException(ex);
@@ -127,7 +141,7 @@
 
     public void write(@Nonnull Instruction20bc instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getVerificationError());
             writer.writeUshort(getReferenceIndex(instruction));
         } catch (IOException ex) {
@@ -137,7 +151,7 @@
 
     public void write(@Nonnull Instruction20t instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(0);
             writer.writeShort(instruction.getCodeOffset());
         } catch (IOException ex) {
@@ -147,7 +161,7 @@
 
     public void write(@Nonnull Instruction21c instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeUshort(getReferenceIndex(instruction));
         } catch (IOException ex) {
@@ -157,7 +171,7 @@
 
     public void write(@Nonnull Instruction21ih instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeShort(instruction.getHatLiteral());
         } catch (IOException ex) {
@@ -167,7 +181,7 @@
 
     public void write(@Nonnull Instruction21lh instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeShort(instruction.getHatLiteral());
         } catch (IOException ex) {
@@ -177,7 +191,7 @@
 
     public void write(@Nonnull Instruction21s instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeShort(instruction.getNarrowLiteral());
         } catch (IOException ex) {
@@ -187,7 +201,7 @@
 
     public void write(@Nonnull Instruction21t instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeShort(instruction.getCodeOffset());
         } catch (IOException ex) {
@@ -197,7 +211,7 @@
 
     public void write(@Nonnull Instruction22b instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.write(instruction.getRegisterB());
             writer.write(instruction.getNarrowLiteral());
@@ -208,7 +222,7 @@
 
     public void write(@Nonnull Instruction22c instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
             writer.writeUshort(getReferenceIndex(instruction));
         } catch (IOException ex) {
@@ -218,7 +232,7 @@
 
     public void write(@Nonnull Instruction22s instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
             writer.writeShort(instruction.getNarrowLiteral());
         } catch (IOException ex) {
@@ -228,7 +242,7 @@
 
     public void write(@Nonnull Instruction22t instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
             writer.writeShort(instruction.getCodeOffset());
         } catch (IOException ex) {
@@ -238,7 +252,7 @@
 
     public void write(@Nonnull Instruction22x instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeUshort(instruction.getRegisterB());
         } catch (IOException ex) {
@@ -248,7 +262,7 @@
 
     public void write(@Nonnull Instruction23x instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.write(instruction.getRegisterB());
             writer.write(instruction.getRegisterC());
@@ -259,7 +273,7 @@
 
     public void write(@Nonnull Instruction30t instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(0);
             writer.writeInt(instruction.getCodeOffset());
         } catch (IOException ex) {
@@ -269,7 +283,7 @@
 
     public void write(@Nonnull Instruction31c instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeInt(getReferenceIndex(instruction));
         } catch (IOException ex) {
@@ -279,7 +293,7 @@
 
     public void write(@Nonnull Instruction31i instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeInt(instruction.getNarrowLiteral());
         } catch (IOException ex) {
@@ -289,7 +303,7 @@
 
     public void write(@Nonnull Instruction31t instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeInt(instruction.getCodeOffset());
         } catch (IOException ex) {
@@ -299,7 +313,7 @@
 
     public void write(@Nonnull Instruction32x instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(0);
             writer.writeUshort(instruction.getRegisterA());
             writer.writeUshort(instruction.getRegisterB());
@@ -310,7 +324,7 @@
 
     public void write(@Nonnull Instruction35c instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount()));
             writer.writeUshort(getReferenceIndex(instruction));
             writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD()));
@@ -322,7 +336,7 @@
 
     public void write(@Nonnull Instruction25x instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(packNibbles(
                     instruction.getRegisterParameterG(), instruction.getParameterRegisterCount()));
             writer.write(packNibbles(
@@ -335,7 +349,7 @@
     }
     public void write(@Nonnull Instruction3rc instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterCount());
             writer.writeUshort(getReferenceIndex(instruction));
             writer.writeUshort(instruction.getStartRegister());
@@ -346,7 +360,7 @@
 
     public void write(@Nonnull Instruction51l instruction) {
         try {
-            writer.write(instruction.getOpcode().value);
+            writer.write(getOpcodeValue(instruction.getOpcode()));
             writer.write(instruction.getRegisterA());
             writer.writeLong(instruction.getWideLiteral());
         } catch (IOException ex) {
@@ -356,7 +370,7 @@
 
     public void write(@Nonnull ArrayPayload instruction) {
         try {
-            writer.writeUshort(instruction.getOpcode().value);
+            writer.writeUshort(getOpcodeValue(instruction.getOpcode()));
             writer.writeUshort(instruction.getElementWidth());
             List<Number> elements = instruction.getArrayElements();
             writer.writeInt(elements.size());
@@ -393,7 +407,7 @@
     public void write(@Nonnull SparseSwitchPayload instruction) {
         try {
             writer.writeUbyte(0);
-            writer.writeUbyte(instruction.getOpcode().value >> 8);
+            writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8);
             List<? extends SwitchElement> elements = Ordering.from(switchElementComparator).immutableSortedCopy(
                     instruction.getSwitchElements());
             writer.writeUshort(elements.size());
@@ -417,7 +431,7 @@
     public void write(@Nonnull PackedSwitchPayload instruction) {
         try {
             writer.writeUbyte(0);
-            writer.writeUbyte(instruction.getOpcode().value >> 8);
+            writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8);
             List<? extends SwitchElement> elements = instruction.getSwitchElements();
             writer.writeUshort(elements.size());
             if (elements.size() == 0) {
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java
index 9a727b2..d119024 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java
@@ -34,8 +34,8 @@
 import com.google.common.base.Function;
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Iterators;
-import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.ValueType;
 import org.jf.dexlib2.iface.Annotation;
 import org.jf.dexlib2.iface.MethodImplementation;
@@ -49,7 +49,6 @@
 import javax.annotation.Nonnull;
 import javax.annotation.Nullable;
 import java.io.IOException;
-import java.util.Collections;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
@@ -59,20 +58,27 @@
         BuilderClassDef, BuilderAnnotation, BuilderAnnotationSet, BuilderTypeList, BuilderField, BuilderMethod,
         BuilderEncodedValue, BuilderAnnotationElement> {
 
-    private final BuilderContext context;
+    @Nonnull private final BuilderContext context;
 
-    public static DexBuilder makeDexBuilder() {
+    @Nonnull public static DexBuilder makeDexBuilder() {
         BuilderContext context = new BuilderContext();
-        return new DexBuilder(15, context);
+        return new DexBuilder(Opcodes.forApi(20), context);
     }
 
+    @Deprecated
+    @Nonnull
     public static DexBuilder makeDexBuilder(int api) {
         BuilderContext context = new BuilderContext();
-        return new DexBuilder(api, context);
+        return new DexBuilder(Opcodes.forApi(api), context);
     }
 
-    private DexBuilder(int api, @Nonnull BuilderContext context) {
-        super(api, context.stringPool, context.typePool, context.protoPool,
+    @Nonnull public static DexBuilder makeDexBuilder(@Nonnull Opcodes opcodes) {
+        BuilderContext context = new BuilderContext();
+        return new DexBuilder(opcodes, context);
+    }
+
+    private DexBuilder(@Nonnull Opcodes opcodes, @Nonnull BuilderContext context) {
+        super(opcodes, context.stringPool, context.typePool, context.protoPool,
                 context.fieldPool, context.methodPool, context.classPool, context.typeListPool, context.annotationPool,
                 context.annotationSetPool);
         this.context = context;
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java
index d7f6392..27d8044 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java
@@ -31,6 +31,7 @@
 
 package org.jf.dexlib2.writer.pool;
 
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.ValueType;
 import org.jf.dexlib2.iface.Annotation;
 import org.jf.dexlib2.iface.AnnotationElement;
@@ -56,11 +57,19 @@
         TypeListPool.Key<? extends Collection<? extends CharSequence>>, Field, PoolMethod,
         EncodedValue, AnnotationElement> {
 
+    @Nonnull
     public static DexPool makeDexPool() {
-        return makeDexPool(15);
+        return makeDexPool(Opcodes.forApi(20));
     }
 
+    @Deprecated
+    @Nonnull
     public static DexPool makeDexPool(int api) {
+        return makeDexPool(Opcodes.forApi(api));
+    }
+
+    @Nonnull
+    public static DexPool makeDexPool(@Nonnull Opcodes opcodes) {
         StringPool stringPool = new StringPool();
         TypePool typePool = new TypePool(stringPool);
         FieldPool fieldPool = new FieldPool(stringPool, typePool);
@@ -72,14 +81,14 @@
         ClassPool classPool = new ClassPool(stringPool, typePool, fieldPool, methodPool, annotationSetPool,
                 typeListPool);
 
-        return new DexPool(api, stringPool, typePool, protoPool, fieldPool, methodPool, classPool, typeListPool,
+        return new DexPool(opcodes, stringPool, typePool, protoPool, fieldPool, methodPool, classPool, typeListPool,
                 annotationPool, annotationSetPool);
     }
 
-    private DexPool(int api, StringPool stringPool, TypePool typePool, ProtoPool protoPool, FieldPool fieldPool,
+    private DexPool(Opcodes opcodes, StringPool stringPool, TypePool typePool, ProtoPool protoPool, FieldPool fieldPool,
                     MethodPool methodPool, ClassPool classPool, TypeListPool typeListPool,
                     AnnotationPool annotationPool, AnnotationSetPool annotationSetPool) {
-        super(api, stringPool, typePool, protoPool, fieldPool, methodPool,
+        super(opcodes, stringPool, typePool, protoPool, fieldPool, methodPool,
                 classPool, typeListPool, annotationPool, annotationSetPool);
     }
 
diff --git a/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
index c46f9ba..96ac536 100644
--- a/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
+++ b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
@@ -34,6 +34,7 @@
 import org.jf.dexlib2.iface.instruction.Instruction;
 import org.jf.dexlib2.iface.instruction.OneRegisterInstruction;
 import org.jf.dexlib2.iface.instruction.WideLiteralInstruction;
+import org.jf.dexlib2.Opcodes;
 
 import java.util.List;
 
@@ -63,7 +64,13 @@
     public static final int NEGATIVE_ONE = -1;
     public static final int OTHER = 0;
 
-    public static int test(List<? extends Instruction> instructions) {
+    @Nonnull private final Opcodes opcodes;
+
+    public SyntheticAccessorFSM(@Nonnull Opcodes opcodes) {
+        this.opcodes = opcodes;
+    }
+
+    public int test(List<? extends Instruction> instructions) {
         int accessorType = -1;
         int cs, p = 0;
         int pe = instructions.size();
@@ -85,7 +92,7 @@
         %%{
             import "Opcodes.rl";
             alphtype short;
-            getkey instructions.get(p).getOpcode().value;
+            getkey opcodes.getOpcodeValue(instructions.get(p).getOpcode());
 
             get = (0x52 .. 0x58) | (0x60 .. 0x66); # all igets/sgets
 
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java b/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java
index 924d3fd..4c8f85b 100644
--- a/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java
+++ b/dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java
@@ -81,7 +81,7 @@
         Assert.assertNotNull(url);
         DexFile f = DexFileFactory.loadDexFile(url.getFile(), 15, false);
 
-        SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getClasses());
+        SyntheticAccessorResolver sar = new SyntheticAccessorResolver(f.getOpcodes(), f.getClasses());
 
         ClassDef accessorTypesClass = null;
         ClassDef accessorsClass = null;
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java
index f3b1094..3f1ee56 100644
--- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java
+++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java
@@ -33,6 +33,7 @@
 
 import com.google.common.collect.ImmutableSet;
 import junit.framework.Assert;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.immutable.ImmutableDexFile;
 import org.junit.Test;
 
@@ -53,39 +54,42 @@
     private final ClassPath classPath;
 
     public CommonSuperclassTest() throws IOException {
-        classPath = new ClassPath(new ImmutableDexFile(ImmutableSet.of(
-                TestUtils.makeClassDef("Ljava/lang/Object;", null),
-                TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;"),
-                TestUtils.makeClassDef("Ltest/two;", "Ljava/lang/Object;"),
-                TestUtils.makeClassDef("Ltest/onetwo;", "Ltest/one;"),
-                TestUtils.makeClassDef("Ltest/onetwothree;", "Ltest/onetwo;"),
-                TestUtils.makeClassDef("Ltest/onethree;", "Ltest/one;"),
-                TestUtils.makeClassDef("Ltest/fivetwo;", "Ltest/five;"),
-                TestUtils.makeClassDef("Ltest/fivetwothree;", "Ltest/fivetwo;"),
-                TestUtils.makeClassDef("Ltest/fivethree;", "Ltest/five;"),
-                TestUtils.makeInterfaceDef("Ljava/lang/Cloneable;"),
-                TestUtils.makeInterfaceDef("Ljava/io/Serializable;"),
+        classPath = new ClassPath(new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19),
+                ImmutableSet.of(
+                        TestUtils.makeClassDef("Ljava/lang/Object;", null),
+                        TestUtils.makeClassDef("Ltest/one;", "Ljava/lang/Object;"),
+                        TestUtils.makeClassDef("Ltest/two;", "Ljava/lang/Object;"),
+                        TestUtils.makeClassDef("Ltest/onetwo;", "Ltest/one;"),
+                        TestUtils.makeClassDef("Ltest/onetwothree;", "Ltest/onetwo;"),
+                        TestUtils.makeClassDef("Ltest/onethree;", "Ltest/one;"),
+                        TestUtils.makeClassDef("Ltest/fivetwo;", "Ltest/five;"),
+                        TestUtils.makeClassDef("Ltest/fivetwothree;", "Ltest/fivetwo;"),
+                        TestUtils.makeClassDef("Ltest/fivethree;", "Ltest/five;"),
+                        TestUtils.makeInterfaceDef("Ljava/lang/Cloneable;"),
+                        TestUtils.makeInterfaceDef("Ljava/io/Serializable;"),
 
-                // basic class and interface
-                TestUtils.makeClassDef("Liface/classiface1;", "Ljava/lang/Object;", "Liface/iface1;"),
-                TestUtils.makeInterfaceDef("Liface/iface1;"),
+                        // basic class and interface
+                        TestUtils.makeClassDef("Liface/classiface1;", "Ljava/lang/Object;", "Liface/iface1;"),
+                        TestUtils.makeInterfaceDef("Liface/iface1;"),
 
-                // a more complex interface tree
-                TestUtils.makeInterfaceDef("Liface/base1;"),
-                // implements undefined interface
-                TestUtils.makeInterfaceDef("Liface/sub1;", "Liface/base1;", "Liface/base2;"),
-                // this implements sub1, so that its interfaces can't be fully resolved either
-                TestUtils.makeInterfaceDef("Liface/sub2;", "Liface/base1;", "Liface/sub1;"),
-                TestUtils.makeInterfaceDef("Liface/sub3;", "Liface/base1;"),
-                TestUtils.makeInterfaceDef("Liface/sub4;", "Liface/base1;", "Liface/sub3;"),
-                TestUtils.makeClassDef("Liface/classsub1;", "Ljava/lang/Object;", "Liface/sub1;"),
-                TestUtils.makeClassDef("Liface/classsub2;", "Ljava/lang/Object;", "Liface/sub2;"),
-                TestUtils.makeClassDef("Liface/classsub3;", "Ljava/lang/Object;", "Liface/sub3;", "Liface/base;"),
-                TestUtils.makeClassDef("Liface/classsub4;", "Ljava/lang/Object;", "Liface/sub3;", "Liface/sub4;"),
-                TestUtils.makeClassDef("Liface/classsubsub4;", "Liface/classsub4;"),
-                TestUtils.makeClassDef("Liface/classsub1234;", "Ljava/lang/Object;", "Liface/sub1;", "Liface/sub2;",
-                        "Liface/sub3;", "Liface/sub4;")
-        )));
+                        // a more complex interface tree
+                        TestUtils.makeInterfaceDef("Liface/base1;"),
+                        // implements undefined interface
+                        TestUtils.makeInterfaceDef("Liface/sub1;", "Liface/base1;", "Liface/base2;"),
+                        // this implements sub1, so that its interfaces can't be fully resolved either
+                        TestUtils.makeInterfaceDef("Liface/sub2;", "Liface/base1;", "Liface/sub1;"),
+                        TestUtils.makeInterfaceDef("Liface/sub3;", "Liface/base1;"),
+                        TestUtils.makeInterfaceDef("Liface/sub4;", "Liface/base1;", "Liface/sub3;"),
+                        TestUtils.makeClassDef("Liface/classsub1;", "Ljava/lang/Object;", "Liface/sub1;"),
+                        TestUtils.makeClassDef("Liface/classsub2;", "Ljava/lang/Object;", "Liface/sub2;"),
+                        TestUtils.makeClassDef("Liface/classsub3;", "Ljava/lang/Object;", "Liface/sub3;",
+                                "Liface/base;"),
+                        TestUtils.makeClassDef("Liface/classsub4;", "Ljava/lang/Object;", "Liface/sub3;",
+                                "Liface/sub4;"),
+                        TestUtils.makeClassDef("Liface/classsubsub4;", "Liface/classsub4;"),
+                        TestUtils.makeClassDef("Liface/classsub1234;", "Ljava/lang/Object;", "Liface/sub1;",
+                                "Liface/sub2;", "Liface/sub3;", "Liface/sub4;")
+        ))));
     }
 
     public void superclassTest(String commonSuperclass,
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java
index 65a82f0..90a6359 100644
--- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java
+++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java
@@ -35,6 +35,7 @@
 import com.google.common.collect.Lists;
 import org.jf.dexlib2.AccessFlags;
 import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.iface.ClassDef;
 import org.jf.dexlib2.iface.DexFile;
 import org.jf.dexlib2.iface.instruction.Instruction;
@@ -66,12 +67,12 @@
         ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
                 null, null, null, null, null, ImmutableList.of(method));
 
-        DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
+        DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
 
         ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
                 15, false);
         InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
-        MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
+        MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
 
         Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
         Assert.assertEquals(Opcode.INVOKE_VIRTUAL, deodexedInstruction.getOpcode());
@@ -93,12 +94,12 @@
         ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
                 null, null, null, null, ImmutableList.of(method), null);
 
-        DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
+        DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
 
         ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
                 15, false);
         InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
-        MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
+        MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
 
         Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
         Assert.assertEquals(Opcode.INVOKE_STATIC, deodexedInstruction.getOpcode());
@@ -120,12 +121,12 @@
         ClassDef classDef = new ImmutableClassDef("Lblah;", AccessFlags.PUBLIC.getValue(), "Ljava/lang/Object;", null,
                 null, null, null, null, ImmutableList.of(method), null);
 
-        DexFile dexFile = new ImmutableDexFile(ImmutableList.of(classDef));
+        DexFile dexFile = new ImmutableDexFile(Opcodes.forApi(19), ImmutableList.of(classDef));
 
         ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
                 15, false);
         InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
-        MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
+        MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
 
         Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
         Assert.assertEquals(Opcode.INVOKE_DIRECT, deodexedInstruction.getOpcode());
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java b/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java
index 59b0d27..84cd284 100644
--- a/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java
+++ b/dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java
@@ -34,7 +34,9 @@
 import com.google.common.collect.ImmutableList;
 import com.google.common.collect.ImmutableSet;
 import junit.framework.Assert;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.analysis.ClassPath;
+import org.jf.dexlib2.analysis.DexClassProvider;
 import org.jf.dexlib2.analysis.TestUtils;
 import org.jf.dexlib2.analysis.TypeProto;
 import org.jf.dexlib2.iface.ClassDef;
@@ -45,7 +47,6 @@
 
 public class SuperclassChainTest {
 
-
     @Test
     public void testGetSuperclassChain() throws IOException {
         ClassDef objectClassDef = TestUtils.makeClassDef("Ljava/lang/Object;", null);
@@ -56,7 +57,7 @@
         ImmutableSet<ClassDef> classes = ImmutableSet.<ClassDef>of(
                 objectClassDef, oneClassDef, twoClassDef, threeClassDef);
 
-        ClassPath classPath = new ClassPath(new ImmutableDexFile(classes));
+        ClassPath classPath = new ClassPath(new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19), classes)));
 
         TypeProto objectClassProto = classPath.getClass("Ljava/lang/Object;");
         TypeProto oneClassProto = classPath.getClass("Ltest/one;");
@@ -87,7 +88,7 @@
         ClassDef twoClassDef = TestUtils.makeClassDef("Ltest/two;", "Ltest/one;");
         ClassDef threeClassDef = TestUtils.makeClassDef("Ltest/three;", "Ltest/two;");
         ImmutableSet<ClassDef> classes = ImmutableSet.<ClassDef>of(twoClassDef, threeClassDef);
-        ClassPath classPath = new ClassPath(new ImmutableDexFile(classes));
+        ClassPath classPath = new ClassPath(new DexClassProvider(new ImmutableDexFile(Opcodes.forApi(19), classes)));
 
         TypeProto unknownClassProto = classPath.getUnknownClass();
         TypeProto oneClassProto = classPath.getClass("Ltest/one;");
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java b/dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java
new file mode 100644
index 0000000..0df5ab3
--- /dev/null
+++ b/dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.dexlib2.builder;
+
+import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.builder.instruction.BuilderInstruction10x;
+import org.jf.dexlib2.builder.instruction.BuilderInstruction32x;
+import org.jf.dexlib2.iface.MethodImplementation;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class MutableMethodImplementationTest {
+
+    @Test
+    public void testTryEndAtEndOfMethod() {
+        MethodImplementationBuilder builder = new MethodImplementationBuilder(10);
+
+        Label startLabel = builder.addLabel("start");
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction32x(Opcode.MOVE_16, 0, 0));
+        Label endLabel = builder.addLabel("end");
+
+        builder.addCatch(startLabel, endLabel, startLabel);
+
+        MethodImplementation methodImplementation = builder.getMethodImplementation();
+
+        Assert.assertEquals(0, methodImplementation.getTryBlocks().get(0).getStartCodeAddress());
+        Assert.assertEquals(8, methodImplementation.getTryBlocks().get(0).getCodeUnitCount());
+
+        methodImplementation = new MutableMethodImplementation(methodImplementation);
+
+        Assert.assertEquals(0, methodImplementation.getTryBlocks().get(0).getStartCodeAddress());
+        Assert.assertEquals(8, methodImplementation.getTryBlocks().get(0).getCodeUnitCount());
+    }
+
+    @Test
+    public void testNewLabelByAddress() {
+        MethodImplementationBuilder builder = new MethodImplementationBuilder(10);
+
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction32x(Opcode.MOVE_16, 0, 0));
+
+        MutableMethodImplementation mutableMethodImplementation =
+                new MutableMethodImplementation(builder.getMethodImplementation());
+
+        mutableMethodImplementation.addCatch(
+                mutableMethodImplementation.newLabelForAddress(0),
+                mutableMethodImplementation.newLabelForAddress(8),
+                mutableMethodImplementation.newLabelForAddress(1));
+
+        Assert.assertEquals(0, mutableMethodImplementation.getTryBlocks().get(0).getStartCodeAddress());
+        Assert.assertEquals(8, mutableMethodImplementation.getTryBlocks().get(0).getCodeUnitCount());
+        Assert.assertEquals(1, mutableMethodImplementation.getTryBlocks().get(0).getExceptionHandlers().get(0)
+                .getHandlerCodeAddress());
+    }
+
+    @Test
+    public void testNewLabelByIndex() {
+        MethodImplementationBuilder builder = new MethodImplementationBuilder(10);
+
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction10x(Opcode.NOP));
+        builder.addInstruction(new BuilderInstruction32x(Opcode.MOVE_16, 0, 0));
+
+        MutableMethodImplementation mutableMethodImplementation =
+                new MutableMethodImplementation(builder.getMethodImplementation());
+
+        mutableMethodImplementation.addCatch(
+                mutableMethodImplementation.newLabelForIndex(0),
+                mutableMethodImplementation.newLabelForIndex(6),
+                mutableMethodImplementation.newLabelForIndex(1));
+
+        Assert.assertEquals(0, mutableMethodImplementation.getTryBlocks().get(0).getStartCodeAddress());
+        Assert.assertEquals(8, mutableMethodImplementation.getTryBlocks().get(0).getCodeUnitCount());
+        Assert.assertEquals(1, mutableMethodImplementation.getTryBlocks().get(0).getExceptionHandlers().get(0)
+                .getHandlerCodeAddress());
+    }
+}
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java
index 8ba975a..1a0a289 100644
--- a/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java
+++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java
@@ -72,12 +72,12 @@
         MemoryDataStore dataStore = new MemoryDataStore();
 
         try {
-            DexPool.writeTo(dataStore, new ImmutableDexFile(ImmutableSet.of(classDef)));
+            DexPool.writeTo(dataStore, new ImmutableDexFile(Opcodes.forApi(19), ImmutableSet.of(classDef)));
         } catch (IOException ex) {
             throw new RuntimeException(ex);
         }
 
-        DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dataStore.getData());
+        DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dataStore.getData());
         ClassDef dbClassDef = Iterables.getFirst(dexFile.getClasses(), null);
         Assert.assertNotNull(dbClassDef);
         Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);
@@ -112,12 +112,12 @@
         MemoryDataStore dataStore = new MemoryDataStore();
 
         try {
-            DexPool.writeTo(dataStore, new ImmutableDexFile(ImmutableSet.of(classDef)));
+            DexPool.writeTo(dataStore, new ImmutableDexFile(Opcodes.forApi(19), ImmutableSet.of(classDef)));
         } catch (IOException ex) {
             throw new RuntimeException(ex);
         }
 
-        DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dataStore.getData());
+        DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dataStore.getData());
         ClassDef dbClassDef = Iterables.getFirst(dexFile.getClasses(), null);
         Assert.assertNotNull(dbClassDef);
         Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);
diff --git a/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java b/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java
index 7e504a1..c246e0e 100644
--- a/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java
+++ b/dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java
@@ -62,7 +62,7 @@
 public class JumboStringConversionTest {
     @Test
     public void testJumboStringConversion() throws IOException {
-        DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
+        DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(15));
 
         MethodImplementationBuilder methodBuilder = new MethodImplementationBuilder(1);
         for (int i=0; i<66000; i++) {
@@ -92,7 +92,7 @@
         MemoryDataStore dexStore = new MemoryDataStore();
         dexBuilder.writeTo(dexStore);
 
-        DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
+        DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dexStore.getData());
 
         ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
         Assert.assertNotNull(classDef);
@@ -122,7 +122,7 @@
 
     @Test
     public void testJumboStringConversion_NonMethodBuilder() throws IOException {
-        DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
+        DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(15));
 
         final List<Instruction> instructions = Lists.newArrayList();
         for (int i=0; i<66000; i++) {
@@ -189,7 +189,7 @@
         MemoryDataStore dexStore = new MemoryDataStore();
         dexBuilder.writeTo(dexStore);
 
-        DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
+        DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dexStore.getData());
 
         ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
         Assert.assertNotNull(classDef);
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7e79327..692d204 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Sun Mar 01 19:20:47 PST 2015
+#Sun Feb 14 12:35:03 PST 2016
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-2.3-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-2.11-bin.zip
diff --git a/settings.gradle b/settings.gradle
index 6c4f08c..f7a6cf7 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1,5 @@
-include 'util', 'dexlib2', 'baksmali', 'smali', 'dexlib2:accessorTestGenerator'
\ No newline at end of file
+include 'util', 'dexlib2', 'baksmali', 'smali', 'dexlib2:accessorTestGenerator'
+
+if (System.getProperty("user.dir").startsWith(file("smalidea").absolutePath)) {
+    include 'smalidea'
+}
\ No newline at end of file
diff --git a/smali/build.gradle b/smali/build.gradle
index 0e5cbf2..75001d7 100644
--- a/smali/build.gradle
+++ b/smali/build.gradle
@@ -52,6 +52,15 @@
     compile.exclude group: 'de.jflex', module: 'jflex'
 }
 
+sourceSets {
+    main {
+        resources {
+            // This adds the generated .tokens files to the jar
+            srcDir 'build/generated-src/antlr/main'
+        }
+    }
+}
+
 idea {
     module {
         excludeDirs -= buildDir
@@ -83,16 +92,13 @@
 processResources.inputs.property('version', version)
 processResources.expand('version': version)
 
-// This is the jar that gets uploaded to maven
-jar {
-    baseName = 'maven'
-}
-
 // Build a separate jar that contains all dependencies
 task fatJar(type: Jar, dependsOn: jar) {
     from sourceSets.main.output
     from { configurations.compile.collect { it.isDirectory() ? it : zipTree(it) } }
 
+    classifier = 'fat'
+
     manifest {
         attributes('Main-Class': 'org.jf.smali.main')
     }
diff --git a/smali/src/main/antlr/smaliParser.g b/smali/src/main/antlr/smaliParser.g
index 6d07452..fcccbe8 100644
--- a/smali/src/main/antlr/smaliParser.g
+++ b/smali/src/main/antlr/smaliParser.g
@@ -39,7 +39,7 @@
   ANNOTATION_DIRECTIVE;
   ANNOTATION_VISIBILITY;
   ARRAY_DATA_DIRECTIVE;
-  ARRAY_DESCRIPTOR;
+  ARRAY_TYPE_PREFIX;
   ARROW;
   BOOL_LITERAL;
   BYTE_LITERAL;
@@ -133,10 +133,7 @@
   OPEN_BRACE;
   OPEN_PAREN;
   PACKED_SWITCH_DIRECTIVE;
-  PARAM_LIST_END;
-  PARAM_LIST_START;
-  PARAM_LIST_OR_ID_END;
-  PARAM_LIST_OR_ID_START;
+  PARAM_LIST_OR_ID_PRIMITIVE_TYPE;
   PARAMETER_DIRECTIVE;
   POSITIVE_INTEGER_LITERAL;
   PRIMITIVE_TYPE;
@@ -260,7 +257,7 @@
   private boolean verboseErrors = false;
   private boolean allowOdex = false;
   private int apiLevel = 15;
-  private Opcodes opcodes = new Opcodes(apiLevel, false);
+  private Opcodes opcodes = Opcodes.forApi(apiLevel);
 
   public void setVerboseErrors(boolean verboseErrors) {
     this.verboseErrors = verboseErrors;
@@ -377,16 +374,12 @@
       case '[':
       {
         int i = typeStartIndex;
-            while (str.charAt(++i) == '[');
+        while (str.charAt(++i) == '[');
 
-            if (str.charAt(i++) == 'L') {
-                while (str.charAt(i++) != ';');
-        }
-
-            token.setType(ARRAY_DESCRIPTOR);
-            token.setText(str.substring(typeStartIndex, i));
-            token.setStopIndex(baseToken.getStartIndex() + i - 1);
-            break;
+        token.setType(ARRAY_TYPE_PREFIX);
+        token.setText(str.substring(typeStartIndex, i));
+        token.setStopIndex(baseToken.getStartIndex() + i - 1);
+        break;
       }
       default:
         throw new RuntimeException(String.format("Invalid character '\%c' in param list \"\%s\" at position \%d", str.charAt(typeStartIndex), str, typeStartIndex));
@@ -541,7 +534,7 @@
     };
 
 param_list_or_id
-  : PARAM_LIST_OR_ID_START PRIMITIVE_TYPE+ PARAM_LIST_OR_ID_END;
+  : PARAM_LIST_OR_ID_PRIMITIVE_TYPE+;
 
 /*identifiers are much more general than most languages. Any of the below can either be
 the indicated type OR an identifier, depending on the context*/
@@ -598,25 +591,30 @@
   : OPEN_PAREN param_list CLOSE_PAREN type_descriptor
     -> ^(I_METHOD_PROTOTYPE[$start, "I_METHOD_PROTOTYPE"] ^(I_METHOD_RETURN_TYPE type_descriptor) param_list?);
 
+param_list_or_id_primitive_type
+  : PARAM_LIST_OR_ID_PRIMITIVE_TYPE -> PRIMITIVE_TYPE[$PARAM_LIST_OR_ID_PRIMITIVE_TYPE];
+
 param_list
-  : PARAM_LIST_START nonvoid_type_descriptor* PARAM_LIST_END -> nonvoid_type_descriptor*
-  | PARAM_LIST_OR_ID_START PRIMITIVE_TYPE* PARAM_LIST_OR_ID_END -> PRIMITIVE_TYPE*
+  : param_list_or_id_primitive_type+
   | nonvoid_type_descriptor*;
 
+array_descriptor
+  : ARRAY_TYPE_PREFIX (PRIMITIVE_TYPE | CLASS_DESCRIPTOR);
+
 type_descriptor
   : VOID_TYPE
   | PRIMITIVE_TYPE
   | CLASS_DESCRIPTOR
-  | ARRAY_DESCRIPTOR;
+  | array_descriptor;
 
 nonvoid_type_descriptor
   : PRIMITIVE_TYPE
   | CLASS_DESCRIPTOR
-  | ARRAY_DESCRIPTOR;
+  | array_descriptor;
 
 reference_type_descriptor
   : CLASS_DESCRIPTOR
-  | ARRAY_DESCRIPTOR;
+  | array_descriptor;
 
 integer_literal
   : POSITIVE_INTEGER_LITERAL -> INTEGER_LITERAL[$POSITIVE_INTEGER_LITERAL]
@@ -692,9 +690,10 @@
   : SUBANNOTATION_DIRECTIVE CLASS_DESCRIPTOR annotation_element* END_SUBANNOTATION_DIRECTIVE
     -> ^(I_SUBANNOTATION[$start, "I_SUBANNOTATION"] CLASS_DESCRIPTOR annotation_element*);
 
+// TODO: how does dalvik handle a primitive or array type, or a non-enum type?
 enum_literal
-  : ENUM_DIRECTIVE reference_type_descriptor ARROW simple_name COLON reference_type_descriptor
-  -> ^(I_ENCODED_ENUM reference_type_descriptor simple_name reference_type_descriptor);
+  : ENUM_DIRECTIVE field_reference
+  -> ^(I_ENCODED_ENUM field_reference);
 
 type_field_method_literal
   : reference_type_descriptor
diff --git a/smali/src/main/antlr/smaliTreeWalker.g b/smali/src/main/antlr/smaliTreeWalker.g
index 8eed2b2..c3a5099 100644
--- a/smali/src/main/antlr/smaliTreeWalker.g
+++ b/smali/src/main/antlr/smaliTreeWalker.g
@@ -77,7 +77,7 @@
   public String classType;
   private boolean verboseErrors = false;
   private int apiLevel = 15;
-  private Opcodes opcodes = new Opcodes(apiLevel, false);
+  private Opcodes opcodes = Opcodes.forApi(apiLevel);
   private DexBuilder dexBuilder;
 
   public void setDexBuilder(DexBuilder dexBuilder) {
@@ -1219,20 +1219,20 @@
       $method::methodBuilder.addInstruction(new BuilderSparseSwitchPayload($sparse_switch_elements.elements));
     };
 
+array_descriptor returns [String type]
+  : ARRAY_TYPE_PREFIX ( PRIMITIVE_TYPE { $type = $ARRAY_TYPE_PREFIX.text + $PRIMITIVE_TYPE.text; }
+                      | CLASS_DESCRIPTOR { $type = $ARRAY_TYPE_PREFIX.text + $CLASS_DESCRIPTOR.text; });
+
 nonvoid_type_descriptor returns [String type]
-  : (PRIMITIVE_TYPE
-  | CLASS_DESCRIPTOR
-  | ARRAY_DESCRIPTOR)
-  {
-    $type = $start.getText();
-  };
+  : (PRIMITIVE_TYPE { $type = $text; }
+  | CLASS_DESCRIPTOR { $type = $text; }
+  | array_descriptor { $type = $array_descriptor.type; })
+  ;
 
 reference_type_descriptor returns [String type]
-  : (CLASS_DESCRIPTOR
-  | ARRAY_DESCRIPTOR)
-  {
-    $type = $start.getText();
-  };
+  : (CLASS_DESCRIPTOR { $type = $text; }
+  | array_descriptor { $type = $array_descriptor.type; })
+  ;
 
 type_descriptor returns [String type]
   : VOID_TYPE {$type = "V";}
diff --git a/smali/src/main/java/org/jf/smali/SmaliOptions.java b/smali/src/main/java/org/jf/smali/SmaliOptions.java
new file mode 100644
index 0000000..165c3a8
--- /dev/null
+++ b/smali/src/main/java/org/jf/smali/SmaliOptions.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smali;
+
+public class SmaliOptions {
+    public int apiLevel = 15;
+    public String outputDexFile = "out.dex";
+
+    public int jobs = Runtime.getRuntime().availableProcessors();
+    public boolean allowOdex = false;
+    public boolean verboseErrors = false;
+    public boolean printTokens = false;
+    public boolean experimental = false;
+
+    public boolean listMethods = false;
+    public String methodListFilename = null;
+
+    public boolean listFields = false;
+    public String fieldListFilename = null;
+
+    public boolean listTypes = false;
+    public String typeListFilename = null;
+}
diff --git a/smali/src/main/java/org/jf/smali/SmaliTestUtils.java b/smali/src/main/java/org/jf/smali/SmaliTestUtils.java
index 26de008..bef0741 100644
--- a/smali/src/main/java/org/jf/smali/SmaliTestUtils.java
+++ b/smali/src/main/java/org/jf/smali/SmaliTestUtils.java
@@ -57,7 +57,7 @@
             throws RecognitionException, IOException {
         CommonTokenStream tokens;
         LexerErrorInterface lexer;
-        DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel);
+        DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(apiLevel, experimental));
 
         Reader reader = new StringReader(smaliText);
 
@@ -94,8 +94,7 @@
 
         dexBuilder.writeTo(dataStore);
 
-        DexBackedDexFile dexFile = new DexBackedDexFile(
-                new Opcodes(apiLevel, experimental), dataStore.getData());
+        DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(apiLevel, experimental), dataStore.getData());
 
         return Iterables.getFirst(dexFile.getClasses(), null);
     }
diff --git a/smali/src/main/java/org/jf/smali/main.java b/smali/src/main/java/org/jf/smali/main.java
index 98fb7a1..e556280 100644
--- a/smali/src/main/java/org/jf/smali/main.java
+++ b/smali/src/main/java/org/jf/smali/main.java
@@ -36,8 +36,8 @@
 import org.antlr.runtime.TokenSource;
 import org.antlr.runtime.tree.CommonTree;
 import org.antlr.runtime.tree.CommonTreeNodeStream;
-import org.antlr.runtime.tree.TreeNodeStream;
 import org.apache.commons.cli.*;
+import org.jf.dexlib2.Opcodes;
 import org.jf.dexlib2.writer.builder.DexBuilder;
 import org.jf.dexlib2.writer.io.FileDataStore;
 import org.jf.util.ConsoleUtil;
@@ -46,10 +46,7 @@
 import javax.annotation.Nonnull;
 import java.io.*;
 import java.util.*;
-import java.util.concurrent.Callable;
-import java.util.concurrent.ExecutorService;
-import java.util.concurrent.Executors;
-import java.util.concurrent.Future;
+import java.util.concurrent.*;
 
 /**
  * Main class for smali. It recognizes enough options to be able to dispatch
@@ -93,6 +90,95 @@
     }
 
     /**
+     * A more programmatic-friendly entry point for smali
+     *
+     * @param options a SmaliOptions object with the options to run smali with
+     * @param input The files/directories to process
+     * @return true if assembly completed with no errors, or false if errors were encountered
+     */
+    public static boolean run(final SmaliOptions options, String... input) throws IOException {
+        LinkedHashSet<File> filesToProcessSet = new LinkedHashSet<File>();
+
+        for (String fileToProcess: input) {
+            File argFile = new File(fileToProcess);
+
+            if (!argFile.exists()) {
+                throw new IllegalArgumentException("Cannot find file or directory \"" + fileToProcess + "\"");
+            }
+
+            if (argFile.isDirectory()) {
+                getSmaliFilesInDir(argFile, filesToProcessSet);
+            } else if (argFile.isFile()) {
+                filesToProcessSet.add(argFile);
+            }
+        }
+
+        boolean errors = false;
+
+        final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(
+                Opcodes.forApi(options.apiLevel, options.experimental));
+
+        ExecutorService executor = Executors.newFixedThreadPool(options.jobs);
+        List<Future<Boolean>> tasks = Lists.newArrayList();
+
+        for (final File file: filesToProcessSet) {
+            tasks.add(executor.submit(new Callable<Boolean>() {
+                @Override public Boolean call() throws Exception {
+                    return assembleSmaliFile(file, dexBuilder, options);
+                }
+            }));
+        }
+
+        for (Future<Boolean> task: tasks) {
+            while(true) {
+                try {
+                    try {
+                        if (!task.get()) {
+                            errors = true;
+                        }
+                    } catch (ExecutionException ex) {
+                        throw new RuntimeException(ex);
+                    }
+                } catch (InterruptedException ex) {
+                    continue;
+                }
+                break;
+            }
+        }
+
+        executor.shutdown();
+
+        if (errors) {
+            return false;
+        }
+
+        if (options.listMethods) {
+            if (Strings.isNullOrEmpty(options.methodListFilename)) {
+                options.methodListFilename = options.outputDexFile + ".methods";
+            }
+            writeReferences(dexBuilder.getMethodReferences(), options.methodListFilename);
+        }
+
+        if (options.listFields) {
+            if (Strings.isNullOrEmpty(options.fieldListFilename)) {
+                options.fieldListFilename = options.outputDexFile + ".fields";
+            }
+            writeReferences(dexBuilder.getFieldReferences(), options.fieldListFilename);
+        }
+
+        if (options.listTypes) {
+            if (Strings.isNullOrEmpty(options.typeListFilename)) {
+                options.typeListFilename = options.outputDexFile + ".types";
+            }
+            writeReferences(dexBuilder.getTypeReferences(), options.typeListFilename);
+        }
+
+        dexBuilder.writeTo(new FileDataStore(new File(options.outputDexFile)));
+
+        return true;
+    }
+
+    /**
      * Run!
      */
     public static void main(String[] args) {
@@ -109,24 +195,7 @@
             return;
         }
 
-        int jobs = -1;
-        boolean allowOdex = false;
-        boolean verboseErrors = false;
-        boolean printTokens = false;
-        boolean experimental = false;
-
-        boolean listMethods = false;
-        String methodListFilename = null;
-
-        boolean listFields = false;
-        String fieldListFilename = null;
-
-        boolean listTypes = false;
-        String typeListFilename = null;
-
-        int apiLevel = 15;
-
-        String outputDexFile = "out.dex";
+        SmaliOptions smaliOptions = new SmaliOptions();
 
         String[] remainingArgs = commandLine.getArgs();
 
@@ -150,37 +219,37 @@
                     usage(false);
                     return;
                 case 'o':
-                    outputDexFile = commandLine.getOptionValue("o");
+                    smaliOptions.outputDexFile = commandLine.getOptionValue("o");
                     break;
                 case 'x':
-                    allowOdex = true;
+                    smaliOptions.allowOdex = true;
                     break;
                 case 'X':
-                    experimental = true;
+                    smaliOptions.experimental = true;
                     break;
                 case 'a':
-                    apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
+                    smaliOptions.apiLevel = Integer.parseInt(commandLine.getOptionValue("a"));
                     break;
                 case 'j':
-                    jobs = Integer.parseInt(commandLine.getOptionValue("j"));
+                    smaliOptions.jobs = Integer.parseInt(commandLine.getOptionValue("j"));
                     break;
                 case 'm':
-                    listMethods = true;
-                    methodListFilename = commandLine.getOptionValue("m");
+                    smaliOptions.listMethods = true;
+                    smaliOptions.methodListFilename = commandLine.getOptionValue("m");
                     break;
                 case 'f':
-                    listFields = true;
-                    fieldListFilename = commandLine.getOptionValue("f");
+                    smaliOptions.listFields = true;
+                    smaliOptions.fieldListFilename = commandLine.getOptionValue("f");
                     break;
                 case 't':
-                    listTypes = true;
-                    typeListFilename = commandLine.getOptionValue("t");
+                    smaliOptions.listTypes = true;
+                    smaliOptions.typeListFilename = commandLine.getOptionValue("t");
                     break;
                 case 'V':
-                    verboseErrors = true;
+                    smaliOptions.verboseErrors = true;
                     break;
                 case 'T':
-                    printTokens = true;
+                    smaliOptions.printTokens = true;
                     break;
                 default:
                     assert false;
@@ -193,90 +262,9 @@
         }
 
         try {
-            LinkedHashSet<File> filesToProcess = new LinkedHashSet<File>();
-
-            for (String arg: remainingArgs) {
-                    File argFile = new File(arg);
-
-                    if (!argFile.exists()) {
-                        throw new RuntimeException("Cannot find file or directory \"" + arg + "\"");
-                    }
-
-                    if (argFile.isDirectory()) {
-                        getSmaliFilesInDir(argFile, filesToProcess);
-                    } else if (argFile.isFile()) {
-                        filesToProcess.add(argFile);
-                    }
-            }
-
-            if (jobs <= 0) {
-                jobs = Runtime.getRuntime().availableProcessors();
-                if (jobs > 6) {
-                    jobs = 6;
-                }
-            }
-
-            boolean errors = false;
-
-            final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel);
-            ExecutorService executor = Executors.newFixedThreadPool(jobs);
-            List<Future<Boolean>> tasks = Lists.newArrayList();
-
-            final boolean finalVerboseErrors = verboseErrors;
-            final boolean finalPrintTokens = printTokens;
-            final boolean finalAllowOdex = allowOdex;
-            final int finalApiLevel = apiLevel;
-            final boolean finalExperimental = experimental;
-            for (final File file: filesToProcess) {
-                tasks.add(executor.submit(new Callable<Boolean>() {
-                    @Override public Boolean call() throws Exception {
-                        return assembleSmaliFile(file, dexBuilder, finalVerboseErrors, finalPrintTokens,
-                                finalAllowOdex, finalApiLevel, finalExperimental);
-                    }
-                }));
-            }
-
-            for (Future<Boolean> task: tasks) {
-                while(true) {
-                    try {
-                        if (!task.get()) {
-                            errors = true;
-                        }
-                    } catch (InterruptedException ex) {
-                        continue;
-                    }
-                    break;
-                }
-            }
-
-            executor.shutdown();
-
-            if (errors) {
+            if (!run(smaliOptions, remainingArgs)) {
                 System.exit(1);
             }
-
-            if (listMethods) {
-                if (Strings.isNullOrEmpty(methodListFilename)) {
-                    methodListFilename = outputDexFile + ".methods";
-                }
-                writeReferences(dexBuilder.getMethodReferences(), methodListFilename);
-            }
-
-            if (listFields) {
-                if (Strings.isNullOrEmpty(fieldListFilename)) {
-                    fieldListFilename = outputDexFile + ".fields";
-                }
-                writeReferences(dexBuilder.getFieldReferences(), fieldListFilename);
-            }
-
-            if (listTypes) {
-                if (Strings.isNullOrEmpty(typeListFilename)) {
-                    typeListFilename = outputDexFile + ".types";
-                }
-                writeReferences(dexBuilder.getTypeReferences(), typeListFilename);
-            }
-
-            dexBuilder.writeTo(new FileDataStore(new File(outputDexFile)));
         } catch (RuntimeException ex) {
             System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
             ex.printStackTrace();
@@ -318,22 +306,20 @@
         }
     }
 
-    private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, boolean verboseErrors,
-                                             boolean printTokens, boolean allowOdex, int apiLevel,
-                                             boolean experimental)
+    private static boolean assembleSmaliFile(File smaliFile, DexBuilder dexBuilder, SmaliOptions options)
             throws Exception {
         CommonTokenStream tokens;
 
         LexerErrorInterface lexer;
 
-        FileInputStream fis = new FileInputStream(smaliFile.getAbsolutePath());
+        FileInputStream fis = new FileInputStream(smaliFile);
         InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
 
         lexer = new smaliFlexLexer(reader);
         ((smaliFlexLexer)lexer).setSourceFile(smaliFile);
         tokens = new CommonTokenStream((TokenSource)lexer);
 
-        if (printTokens) {
+        if (options.printTokens) {
             tokens.getTokens();
 
             for (int i=0; i<tokens.size(); i++) {
@@ -349,9 +335,9 @@
         }
 
         smaliParser parser = new smaliParser(tokens);
-        parser.setVerboseErrors(verboseErrors);
-        parser.setAllowOdex(allowOdex);
-        parser.setApiLevel(apiLevel, experimental);
+        parser.setVerboseErrors(options.verboseErrors);
+        parser.setAllowOdex(options.allowOdex);
+        parser.setApiLevel(options.apiLevel, options.experimental);
 
         smaliParser.smali_file_return result = parser.smali_file();
 
@@ -364,14 +350,14 @@
         CommonTreeNodeStream treeStream = new CommonTreeNodeStream(t);
         treeStream.setTokenStream(tokens);
 
-        if (printTokens) {
+        if (options.printTokens) {
             System.out.println(t.toStringTree());
         }
 
         smaliTreeWalker dexGen = new smaliTreeWalker(treeStream);
-        dexGen.setApiLevel(apiLevel, experimental);
+        dexGen.setApiLevel(options.apiLevel, options.experimental);
 
-        dexGen.setVerboseErrors(verboseErrors);
+        dexGen.setVerboseErrors(options.verboseErrors);
         dexGen.setDexBuilder(dexBuilder);
         dexGen.smali_file();
 
diff --git a/smali/src/main/jflex/smaliLexer.jflex b/smali/src/main/jflex/smaliLexer.jflex
index bc17362..2f57a43 100644
--- a/smali/src/main/jflex/smaliLexer.jflex
+++ b/smali/src/main/jflex/smaliLexer.jflex
@@ -172,6 +172,20 @@
     public String getErrorHeader(InvalidToken token) {
         return getSourceName()+"["+ token.getLine()+","+token.getCharPositionInLine()+"]";
     }
+
+    public void reset(CharSequence charSequence, int start, int end, int initialState) {
+        zzReader = BlankReader.INSTANCE;
+        zzBuffer = new char[charSequence.length()];
+        for (int i=0; i<charSequence.length(); i++) {
+            zzBuffer[i] = charSequence.charAt(i);
+        }
+
+        yychar = zzCurrentPos = zzMarkedPos = zzStartRead = start;
+        zzEndRead = end;
+        zzAtBOL = true;
+        zzAtEOF = false;
+        yybegin(initialState);
+    }
 %}
 
 HexPrefix = 0 [xX]
@@ -217,13 +231,14 @@
 
 ClassDescriptor = L ({SimpleName} "/")* {SimpleName} ;
 
-ArrayDescriptor = "[" + ({PrimitiveType} | {ClassDescriptor})
+ArrayPrefix = "["+
 
-Type = {PrimitiveType} | {ClassDescriptor} | {ArrayDescriptor}
+Type = {PrimitiveType} | {ClassDescriptor} | {ArrayPrefix} ({ClassDescriptor} | {PrimitiveType})
 
 
 %state PARAM_LIST_OR_ID
 %state PARAM_LIST
+%state ARRAY_DESCRIPTOR
 %state STRING
 %state CHAR
 
@@ -292,17 +307,17 @@
 }
 
 <PARAM_LIST_OR_ID> {
-    {PrimitiveType} { return newToken(PRIMITIVE_TYPE); }
-    [^] { yypushback(1); yybegin(YYINITIAL); return newToken(PARAM_LIST_OR_ID_END); }
-    <<EOF>> { yybegin(YYINITIAL); return newToken(PARAM_LIST_OR_ID_END); }
+    {PrimitiveType} { return newToken(PARAM_LIST_OR_ID_PRIMITIVE_TYPE); }
+    [^] { yypushback(1); yybegin(YYINITIAL); }
+    <<EOF>> { yybegin(YYINITIAL); }
 }
 
 <PARAM_LIST> {
     {PrimitiveType} { return newToken(PRIMITIVE_TYPE); }
     {ClassDescriptor} { return newToken(CLASS_DESCRIPTOR); }
-    {ArrayDescriptor} { return newToken(ARRAY_DESCRIPTOR); }
-    [^] { yypushback(1); yybegin(YYINITIAL); return newToken(PARAM_LIST_END); }
-    <<EOF>> { yybegin(YYINITIAL); return newToken(PARAM_LIST_END); }
+    {ArrayPrefix} { return newToken(ARRAY_TYPE_PREFIX); }
+    [^] { yypushback(1); yybegin(YYINITIAL);}
+    <<EOF>> { yybegin(YYINITIAL);}
 }
 
 <STRING> {
@@ -406,7 +421,7 @@
         return newToken(INSTRUCTION_FORMAT10x);
     }
 
-    "return-void-barrier" {
+    "return-void-barrier" | "return-void-no-barrier" {
         return newToken(INSTRUCTION_FORMAT10x_ODEX);
     }
 
@@ -507,7 +522,9 @@
     "liberate-variable" {
         return newToken(INSTRUCTION_FORMAT22c_STRING);
     }
-    "iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" {
+
+    "iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" |
+    "iput-boolean-quick" | "iput-byte-quick" | "iput-char-quick" | "iput-short-quick" {
         return newToken(INSTRUCTION_FORMAT22cs_FIELD);
     }
 
@@ -612,23 +629,36 @@
     }
 }
 
+<ARRAY_DESCRIPTOR> {
+   {PrimitiveType} { yybegin(YYINITIAL); return newToken(PRIMITIVE_TYPE); }
+   {ClassDescriptor} { yybegin(YYINITIAL); return newToken(CLASS_DESCRIPTOR); }
+   [^] { yypushback(1); yybegin(YYINITIAL); }
+   <<EOF>> { yybegin(YYINITIAL); }
+}
+
 /*Types*/
 <YYINITIAL> {
     {PrimitiveType} { return newToken(PRIMITIVE_TYPE); }
     V { return newToken(VOID_TYPE); }
     {ClassDescriptor} { return newToken(CLASS_DESCRIPTOR); }
-    {ArrayDescriptor} { return newToken(ARRAY_DESCRIPTOR); }
+
+    // we have to drop into a separate state so that we don't parse something like
+    // "[I->" as "[" followed by "I-" as a SIMPLE_NAME
+    {ArrayPrefix} {
+      yybegin(ARRAY_DESCRIPTOR);
+      return newToken(ARRAY_TYPE_PREFIX);
+    }
 
     {PrimitiveType} {PrimitiveType}+ {
+        // go back and re-lex it as a PARAM_LIST_OR_ID
         yypushback(yylength());
         yybegin(PARAM_LIST_OR_ID);
-        return newToken(PARAM_LIST_OR_ID_START);
     }
 
     {Type} {Type}+ {
+        // go back and re-lex it as a PARAM_LIST
         yypushback(yylength());
         yybegin(PARAM_LIST);
-        return newToken(PARAM_LIST_START);
     }
 
     {SimpleName} { return newToken(SIMPLE_NAME); }
diff --git a/smali/src/test/resources/LexerTest/InstructionTest.smali b/smali/src/test/resources/LexerTest/InstructionTest.smali
index f6dcbb1..174cff8 100644
--- a/smali/src/test/resources/LexerTest/InstructionTest.smali
+++ b/smali/src/test/resources/LexerTest/InstructionTest.smali
@@ -2,6 +2,7 @@
 return-void
 nop
 return-void-barrier
+return-void-no-barrier
 const/4
 move-result
 move-result-wide
@@ -132,6 +133,10 @@
 iput-quick
 iput-wide-quick
 iput-object-quick
+iput-boolean-quick
+iput-byte-quick
+iput-char-quick
+iput-short-quick
 rsub-int
 add-int/lit16
 mul-int/lit16
diff --git a/smali/src/test/resources/LexerTest/InstructionTest.tokens b/smali/src/test/resources/LexerTest/InstructionTest.tokens
index fb5503b..fa959ba 100644
--- a/smali/src/test/resources/LexerTest/InstructionTest.tokens
+++ b/smali/src/test/resources/LexerTest/InstructionTest.tokens
@@ -2,6 +2,7 @@
 INSTRUCTION_FORMAT10x("return-void")
 INSTRUCTION_FORMAT10x("nop")
 INSTRUCTION_FORMAT10x_ODEX("return-void-barrier")
+INSTRUCTION_FORMAT10x_ODEX("return-void-no-barrier")
 INSTRUCTION_FORMAT11n("const/4")
 INSTRUCTION_FORMAT11x("move-result")
 INSTRUCTION_FORMAT11x("move-result-wide")
@@ -132,6 +133,10 @@
 INSTRUCTION_FORMAT22cs_FIELD("iput-quick")
 INSTRUCTION_FORMAT22cs_FIELD("iput-wide-quick")
 INSTRUCTION_FORMAT22cs_FIELD("iput-object-quick")
+INSTRUCTION_FORMAT22cs_FIELD("iput-boolean-quick")
+INSTRUCTION_FORMAT22cs_FIELD("iput-byte-quick")
+INSTRUCTION_FORMAT22cs_FIELD("iput-char-quick")
+INSTRUCTION_FORMAT22cs_FIELD("iput-short-quick")
 INSTRUCTION_FORMAT22s_OR_ID("rsub-int")
 INSTRUCTION_FORMAT22s("add-int/lit16")
 INSTRUCTION_FORMAT22s("mul-int/lit16")
diff --git a/smali/src/test/resources/LexerTest/RealSmaliFileTest.tokens b/smali/src/test/resources/LexerTest/RealSmaliFileTest.tokens
index ba40c2f..f9b096f 100644
--- a/smali/src/test/resources/LexerTest/RealSmaliFileTest.tokens
+++ b/smali/src/test/resources/LexerTest/RealSmaliFileTest.tokens
@@ -184,7 +184,8 @@
 COMMA(",")
 REGISTER("v0")
 COMMA(",")
-ARRAY_DESCRIPTOR("[I")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
 INSTRUCTION_FORMAT31t("fill-array-data")
 REGISTER("v0")
 COMMA(",")
@@ -197,7 +198,8 @@
 ARROW("->")
 SIMPLE_NAME("PROC_WAKELOCKS_FORMAT")
 COLON(":")
-ARRAY_DESCRIPTOR("[I")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
 LINE_DIRECTIVE(".line")
 POSITIVE_INTEGER_LITERAL("3495")
 INSTRUCTION_FORMAT21c_TYPE("new-instance")
@@ -552,13 +554,11 @@
 ACCESS_SPEC("public")
 SIMPLE_NAME("setCallForwardingOption")
 OPEN_PAREN("(")
-PARAM_LIST_START("")
 PRIMITIVE_TYPE("I")
 PRIMITIVE_TYPE("I")
 CLASS_DESCRIPTOR("Ljava/lang/String;")
 PRIMITIVE_TYPE("I")
 CLASS_DESCRIPTOR("Landroid/os/Message;")
-PARAM_LIST_END("")
 CLOSE_PAREN(")")
 VOID_TYPE("V")
 REGISTERS_DIRECTIVE(".registers")
@@ -703,12 +703,10 @@
 ARROW("->")
 SIMPLE_NAME("obtainMessage")
 OPEN_PAREN("(")
-PARAM_LIST_START("")
 PRIMITIVE_TYPE("I")
 PRIMITIVE_TYPE("I")
 PRIMITIVE_TYPE("I")
 CLASS_DESCRIPTOR("Ljava/lang/Object;")
-PARAM_LIST_END("")
 CLOSE_PAREN(")")
 CLASS_DESCRIPTOR("Landroid/os/Message;")
 INSTRUCTION_FORMAT11x("move-result-object")
@@ -760,14 +758,12 @@
 ARROW("->")
 SIMPLE_NAME("setCallForward")
 OPEN_PAREN("(")
-PARAM_LIST_START("")
 PRIMITIVE_TYPE("I")
 PRIMITIVE_TYPE("I")
 PRIMITIVE_TYPE("I")
 CLASS_DESCRIPTOR("Ljava/lang/String;")
 PRIMITIVE_TYPE("I")
 CLASS_DESCRIPTOR("Landroid/os/Message;")
-PARAM_LIST_END("")
 CLOSE_PAREN(")")
 VOID_TYPE("V")
 LINE_DIRECTIVE(".line")
diff --git a/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.smali b/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.smali
index 2120d33..9becb91 100644
--- a/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.smali
+++ b/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.smali
@@ -23,6 +23,8 @@
 [D
 [Ljava/lang/String;
 [LI/I/I;
+[[LI/I/I;
+[[I
 
 IIIII
 ZBSCIJFD
@@ -49,4 +51,6 @@
 
 <linit>
 
-III
\ No newline at end of file
+III
+
+[I->clone()Ljava/lang/Object;
\ No newline at end of file
diff --git a/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.tokens b/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.tokens
index d99d2c2..b0b66db 100644
--- a/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.tokens
+++ b/smali/src/test/resources/LexerTest/TypeAndIdentifierTest.tokens
@@ -13,63 +13,72 @@
 CLASS_DESCRIPTOR("LV;")
 CLASS_DESCRIPTOR("LI/I/I;")
 
-ARRAY_DESCRIPTOR("[Z")
-ARRAY_DESCRIPTOR("[B")
-ARRAY_DESCRIPTOR("[S")
-ARRAY_DESCRIPTOR("[C")
-ARRAY_DESCRIPTOR("[I")
-ARRAY_DESCRIPTOR("[J")
-ARRAY_DESCRIPTOR("[F")
-ARRAY_DESCRIPTOR("[D")
-ARRAY_DESCRIPTOR("[Ljava/lang/String;")
-ARRAY_DESCRIPTOR("[LI/I/I;")
-
-PARAM_LIST_OR_ID_START("")
-PRIMITIVE_TYPE("I")
-PRIMITIVE_TYPE("I")
-PRIMITIVE_TYPE("I")
-PRIMITIVE_TYPE("I")
-PRIMITIVE_TYPE("I")
-PARAM_LIST_OR_ID_END("")
-
-PARAM_LIST_OR_ID_START("")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("Z")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("B")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("S")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("C")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("I")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("J")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("F")
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("D")
-PARAM_LIST_OR_ID_END("")
+ARRAY_TYPE_PREFIX("[")
+CLASS_DESCRIPTOR("Ljava/lang/String;")
+ARRAY_TYPE_PREFIX("[")
+CLASS_DESCRIPTOR("LI/I/I;")
+ARRAY_TYPE_PREFIX("[[")
+CLASS_DESCRIPTOR("LI/I/I;")
+ARRAY_TYPE_PREFIX("[[")
+PRIMITIVE_TYPE("I")
 
-PARAM_LIST_START("")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("Z")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("B")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("S")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("C")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("J")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("F")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("D")
+
 PRIMITIVE_TYPE("I")
 CLASS_DESCRIPTOR("La;")
-ARRAY_DESCRIPTOR("[La;")
-ARRAY_DESCRIPTOR("[I")
-PARAM_LIST_END("")
+ARRAY_TYPE_PREFIX("[")
+CLASS_DESCRIPTOR("La;")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
 
-PARAM_LIST_START("")
 CLASS_DESCRIPTOR("Ljava/lang/String;")
 CLASS_DESCRIPTOR("Ljava/lang/String;")
-PARAM_LIST_END("")
 
-PARAM_LIST_START("")
-ARRAY_DESCRIPTOR("[I")
-ARRAY_DESCRIPTOR("[I")
-ARRAY_DESCRIPTOR("[I")
-PARAM_LIST_END("")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
 
-PARAM_LIST_START("")
-ARRAY_DESCRIPTOR("[I")
-ARRAY_DESCRIPTOR("[Z")
-PARAM_LIST_END("")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("Z")
 
-PARAM_LIST_START("")
-ARRAY_DESCRIPTOR("[I")
-ARRAY_DESCRIPTOR("[Ljava/lang/String;")
-PARAM_LIST_END("")
+ARRAY_TYPE_PREFIX("[")
+PRIMITIVE_TYPE("I")
+ARRAY_TYPE_PREFIX("[")
+CLASS_DESCRIPTOR("Ljava/lang/String;")
 
 MEMBER_NAME("<init>")
 MEMBER_NAME("<clinit>")
@@ -79,17 +88,23 @@
 SIMPLE_NAME("Ljava") INVALID_TOKEN("/") SIMPLE_NAME("lang") INVALID_TOKEN("/") SIMPLE_NAME("String")
 SIMPLE_NAME("L") INVALID_TOKEN(";")
 SIMPLE_NAME("LI")
-SIMPLE_NAME("L") ARRAY_DESCRIPTOR("[Ljava/lang/String;")
+SIMPLE_NAME("L") ARRAY_TYPE_PREFIX("[") CLASS_DESCRIPTOR("Ljava/lang/String;")
 
-INVALID_TOKEN("[")
-INVALID_TOKEN("[") VOID_TYPE("V")
-INVALID_TOKEN("[") SIMPLE_NAME("java") INVALID_TOKEN("/") SIMPLE_NAME("lang") INVALID_TOKEN("/") SIMPLE_NAME("String") INVALID_TOKEN(";")
-INVALID_TOKEN("[") INVALID_TOKEN(";")
+ARRAY_TYPE_PREFIX("[")
+ARRAY_TYPE_PREFIX("[") VOID_TYPE("V")
+ARRAY_TYPE_PREFIX("[") SIMPLE_NAME("java") INVALID_TOKEN("/") SIMPLE_NAME("lang") INVALID_TOKEN("/") SIMPLE_NAME("String") INVALID_TOKEN(";")
+ARRAY_TYPE_PREFIX("[") INVALID_TOKEN(";")
 
 MEMBER_NAME("<linit>")
 
-PARAM_LIST_OR_ID_START("")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+PARAM_LIST_OR_ID_PRIMITIVE_TYPE("I")
+
+ARRAY_TYPE_PREFIX("[")
 PRIMITIVE_TYPE("I")
-PRIMITIVE_TYPE("I")
-PRIMITIVE_TYPE("I")
-PARAM_LIST_OR_ID_END("")
\ No newline at end of file
+ARROW("->")
+SIMPLE_NAME("clone")
+OPEN_PAREN("(")
+CLOSE_PAREN(")")
+CLASS_DESCRIPTOR("Ljava/lang/Object;")
\ No newline at end of file
diff --git a/smalidea/.gitignore b/smalidea/.gitignore
new file mode 100644
index 0000000..f8be399
--- /dev/null
+++ b/smalidea/.gitignore
@@ -0,0 +1,3 @@
+build
+test-config
+test-system
diff --git a/smalidea/build.gradle b/smalidea/build.gradle
new file mode 100644
index 0000000..6b855d7
--- /dev/null
+++ b/smalidea/build.gradle
@@ -0,0 +1,236 @@
+/*
+ * Copyright 2013, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+buildscript {
+    repositories {
+        maven {
+            url "https://plugins.gradle.org/m2/"
+        }
+    }
+    dependencies {
+        classpath 'gradle.plugin.org.jetbrains:gradle-intellij-plugin:0.0.40'
+    }
+}
+
+apply plugin: 'java'
+apply plugin: 'idea'
+apply plugin: 'antlr'
+
+version = '0.03'
+
+if (!('release' in gradle.startParameter.taskNames)) {
+    def versionSuffix
+    try {
+        def git = org.eclipse.jgit.api.Git.open(file('..'))
+        def head = git.getRepository().getRef('HEAD')
+        versionSuffix = head.getObjectId().abbreviate(8).name()
+
+        if (!git.status().call().clean) {
+            versionSuffix += '-dirty'
+        }
+    } catch (Exception ex) {
+        // In case we can't get the commit for some reason,
+        // just use -dev
+        versionSuffix = 'dev'
+    }
+
+    def baseVersion = version
+    version = baseVersion + '-' + versionSuffix
+} else {
+    if (System.env.JDK7_HOME == null && !JavaVersion.current().isJava7()) {
+        throw new InvalidUserDataException("bzzzzzzzt. Release builds must be performed with java 7. " +
+                "Either run gradle with java 7, or define the JDK7_HOME environment variable.")
+    }
+}
+
+if (System.env.JDK7_HOME != null) {
+    sourceCompatibility = 1.7
+    targetCompatibility = 1.7
+
+    tasks.withType(JavaCompile) {
+        doFirst {
+            options.fork = true
+            options.bootClasspath = "$System.env.JDK7_HOME/jre/lib/rt.jar"
+            options.bootClasspath += "$File.pathSeparator$System.env.JDK7_HOME/jre/lib/jsse.jar"
+        }
+    }
+}
+
+def sandboxDir = "${buildDir}/sandbox"
+
+// We don't want to use the org.jetbrains.intellij plugin when generating the idea project files,
+// so that idea classes aren't included as project dependencies, since they will already exist
+// in the plugin sdk defined for the project
+if (!('idea' in gradle.startParameter.taskNames)) {
+    apply plugin: 'org.jetbrains.intellij'
+
+    intellij {
+        version 'IC-14.1.4'
+        pluginName 'smalidea'
+
+        updateSinceUntilBuild false
+
+        sandboxDirectory sandboxDir
+    }
+
+    // This prints out the directories that can be used to configure a plugin sdk in IDEA, using
+    // the copy of IDEA downloaded by the org.jetbrains.intellij plugin
+    task ideaDirs() {
+        project.afterEvaluate {
+            if (intellij != null) {
+                println "IDEA Plugin jdk: ${intellij.ideaDirectory}"
+                println "sources: ${project.configurations['intellij-sources'].files[0]}"
+            }
+        }
+    }
+
+    dependencies {
+        compile files("${System.properties['java.home']}/../lib/tools.jar")
+    }
+} else {
+    // If we're running the idea task, let's make sure nothing else is being run, since
+    // we have to use a special configuration for the idea task
+    if (gradle.startParameter.taskNames.size() > 1) {
+        throw new InvalidUserDataException("The idea task must be run by itself.")
+    }
+
+    project(':') {
+        idea {
+            project {
+                ipr {
+                    withXml {
+                        def node = it.asNode()
+
+                        /*node.find { it.@name == 'ProjectRootManager' }
+                                .@'project-jdk-type' = 'IDEA JDK'*/
+
+                        def componentNode = node.find { it.@name == 'ProjectRunConfigurationManager' }
+                        if (componentNode == null) {
+                            componentNode = it.node.appendNode 'component', [name: 'ProjectRunConfigurationManager']
+                        }
+
+                        if (componentNode.find { it.@name == 'All smalidea tests' } == null) {
+                            componentNode.append(new XmlParser().parseText("""
+                                <configuration default="false" name="All smalidea tests" type="JUnit" factoryName="JUnit">
+                                  <extension name="coverage" enabled="false" merge="false" runner="idea" />
+                                  <module name="smalidea" />
+                                  <option name="TEST_OBJECT" value="directory" />
+                                  <option name="VM_PARAMETERS" value="-Didea.system.path=${sandboxDir}/config -Didea.system.path=${sandboxDir}/system-test -Didea.load.plugins.id=org.jf.smalidea" />
+                                  <option name="WORKING_DIRECTORY" value="file://\$PROJECT_DIR\$/smalidea" />
+                                  <option name="PASS_PARENT_ENVS" value="true" />
+                                  <option name="TEST_SEARCH_SCOPE">
+                                    <value defaultName="moduleWithDependencies" />
+                                  </option>
+                                  <dir value="\$PROJECT_DIR\$/smalidea/src/test/java" />
+                                </configuration>"""))
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    idea {
+        module {
+            jdkName = 'IDEA Plugin jdk'
+
+            excludeDirs -= buildDir
+            if (buildDir.exists()) {
+                excludeDirs.addAll(buildDir.listFiles())
+            }
+
+            for (sourceDir in (sourceDirs + testSourceDirs)) {
+                excludeDirs.remove(sourceDir);
+                while ((sourceDir = sourceDir.getParentFile()) != null) {
+                    excludeDirs.remove(sourceDir);
+                }
+            }
+
+            iml {
+                withXml {
+                    def node = it.node
+
+                    node.@type = 'PLUGIN_MODULE'
+
+                    def pluginUrl = 'file://$MODULE_DIR$/src/main/resources/META-INF/plugin.xml'
+
+                    def pluginNode = node.find { it.@name == 'DevKit.ModuleBuildProperties' }
+                    if (pluginNode == null) {
+                        node.appendNode 'component', [name: 'DevKit.ModuleBuildProperties',
+                                                      url : pluginUrl]
+                    } else {
+                        pluginNode.@url = pluginUrl
+                    }
+                }
+            }
+        }
+    }
+}
+
+repositories {
+    mavenLocal()
+    mavenCentral()
+}
+
+dependencies {
+    compile project(':smali')
+    compile depends.antlr_runtime
+    compile depends.gson
+
+    antlr depends.antlr
+}
+
+task extractTokens(type: org.gradle.api.tasks.Copy, dependsOn: ':smali:build') {
+    def allArtifacts = configurations.default.resolvedConfiguration.resolvedArtifacts
+    def smaliArtifact = allArtifacts.find { it.moduleVersion.id.name.equals('smali') }
+
+    from(zipTree(smaliArtifact.file)) {
+        include '**/*.tokens'
+    }
+    into "${buildDir}/tokens"
+}
+
+generateGrammarSource {
+    def tokensDir = file("${buildDir}/tokens/org/jf/smali")
+    inputs.file new File(tokensDir, 'smaliParser.tokens')
+    setArguments(['-lib', tokensDir.path])
+    outputDirectory(file("${buildDir}/generated-src/antlr/main/org/jf/smalidea"))
+}
+generateGrammarSource.dependsOn(extractTokens)
+
+ideaModule.dependsOn(generateGrammarSource)
+
+task release(dependsOn: 'buildPlugin') {
+}
+
+tasks.getByPath('idea').dependsOn(project(':').getTasksByName('idea', true).findAll({
+    it.project.name != 'smalidea'
+}))
\ No newline at end of file
diff --git a/smalidea/src/main/antlr/smalideaParser.g b/smalidea/src/main/antlr/smalideaParser.g
new file mode 100644
index 0000000..0da263f
--- /dev/null
+++ b/smalidea/src/main/antlr/smalideaParser.g
@@ -0,0 +1,1363 @@
+/*
+ * [The "BSD licence"]
+ * Copyright (c) 2010 Ben Gruver
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+parser grammar smalideaParser;
+
+options {
+  tokenVocab=smaliParser;
+}
+
+@header {
+package org.jf.smalidea;
+
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.PsiBuilder.Marker;
+import com.intellij.psi.tree.IElementType;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+}
+
+
+@members {
+    private PsiBuilder psiBuilder;
+
+    public void setPsiBuilder(PsiBuilder psiBuilder) {
+        this.psiBuilder = psiBuilder;
+    }
+
+    public Marker mark() {
+        return psiBuilder.mark();
+    }
+
+    protected void syncToFollows(boolean acceptEof) {
+        BitSet follow = computeErrorRecoverySet();
+        int mark = input.mark();
+        Marker marker = null;
+        try {
+            int token = input.LA(1);
+            while (!follow.member(token)) {
+                if (token == Token.EOF) {
+                    if (acceptEof) {
+                        break;
+                    }
+                    input.rewind(mark);
+                    mark = -1;
+                    marker = null;
+                    return;
+                }
+                if (marker == null) {
+                    marker = mark();
+                }
+                input.consume();
+                token = input.LA(1);
+            }
+        } finally {
+            if  (mark != -1) {
+                input.release(mark);
+            }
+            if (marker != null) {
+                marker.error("Unexpected tokens");
+            }
+        }
+    }
+
+    @Override
+    public void recover(IntStream input, RecognitionException re) {
+        BitSet followSet = computeErrorRecoverySet();
+        beginResync();
+        consumeUntil(input, followSet);
+        endResync();
+    }
+
+    @Override
+    protected Object recoverFromMismatchedToken(IntStream input, int ttype, BitSet follow)
+            throws RecognitionException
+    {
+        RecognitionException e = null;
+        // if next token is what we are looking for then "delete" this token
+        if ( mismatchIsUnwantedToken(input, ttype) ) {
+            e = new UnwantedTokenException(ttype, input);
+            beginResync();
+            Marker mark = mark();
+            input.consume(); // simply delete extra token
+            mark.error(getErrorMessage(e, tokenNames));
+            endResync();
+            reportError(null, e, true);  // report after consuming so AW sees the token in the exception
+            // we want to return the token we're actually matching
+            Object matchedSymbol = getCurrentInputSymbol(input);
+            input.consume(); // move past ttype token as if all were ok
+            return matchedSymbol;
+        }
+        // can't recover with single token deletion, try insertion
+        if ( mismatchIsMissingToken(input, follow) ) {
+            Object inserted = getMissingSymbol(input, e, ttype, follow);
+            Marker mark = mark();
+            e = new MissingTokenException(ttype, input, inserted);
+            mark.error(getErrorMessage(e, tokenNames));
+            reportError(null, e, true);  // report after inserting so AW sees the token in the exception
+            return inserted;
+        }
+
+        // even that didn't work; must throw the exception
+        e = new MismatchedTokenException(ttype, input);
+        throw e;
+    }
+
+    @Override
+    public void reportError(RecognitionException e) {
+        reportError(mark(), e, false);
+    }
+
+    public void reportError(@Nullable Marker marker, RecognitionException e, boolean alreadyReported) {
+        // if we've already reported an error and have not matched a token
+        // yet successfully, don't report any errors.
+        if ( state.errorRecovery ) {
+            if (marker != null) {
+                marker.drop();
+            }
+            return;
+        }
+        state.syntaxErrors++; // don't count spurious
+        state.errorRecovery = true;
+
+        if (marker != null) {
+            if (!alreadyReported) {
+                displayRecognitionError(marker, this.getTokenNames(), e);
+            } else {
+                marker.drop();
+            }
+        }
+    }
+
+    public void finishToken(Marker marker, IElementType elementType) {
+        if (state.errorRecovery) {
+            marker.drop();
+        } else {
+            marker.done(elementType);
+        }
+    }
+
+    @Override
+    public void displayRecognitionError(String[] tokenNames, RecognitionException e) {
+        displayRecognitionError(mark(), tokenNames, e);
+    }
+
+    public void displayRecognitionError(@Nonnull Marker marker, String[] tokenNames, RecognitionException e) {
+        marker.error(getErrorMessage(e, tokenNames));
+    }
+}
+
+sync[boolean toEof]
+  @init { syncToFollows($toEof); }
+  : /*epsilon*/;
+
+smali_file
+  @init {
+    mark().done(SmaliElementTypes.EXTENDS_LIST);
+    mark().done(SmaliElementTypes.IMPLEMENTS_LIST);
+  }
+  :
+  (
+    ( class_spec
+    | super_spec
+    | implements_spec
+    | source_spec
+    | method
+    | field
+    | annotation
+    )
+    sync[true]
+  )+
+  EOF;
+
+class_spec
+  @init { Marker marker = mark(); }
+  : CLASS_DIRECTIVE class_access_list class_descriptor
+  { marker.done(SmaliElementTypes.CLASS_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+super_spec
+  @init { Marker marker = mark(); }
+  : SUPER_DIRECTIVE class_descriptor
+  { marker.done(SmaliElementTypes.SUPER_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+implements_spec
+  @init { Marker marker = mark(); }
+  : IMPLEMENTS_DIRECTIVE class_descriptor
+  { marker.done(SmaliElementTypes.IMPLEMENTS_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+source_spec
+  @init { Marker marker = mark(); }
+  : SOURCE_DIRECTIVE string_literal
+  { marker.done(SmaliElementTypes.SOURCE_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+// class_access_list should be separate from access_list, because
+// it exists in a slightly different context, and can consume
+// ACCESS_SPECs greedily, without having to look ahead.
+class_access_list
+  @init { Marker marker = mark(); }
+  : ACCESS_SPEC*
+  { marker.done(SmaliElementTypes.MODIFIER_LIST); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+access_list
+  @init { Marker marker = mark(); }
+  : ACCESS_SPEC*
+  { marker.done(SmaliElementTypes.MODIFIER_LIST); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+/*When there are annotations immediately after a field definition, we don't know whether they are field annotations
+or class annotations until we determine if there is an .end field directive. In either case, we still "consume" and parse
+the annotations. If it turns out that they are field annotations, we include them in the I_FIELD AST. Otherwise, we
+add them to the $smali_file::classAnnotations list*/
+field
+  @init {
+    Marker marker = mark();
+    Marker annotationsMarker = null;
+    boolean gotEndField = false;
+  }
+  : FIELD_DIRECTIVE
+    access_list
+    member_name colon nonvoid_type_descriptor
+    field_initializer?
+    (
+       (ANNOTATION_DIRECTIVE)=> (
+         { annotationsMarker = mark(); }
+         ((ANNOTATION_DIRECTIVE)=> annotation)+
+       )
+    )?
+    ( end_field_directive { gotEndField = true; } )?
+  {
+    if (annotationsMarker != null) {
+      if (gotEndField) {
+        annotationsMarker.drop();
+        marker.done(SmaliElementTypes.FIELD);
+      } else {
+        marker.doneBefore(SmaliElementTypes.FIELD, annotationsMarker);
+        annotationsMarker.drop();
+      }
+    } else {
+      marker.done(SmaliElementTypes.FIELD);
+    }
+  };
+  catch [RecognitionException re] {
+    if (annotationsMarker != null) {
+        annotationsMarker.drop();
+    }
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+end_field_directive
+  : END_FIELD_DIRECTIVE;
+
+field_initializer
+  @init { Marker marker = mark(); }
+  : EQUAL literal
+  { marker.done(SmaliElementTypes.FIELD_INITIALIZER); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+method
+  @init {
+    Marker marker = mark();
+    mark().done(SmaliElementTypes.THROWS_LIST);
+  }
+  : METHOD_DIRECTIVE access_list member_name method_prototype statements_and_directives
+    end_method_directive
+  { marker.done(SmaliElementTypes.METHOD); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+end_method_directive
+  : END_METHOD_DIRECTIVE;
+catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+statements_and_directives
+  : (
+      ( ordered_method_item
+      | registers_directive
+      | catch_directive
+      | catchall_directive
+      | parameter_directive
+      | annotation
+      )
+      sync[false]
+    )*;
+
+/* Method items whose order/location is important */
+ordered_method_item
+  : label
+  | instruction
+  | debug_directive;
+
+registers_directive
+  @init { Marker marker = mark(); }
+  : (
+      REGISTERS_DIRECTIVE integral_literal
+    | LOCALS_DIRECTIVE integral_literal
+    )
+  { marker.done(SmaliElementTypes.REGISTERS_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+param_list_or_id
+  : PARAM_LIST_OR_ID_PRIMITIVE_TYPE+;
+
+/*identifiers are much more general than most languages. Any of the below can either be
+the indicated type OR an identifier, depending on the context*/
+simple_name
+  : SIMPLE_NAME
+  | ACCESS_SPEC
+  | VERIFICATION_ERROR_TYPE
+  | POSITIVE_INTEGER_LITERAL
+  | NEGATIVE_INTEGER_LITERAL
+  | FLOAT_LITERAL_OR_ID
+  | DOUBLE_LITERAL_OR_ID
+  | BOOL_LITERAL
+  | NULL_LITERAL
+  | register
+  | param_list_or_id
+  | PRIMITIVE_TYPE
+  | VOID_TYPE
+  | ANNOTATION_VISIBILITY
+  | INSTRUCTION_FORMAT10t
+  | INSTRUCTION_FORMAT10x
+  | INSTRUCTION_FORMAT10x_ODEX
+  | INSTRUCTION_FORMAT11x
+  | INSTRUCTION_FORMAT12x_OR_ID
+  | INSTRUCTION_FORMAT21c_FIELD
+  | INSTRUCTION_FORMAT21c_FIELD_ODEX
+  | INSTRUCTION_FORMAT21c_STRING
+  | INSTRUCTION_FORMAT21c_TYPE
+  | INSTRUCTION_FORMAT21t
+  | INSTRUCTION_FORMAT22c_FIELD
+  | INSTRUCTION_FORMAT22c_FIELD_ODEX
+  | INSTRUCTION_FORMAT22c_TYPE
+  | INSTRUCTION_FORMAT22cs_FIELD
+  | INSTRUCTION_FORMAT22s_OR_ID
+  | INSTRUCTION_FORMAT22t
+  | INSTRUCTION_FORMAT23x
+  | INSTRUCTION_FORMAT31i_OR_ID
+  | INSTRUCTION_FORMAT31t
+  | INSTRUCTION_FORMAT35c_METHOD
+  | INSTRUCTION_FORMAT35c_METHOD_ODEX
+  | INSTRUCTION_FORMAT35c_TYPE
+  | INSTRUCTION_FORMAT35mi_METHOD
+  | INSTRUCTION_FORMAT35ms_METHOD
+  | INSTRUCTION_FORMAT51l;
+
+member_name
+  @init { Marker marker = mark(); }
+  : member_name_inner
+  { marker.done(SmaliElementTypes.MEMBER_NAME); };
+
+member_name_inner
+  : (simple_name
+    | MEMBER_NAME);
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+method_prototype
+  @init { Marker marker = mark(); }
+  : open_paren param_list close_paren type_descriptor
+    { marker.done(SmaliElementTypes.METHOD_PROTOTYPE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+open_paren
+  : OPEN_PAREN;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+close_paren
+  : CLOSE_PAREN;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+open_brace
+  : OPEN_BRACE;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+close_brace
+  : CLOSE_BRACE;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+comma
+  : COMMA;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+colon
+  : COLON;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+dotdot
+  : DOTDOT;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+param_list_inner
+  : param+;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+param_list
+  @init { Marker marker = mark(); }
+  : param_list_inner?
+    { marker.done(SmaliElementTypes.METHOD_PARAM_LIST); };
+
+param
+  @init {
+    Marker marker = mark();
+    mark().done(SmaliElementTypes.MODIFIER_LIST);
+  }
+  : nonvoid_type_descriptor
+  { marker.done(SmaliElementTypes.METHOD_PARAMETER); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+method_prototype_reference
+  : open_paren param_list_reference close_paren type_descriptor;
+
+param_list_reference
+  @init {
+    Marker marker = mark();
+  }
+  : nonvoid_type_descriptor*
+  { marker.done(SmaliElementTypes.METHOD_REFERENCE_PARAM_LIST); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+primitive_type
+  @init { Marker marker = mark(); }
+  : (PRIMITIVE_TYPE | PARAM_LIST_OR_ID_PRIMITIVE_TYPE)
+  { finishToken(marker, SmaliElementTypes.PRIMITIVE_TYPE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+class_descriptor
+  @init { Marker marker = mark(); }
+  : CLASS_DESCRIPTOR
+  { finishToken(marker, SmaliElementTypes.CLASS_TYPE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+array_descriptor
+  @init { Marker marker = mark(); }
+  : ARRAY_TYPE_PREFIX (primitive_type | class_descriptor)
+  { finishToken(marker, SmaliElementTypes.ARRAY_TYPE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+void_type
+  @init { Marker marker = mark(); }
+  : VOID_TYPE
+  { finishToken(marker, SmaliElementTypes.VOID_TYPE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+type_descriptor
+  : void_type
+  | primitive_type
+  | class_descriptor
+  | array_descriptor;
+  catch [RecognitionException re] {
+    Marker marker = mark();
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+nonvoid_type_descriptor
+  : primitive_type
+  | class_descriptor
+  | array_descriptor;
+  catch [RecognitionException re] {
+    Marker marker = mark();
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+reference_type_descriptor
+  : class_descriptor
+  | array_descriptor;
+  catch [RecognitionException re] {
+    Marker marker = mark();
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+null_literal
+  @init { Marker marker = mark(); }
+  : NULL_LITERAL
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+bool_literal
+  @init { Marker marker = mark(); }
+  : BOOL_LITERAL
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+byte_literal
+  @init { Marker marker = mark(); }
+  : BYTE_LITERAL
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+char_literal
+  @init { Marker marker = mark(); }
+  : CHAR_LITERAL
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+short_literal
+  @init { Marker marker = mark(); }
+  : SHORT_LITERAL
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+integer_literal
+  @init { Marker marker = mark(); }
+  : ( POSITIVE_INTEGER_LITERAL
+    | NEGATIVE_INTEGER_LITERAL)
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+long_literal
+  @init { Marker marker = mark(); }
+  : LONG_LITERAL
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+float_literal
+  @init { Marker marker = mark(); }
+  : ( FLOAT_LITERAL_OR_ID
+    | FLOAT_LITERAL )
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+double_literal
+  @init { Marker marker = mark(); }
+  : ( DOUBLE_LITERAL_OR_ID
+    | DOUBLE_LITERAL)
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+string_literal
+  @init { Marker marker = mark(); }
+  : STRING_LITERAL
+  { finishToken(marker, SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+array_literal
+  @init { Marker marker = mark(); }
+  : open_brace (literal (comma literal)* | ) close_brace
+  { marker.done(SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+enum_literal
+  @init { Marker marker = mark(); }
+  : ENUM_DIRECTIVE fully_qualified_field
+  { marker.done(SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+type_field_method_literal
+  @init { Marker marker = mark(); }
+  : ( type_descriptor
+    | fully_qualified_field
+    | fully_qualified_method)
+  { marker.done(SmaliElementTypes.LITERAL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+subannotation
+  @init {
+    Marker marker = mark();
+    Marker paramListMarker = null;
+  }
+  : SUBANNOTATION_DIRECTIVE class_descriptor
+    { paramListMarker = mark(); }
+    annotation_element*
+    { paramListMarker.done(SmaliElementTypes.ANNOTATION_PARAMETER_LIST); }
+    end_subannotation_directive
+    { marker.done(SmaliElementTypes.ANNOTATION); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+end_subannotation_directive
+  : END_SUBANNOTATION_DIRECTIVE;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+literal
+  : long_literal
+  | integer_literal
+  | short_literal
+  | byte_literal
+  | float_literal
+  | double_literal
+  | char_literal
+  | string_literal
+  | bool_literal
+  | null_literal
+  | array_literal
+  | subannotation
+  | type_field_method_literal
+  | enum_literal;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+string_or_null_literal
+  : string_literal
+  | null_literal;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+integral_literal
+  : long_literal
+  | integer_literal
+  | short_literal
+  | char_literal
+  | byte_literal;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+fixed_32bit_literal
+  : long_literal
+  | integer_literal
+      | short_literal
+  | byte_literal
+  | float_literal
+  | char_literal
+  | bool_literal;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+fixed_literal
+  : integer_literal
+  | long_literal
+  | short_literal
+  | byte_literal
+  | float_literal
+  | double_literal
+  | char_literal
+  | bool_literal;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+annotation_element
+  @init {
+    Marker marker = mark();
+    Marker nameMarker = null;
+  }
+  : { nameMarker = mark(); } simple_name { nameMarker.done(SmaliElementTypes.ANNOTATION_ELEMENT_NAME); }
+    equal literal
+  { marker.done(SmaliElementTypes.ANNOTATION_ELEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+equal
+  : EQUAL;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+annotation
+  @init {
+    Marker marker = mark();
+    Marker paramListMarker = null;
+  }
+  : ANNOTATION_DIRECTIVE annotation_visibility class_descriptor
+    { paramListMarker = mark(); }
+    annotation_element*
+    { paramListMarker.done(SmaliElementTypes.ANNOTATION_PARAMETER_LIST); }
+    end_annotation_directive
+  { marker.done(SmaliElementTypes.ANNOTATION); };
+
+annotation_visibility
+  : ANNOTATION_VISIBILITY;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+end_annotation_directive
+  : END_ANNOTATION_DIRECTIVE;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+arrow
+  : ARROW;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+fully_qualified_method
+  @init { Marker marker = mark(); }
+  : reference_type_descriptor arrow member_name method_prototype_reference
+  { marker.done(SmaliElementTypes.METHOD_REFERENCE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+fully_qualified_field
+  @init { Marker marker = mark(); }
+  : reference_type_descriptor arrow member_name colon nonvoid_type_descriptor
+  { marker.done(SmaliElementTypes.FIELD_REFERENCE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+label
+  @init { Marker marker = mark(); }
+  : colon simple_name
+  { marker.done(SmaliElementTypes.LABEL); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+label_ref
+  @init { Marker marker = mark(); }
+  : colon simple_name
+  { marker.done(SmaliElementTypes.LABEL_REFERENCE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+register_list
+  : open_brace (register (comma register)*)? close_brace;
+
+register_range
+  : open_brace (register (dotdot register)?)? close_brace;
+
+verification_error_reference
+  : class_descriptor | fully_qualified_field | fully_qualified_method;
+
+catch_directive
+  @init { Marker marker = mark(); }
+  : CATCH_DIRECTIVE nonvoid_type_descriptor open_brace label_ref dotdot label_ref close_brace label_ref
+  { marker.done(SmaliElementTypes.CATCH_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+catchall_directive
+  @init { Marker marker = mark(); }
+  : CATCHALL_DIRECTIVE open_brace label_ref dotdot label_ref close_brace label_ref
+  { marker.done(SmaliElementTypes.CATCH_ALL_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+/*When there are annotations immediately after a parameter definition, we don't know whether they are parameter annotations
+or method annotations until we determine if there is an .end parameter directive. In either case, we still "consume" and parse
+the annotations. If it turns out that they are parameter annotations, we include them in the I_PARAMETER AST. Otherwise, we
+add them to the $statements_and_directives::methodAnnotations list*/
+parameter_directive
+  @init {
+    Marker marker = mark();
+    Marker annotationsMarker = null;
+    boolean gotEndParam = false;
+  }
+  : PARAMETER_DIRECTIVE register
+    (comma local_name)?
+    { annotationsMarker = mark(); } parameter_annotations
+    ( end_parameter_directive { gotEndParam = true; } )?
+  {
+    if (gotEndParam) {
+      annotationsMarker.drop();
+      marker.done(SmaliElementTypes.PARAMETER_STATEMENT);
+    } else {
+      marker.doneBefore(SmaliElementTypes.PARAMETER_STATEMENT, annotationsMarker);
+      annotationsMarker.drop();
+    }
+  };
+  catch [RecognitionException re] {
+    if (annotationsMarker != null) {
+        annotationsMarker.drop();
+    }
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+parameter_annotations
+  : ((ANNOTATION_DIRECTIVE)=> annotation)*;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+end_parameter_directive
+  : END_PARAMETER_DIRECTIVE;
+
+local_name
+  @init {
+    Marker localNameMarker = mark();
+    Marker stringMarker = mark();
+  }
+  : STRING_LITERAL
+  {
+    finishToken(stringMarker, SmaliElementTypes.LITERAL);
+    finishToken(localNameMarker, SmaliElementTypes.LOCAL_NAME);
+  };
+  catch [RecognitionException re] {
+      stringMarker.drop();
+      recover(input, re);
+      reportError(localNameMarker, re, false);
+  }
+
+register
+  @init { Marker marker = mark(); }
+  : REGISTER
+  { finishToken(marker, SmaliElementTypes.REGISTER_REFERENCE); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+debug_directive
+  : line_directive
+  | local_directive
+  | end_local_directive
+  | restart_local_directive
+  | prologue_directive
+  | epilogue_directive
+  | source_directive;
+
+line_directive
+  @init { Marker marker = mark(); }
+  : LINE_DIRECTIVE integral_literal
+  { marker.done(SmaliElementTypes.LINE_DEBUG_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+local_directive
+  @init { Marker marker = mark(); }
+  : LOCAL_DIRECTIVE register (comma string_or_null_literal colon type_descriptor
+                              (comma string_literal)? )?
+  { marker.done(SmaliElementTypes.LOCAL_DEBUG_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+end_local_directive
+  @init { Marker marker = mark(); }
+  : END_LOCAL_DIRECTIVE register
+  { marker.done(SmaliElementTypes.END_LOCAL_DEBUG_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+restart_local_directive
+  @init { Marker marker = mark(); }
+  : RESTART_LOCAL_DIRECTIVE register
+  { marker.done(SmaliElementTypes.RESTART_LOCAL_DEBUG_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+prologue_directive
+  @init { Marker marker = mark(); }
+  : PROLOGUE_DIRECTIVE
+  { marker.done(SmaliElementTypes.PROLOGUE_DEBUG_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+epilogue_directive
+  @init { Marker marker = mark(); }
+  : EPILOGUE_DIRECTIVE
+  { marker.done(SmaliElementTypes.EPILOGUE_DEBUG_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+source_directive
+  @init { Marker marker = mark(); }
+  : SOURCE_DIRECTIVE string_literal?
+  { marker.done(SmaliElementTypes.SOURCE_DEBUG_STATEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+instruction_format12x
+  : INSTRUCTION_FORMAT12x
+  | INSTRUCTION_FORMAT12x_OR_ID;
+
+instruction_format22s
+  : INSTRUCTION_FORMAT22s
+  | INSTRUCTION_FORMAT22s_OR_ID;
+
+instruction_format31i
+  : INSTRUCTION_FORMAT31i
+  | INSTRUCTION_FORMAT31i_OR_ID;
+
+instruction
+  @init { Marker marker = mark(); }
+  : ( insn_format10t
+    | insn_format10x
+    | insn_format10x_odex
+    | insn_format11n
+    | insn_format11x
+    | insn_format12x
+    | insn_format20bc
+    | insn_format20t
+    | insn_format21c_field
+    | insn_format21c_field_odex
+    | insn_format21c_string
+      | insn_format21c_type
+      | insn_format21ih
+      | insn_format21lh
+      | insn_format21s
+      | insn_format21t
+      | insn_format22b
+      | insn_format22c_field
+      | insn_format22c_field_odex
+      | insn_format22c_type
+      | insn_format22cs_field
+      | insn_format22s
+      | insn_format22t
+      | insn_format22x
+      | insn_format23x
+      | insn_format30t
+      | insn_format31c
+      | insn_format31i
+      | insn_format31t
+      | insn_format32x
+      | insn_format35c_method
+      | insn_format35c_type
+      | insn_format35c_method_odex
+      | insn_format35mi_method
+      | insn_format35ms_method
+      | insn_format3rc_method
+      | insn_format3rc_method_odex
+      | insn_format3rc_type
+      | insn_format3rmi_method
+      | insn_format3rms_method
+      | insn_format51l
+      | insn_array_data_directive
+      | insn_packed_switch_directive
+      | insn_sparse_switch_directive )
+  { marker.done(SmaliElementTypes.INSTRUCTION); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+insn_format10t
+  : //e.g. goto endloop:
+    //e.g. goto +3
+    INSTRUCTION_FORMAT10t label_ref;
+
+insn_format10x
+  : //e.g. return-void
+    INSTRUCTION_FORMAT10x;
+
+insn_format10x_odex
+  : //e.g. return-void-barrier
+    INSTRUCTION_FORMAT10x_ODEX;
+
+insn_format11n
+  : //e.g. const/4 v0, 5
+    INSTRUCTION_FORMAT11n register comma integral_literal;
+
+insn_format11x
+  : //e.g. move-result-object v1
+    INSTRUCTION_FORMAT11x register;
+
+insn_format12x
+  : //e.g. move v1 v2
+    instruction_format12x register comma register;
+
+insn_format20bc
+  : //e.g. throw-verification-error generic-error, Lsome/class;
+    INSTRUCTION_FORMAT20bc VERIFICATION_ERROR_TYPE comma verification_error_reference;
+
+insn_format20t
+  : //e.g. goto/16 endloop:
+    INSTRUCTION_FORMAT20t label_ref;
+
+insn_format21c_field
+  : //e.g. sget-object v0, java/lang/System/out LJava/io/PrintStream;
+    INSTRUCTION_FORMAT21c_FIELD register comma fully_qualified_field;
+
+insn_format21c_field_odex
+  : //e.g. sget-object-volatile v0, java/lang/System/out LJava/io/PrintStream;
+    INSTRUCTION_FORMAT21c_FIELD_ODEX register comma fully_qualified_field;
+
+insn_format21c_string
+  : //e.g. const-string v1, "Hello World!"
+    INSTRUCTION_FORMAT21c_STRING register comma string_literal;
+
+insn_format21c_type
+  : //e.g. const-class v2, Lorg/jf/HelloWorld2/HelloWorld2;
+    INSTRUCTION_FORMAT21c_TYPE register comma nonvoid_type_descriptor;
+
+insn_format21ih
+  : //e.g. const/high16 v1, 1234
+    INSTRUCTION_FORMAT21ih register comma fixed_32bit_literal;
+
+insn_format21lh
+  : //e.g. const-wide/high16 v1, 1234
+    INSTRUCTION_FORMAT21lh register comma fixed_32bit_literal;
+
+insn_format21s
+  : //e.g. const/16 v1, 1234
+    INSTRUCTION_FORMAT21s register comma integral_literal;
+
+insn_format21t
+  : //e.g. if-eqz v0, endloop:
+    INSTRUCTION_FORMAT21t register comma label_ref;
+
+insn_format22b
+  : //e.g. add-int v0, v1, 123
+    INSTRUCTION_FORMAT22b register comma register comma integral_literal;
+
+insn_format22c_field
+  : //e.g. iput-object v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String;
+    INSTRUCTION_FORMAT22c_FIELD register comma register comma fully_qualified_field;
+
+insn_format22c_field_odex
+  : //e.g. iput-object-volatile v1, v0 org/jf/HelloWorld2/HelloWorld2.helloWorld Ljava/lang/String;
+    INSTRUCTION_FORMAT22c_FIELD_ODEX register comma register comma fully_qualified_field;
+
+insn_format22c_type
+  : //e.g. instance-of v0, v1, Ljava/lang/String;
+    INSTRUCTION_FORMAT22c_TYPE register comma register comma nonvoid_type_descriptor;
+
+insn_format22cs_field
+  : //e.g. iget-quick v0, v1, field@0xc
+    INSTRUCTION_FORMAT22cs_FIELD register comma register comma FIELD_OFFSET;
+
+insn_format22s
+  : //e.g. add-int/lit16 v0, v1, 12345
+    instruction_format22s register comma register comma integral_literal;
+
+insn_format22t
+  : //e.g. if-eq v0, v1, endloop:
+    INSTRUCTION_FORMAT22t register comma register comma label_ref;
+
+insn_format22x
+  : //e.g. move/from16 v1, v1234
+    INSTRUCTION_FORMAT22x register comma register;
+
+insn_format23x
+  : //e.g. add-int v1, v2, v3
+    INSTRUCTION_FORMAT23x register comma register comma register;
+
+insn_format30t
+  : //e.g. goto/32 endloop:
+    INSTRUCTION_FORMAT30t label_ref;
+
+insn_format31c
+  : //e.g. const-string/jumbo v1 "Hello World!"
+    INSTRUCTION_FORMAT31c register comma string_literal;
+
+insn_format31i
+  : //e.g. const v0, 123456
+    instruction_format31i register comma fixed_32bit_literal;
+
+insn_format31t
+  : //e.g. fill-array-data v0, ArrayData:
+    INSTRUCTION_FORMAT31t register comma label_ref;
+
+insn_format32x
+  : //e.g. move/16 v4567, v1234
+    INSTRUCTION_FORMAT32x register comma register;
+
+insn_format35c_method
+  : //e.g. invoke-virtual {v0,v1} java/io/PrintStream/print(Ljava/lang/Stream;)V
+    INSTRUCTION_FORMAT35c_METHOD register_list comma fully_qualified_method;
+
+insn_format35c_type
+  : //e.g. filled-new-array {v0,v1}, I
+    INSTRUCTION_FORMAT35c_TYPE register_list comma nonvoid_type_descriptor;
+
+insn_format35c_method_odex
+  : //e.g. invoke-direct {p0}, Ljava/lang/Object;-><init>()V
+    INSTRUCTION_FORMAT35c_METHOD_ODEX register_list comma fully_qualified_method;
+
+insn_format35mi_method
+  : //e.g. execute-inline {v0, v1}, inline@0x4
+    INSTRUCTION_FORMAT35mi_METHOD register_list comma INLINE_INDEX;
+
+insn_format35ms_method
+  : //e.g. invoke-virtual-quick {v0, v1}, vtable@0x4
+    INSTRUCTION_FORMAT35ms_METHOD register_list comma VTABLE_INDEX;
+
+insn_format3rc_method
+  : //e.g. invoke-virtual/range {v25..v26}, java/lang/StringBuilder/append(Ljava/lang/String;)Ljava/lang/StringBuilder;
+    INSTRUCTION_FORMAT3rc_METHOD register_range comma fully_qualified_method;
+
+insn_format3rc_method_odex
+  : //e.g. invoke-object-init/range {p0}, Ljava/lang/Object;-><init>()V
+    INSTRUCTION_FORMAT3rc_METHOD_ODEX register_list comma fully_qualified_method;
+
+insn_format3rc_type
+  : //e.g. filled-new-array/range {v0..v6}, I
+    INSTRUCTION_FORMAT3rc_TYPE register_range comma nonvoid_type_descriptor;
+
+insn_format3rmi_method
+  : //e.g. execute-inline/range {v0 .. v10}, inline@0x14
+    INSTRUCTION_FORMAT3rmi_METHOD register_range comma INLINE_INDEX;
+
+insn_format3rms_method
+  : //e.g. invoke-virtual-quick/range {v0 .. v10}, vtable@0x14
+    INSTRUCTION_FORMAT3rms_METHOD register_range comma VTABLE_INDEX;
+
+insn_format51l
+  : //e.g. const-wide v0, 5000000000L
+    INSTRUCTION_FORMAT51l register comma fixed_literal;
+
+insn_array_data_directive
+  : ARRAY_DATA_DIRECTIVE
+    integer_literal
+    array_data_element* end_array_data_directive;
+
+end_array_data_directive
+  : END_ARRAY_DATA_DIRECTIVE;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+array_data_element
+  @init { Marker marker = mark(); }
+  : fixed_literal
+  { marker.done(SmaliElementTypes.ARRAY_DATA_ELEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+insn_packed_switch_directive
+  : PACKED_SWITCH_DIRECTIVE
+    fixed_32bit_literal
+    packed_switch_element*
+    end_packed_switch_directive;
+
+end_packed_switch_directive
+  : END_PACKED_SWITCH_DIRECTIVE;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+packed_switch_element
+  @init { Marker marker = mark(); }
+  : label_ref
+  { marker.done(SmaliElementTypes.PACKED_SWITCH_ELEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
+
+insn_sparse_switch_directive
+  : SPARSE_SWITCH_DIRECTIVE
+    sparse_switch_element*
+    end_sparse_switch_directive;
+
+end_sparse_switch_directive
+  : END_SPARSE_SWITCH_DIRECTIVE;
+  catch [RecognitionException re] {
+    Marker errorMarker = mark();
+    recover(input, re);
+    reportError(errorMarker, re, false);
+  }
+
+sparse_switch_element
+  @init { Marker marker = mark(); }
+  : fixed_32bit_literal arrow label_ref
+  { marker.done(SmaliElementTypes.SPARSE_SWITCH_ELEMENT); };
+  catch [RecognitionException re] {
+    recover(input, re);
+    reportError(marker, re, false);
+  }
diff --git a/smalidea/src/main/java/org/jf/smalidea/PsiBuilderTokenStream.java b/smalidea/src/main/java/org/jf/smalidea/PsiBuilderTokenStream.java
new file mode 100644
index 0000000..0000400
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/PsiBuilderTokenStream.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.PsiBuilder.Marker;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.tree.IElementType;
+import org.antlr.runtime.CommonToken;
+import org.antlr.runtime.Token;
+import org.antlr.runtime.TokenSource;
+import org.antlr.runtime.TokenStream;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smali.InvalidToken;
+import org.jf.smali.smaliParser;
+
+import javax.annotation.Nonnull;
+import java.util.ArrayList;
+
+public class PsiBuilderTokenStream implements TokenStream {
+    @Nonnull private PsiBuilder psiBuilder;
+    @Nullable private CommonToken currentToken = null;
+    @Nonnull private ArrayList<Marker> markers = new ArrayList<PsiBuilder.Marker>();
+
+    public PsiBuilderTokenStream(@Nonnull PsiBuilder psiBuilder) {
+        this.psiBuilder = psiBuilder;
+    }
+
+    @Override public Token LT(int k) {
+        if (k == 1) {
+            if (currentToken == null) {
+                buildCurrentToken();
+            }
+            return currentToken;
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public int range() {
+        return currentToken==null?0:1;
+    }
+
+    @Override public Token get(int i) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public TokenSource getTokenSource() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public String toString(int start, int stop) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public String toString(Token start, Token stop) {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public void consume() {
+        psiBuilder.advanceLexer();
+        buildCurrentToken();
+    }
+
+    private void buildCurrentToken() {
+        IElementType element = psiBuilder.getTokenType();
+        if (element != null) {
+            if (element instanceof SmaliLexicalElementType) {
+                SmaliLexicalElementType elementType = (SmaliLexicalElementType)element;
+                currentToken = new CommonToken(elementType.tokenId, psiBuilder.getTokenText());
+            } else if (element == TokenType.BAD_CHARACTER) {
+                currentToken = new InvalidToken("", psiBuilder.getTokenText());
+            } else {
+                throw new UnsupportedOperationException();
+            }
+        } else {
+            currentToken = new CommonToken(Token.EOF);
+        }
+    }
+
+    @Override public int LA(int i) {
+        IElementType elementType = psiBuilder.lookAhead(i-1);
+        if (elementType == null) {
+            return -1;
+        } else if (elementType instanceof SmaliLexicalElementType) {
+            return ((SmaliLexicalElementType)elementType).tokenId;
+        } else if (elementType == TokenType.BAD_CHARACTER) {
+            return smaliParser.INVALID_TOKEN;
+        }
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public int mark() {
+        int ret = markers.size();
+        markers.add(psiBuilder.mark());
+        return ret;
+    }
+
+    @Override public int index() {
+        return psiBuilder.getCurrentOffset();
+    }
+
+    @Override public void rewind(int markerIndex) {
+        PsiBuilder.Marker marker = markers.get(markerIndex);
+        marker.rollbackTo();
+        while (markerIndex < markers.size()) {
+            markers.remove(markerIndex);
+        }
+    }
+
+    @Override public void rewind() {
+        rewind(markers.size()-1);
+        mark();
+    }
+
+    @Override public void release(int markerIndex) {
+        while (markerIndex < markers.size()) {
+            markers.remove(markerIndex).drop();
+        }
+    }
+
+    @Override public void seek(int index) {
+        if (index < psiBuilder.getCurrentOffset()) {
+            throw new UnsupportedOperationException();
+        }
+        while (index > psiBuilder.getCurrentOffset()) {
+            consume();
+        }
+    }
+
+    @Override public int size() {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public String getSourceName() {
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliASTFactory.java b/smalidea/src/main/java/org/jf/smalidea/SmaliASTFactory.java
new file mode 100644
index 0000000..4e0208c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliASTFactory.java
@@ -0,0 +1,19 @@
+package org.jf.smalidea;
+
+import com.intellij.lang.ASTFactory;
+import com.intellij.psi.impl.source.tree.LeafElement;
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.leaf.SmaliClassDescriptor;
+
+public class SmaliASTFactory extends ASTFactory {
+
+    @Nullable
+    @Override
+    public LeafElement createLeaf(IElementType type, CharSequence text) {
+        if (type == SmaliTokens.CLASS_DESCRIPTOR) {
+            return new SmaliClassDescriptor(text);
+        }
+        return super.createLeaf(type, text);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliColorsPage.java b/smalidea/src/main/java/org/jf/smalidea/SmaliColorsPage.java
new file mode 100644
index 0000000..b0ed5c0
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliColorsPage.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.fileTypes.SyntaxHighlighter;
+import com.intellij.openapi.options.colors.AttributesDescriptor;
+import com.intellij.openapi.options.colors.ColorDescriptor;
+import com.intellij.openapi.options.colors.ColorSettingsPage;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import javax.swing.*;
+import java.util.List;
+import java.util.Map;
+
+public class SmaliColorsPage implements ColorSettingsPage {
+    private static final AttributesDescriptor[] ATTRS;
+
+    static {
+        List<TextAttributesKey> keys = SmaliHighlightingColors.getAllKeys();
+
+        ATTRS = new AttributesDescriptor[keys.size()];
+        for (int i=0; i<keys.size(); i++) {
+            TextAttributesKey key = keys.get(i);
+
+            ATTRS[i] = new AttributesDescriptor(key.getExternalName(), key);
+        }
+    }
+
+    @Nullable @Override public Icon getIcon() {
+        return SmaliIcons.SmaliIcon;
+    }
+
+    @NotNull @Override public SyntaxHighlighter getHighlighter() {
+        return new SmaliHighlighter();
+    }
+
+    @NotNull @Override public String getDemoText() {
+        return ".class public Lorg/jf/smalidea/ColorExample;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".source \"ColorExample.smali\"\n" +
+                "\n" +
+                ".field public exampleField:I = 1234\n" +
+                "\n" +
+                ".field public boolField:Z = true\n" +
+                "\n" +
+                "# This is an example comment\n" +
+                "\n" +
+                ".method public constructor <init>()V\n" +
+                "    .registers 1\n" +
+                "    invoke-direct {p0}, Ljava/lang/Object;-><init>()V\n" +
+                "    return-void\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public exampleMethod()V\n" +
+                "    .registers 10\n" +
+                "\n" +
+                "    const v0, 1234\n" +
+                "    const-string v1, \"An Example String\"\n" +
+                "\n" +
+                "    invoke-virtual {p0, v0, v1}, Lorg/jf/smalidea/ColorExample;->anotherMethod(ILjava/lang/String;)V\n" +
+                "\n" +
+                "    move v2, v1\n" +
+                "    move v1, v0\n" +
+                "    move v0, p0\n" +
+                "\n" +
+                "    invoke-virtual/range {v0 .. v2}, Lorg/jf/smalidea/ColorExample;->anotherMethod(ILjava/lang/String;)V\n" +
+                "\n" +
+                "    return-void\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public anotherMethod(ILjava/Lang/String;)V\n" +
+                "    .registers 10\n" +
+                "\n" +
+                "    # This is another example comment\n" +
+                "\n" +
+                "    return-void\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public odexInstructions()V\n" +
+                "    .registers 10\n" +
+                "    invoke-virtual {p0}, vtable@0x1b\n" +
+                "\n" +
+                "    iget-quick p0, field@0x1\n" +
+                "\n" +
+                "    execute-inline {p0}, inline@0xa\n" +
+                "\n" +
+                "    throw-verification-error illegal-method-access, Lblah;->Blort()V\n" +
+                ".end method";
+    }
+
+    @NotNull @Override public AttributesDescriptor[] getAttributeDescriptors() {
+        return ATTRS;
+    }
+
+    @Nullable @Override public Map<String, TextAttributesKey> getAdditionalHighlightingTagToDescriptorMap() {
+        return null;
+    }
+
+    @NotNull @Override public ColorDescriptor[] getColorDescriptors() {
+        return ColorDescriptor.EMPTY_ARRAY;
+    }
+
+    @NotNull @Override public String getDisplayName() {
+        return "smali";
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliFileType.java b/smalidea/src/main/java/org/jf/smalidea/SmaliFileType.java
new file mode 100644
index 0000000..e840bb8
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliFileType.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import org.jetbrains.annotations.NotNull;
+
+import javax.swing.*;
+
+public class SmaliFileType extends LanguageFileType {
+    public static final SmaliFileType INSTANCE = new SmaliFileType();
+    public static final String DEFAULT_EXTENSION = "smali";
+
+    private SmaliFileType() {
+        super(SmaliLanguage.INSTANCE);
+    }
+
+    @NotNull @Override public String getName() {
+        return "smali";
+    }
+
+    @NotNull @Override public String getDescription() {
+        return "smali Files";
+    }
+
+    @NotNull @Override public String getDefaultExtension() {
+        return DEFAULT_EXTENSION;
+    }
+
+    @Override public Icon getIcon() {
+        return SmaliIcons.SmaliIcon;
+    }
+
+    @Override public boolean isJVMDebuggingSupported() {
+        return true;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliFileTypeFactory.java b/smalidea/src/main/java/org/jf/smalidea/SmaliFileTypeFactory.java
new file mode 100644
index 0000000..b5834c1
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliFileTypeFactory.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.fileTypes.FileTypeConsumer;
+import com.intellij.openapi.fileTypes.FileTypeFactory;
+import org.jetbrains.annotations.NotNull;
+
+public class SmaliFileTypeFactory extends FileTypeFactory {
+    @Override
+    public void createFileTypes(@NotNull FileTypeConsumer consumer) {
+        consumer.consume(SmaliFileType.INSTANCE, SmaliFileType.DEFAULT_EXTENSION);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliHighlighter.java b/smalidea/src/main/java/org/jf/smalidea/SmaliHighlighter.java
new file mode 100644
index 0000000..71e206a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliHighlighter.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.openapi.fileTypes.SyntaxHighlighterBase;
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.NotNull;
+
+
+public class SmaliHighlighter extends SyntaxHighlighterBase {
+    @NotNull @Override public Lexer getHighlightingLexer() {
+        return new SmaliLexer();
+    }
+
+    @NotNull @Override public TextAttributesKey[] getTokenHighlights(IElementType tokenType) {
+        if (tokenType instanceof SmaliLexicalElementType) {
+            return ((SmaliLexicalElementType) tokenType).textAttributesKeys;
+        }
+        return new TextAttributesKey[] {};
+    }
+
+    // TODO: implement context sensitive highlighting. i.e. instance fields vs static fields, labels, etc. See: HighlightVisitorImpl
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliHighlightingColors.java b/smalidea/src/main/java/org/jf/smalidea/SmaliHighlightingColors.java
new file mode 100644
index 0000000..eba5fd3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliHighlightingColors.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.google.common.collect.Lists;
+import com.intellij.openapi.editor.DefaultLanguageHighlighterColors;
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+
+import java.util.Collections;
+import java.util.List;
+
+public class SmaliHighlightingColors {
+    private static final List<TextAttributesKey> allKeys = Lists.newArrayList();
+
+    public static final TextAttributesKey ACCESS = createTextAttributesKey(
+            "ACCESS", DefaultLanguageHighlighterColors.KEYWORD);
+    public static final TextAttributesKey ARROW = createTextAttributesKey(
+            "ARROW", DefaultLanguageHighlighterColors.PREDEFINED_SYMBOL);
+    public static final TextAttributesKey BRACES = createTextAttributesKey(
+            "BRACES", DefaultLanguageHighlighterColors.BRACES);
+    public static final TextAttributesKey COLON = createTextAttributesKey(
+            "COLON", DefaultLanguageHighlighterColors.PREDEFINED_SYMBOL);
+    public static final TextAttributesKey COMMA = createTextAttributesKey(
+            "COMMA", DefaultLanguageHighlighterColors.COMMA);
+    public static final TextAttributesKey COMMENT = createTextAttributesKey(
+            "COMMENT", DefaultLanguageHighlighterColors.LINE_COMMENT);
+    public static final TextAttributesKey DIRECTIVE = createTextAttributesKey(
+            "DIRECTIVE", DefaultLanguageHighlighterColors.KEYWORD);
+    public static final TextAttributesKey DOTDOT = createTextAttributesKey(
+            "DOTDOT", DefaultLanguageHighlighterColors.PREDEFINED_SYMBOL);
+    public static final TextAttributesKey EQUAL = createTextAttributesKey(
+            "EQUAL", DefaultLanguageHighlighterColors.PREDEFINED_SYMBOL);
+    public static final TextAttributesKey IDENTIFIER = createTextAttributesKey(
+            "IDENTIFIER", DefaultLanguageHighlighterColors.INSTANCE_METHOD);
+    public static final TextAttributesKey INSTRUCTION = createTextAttributesKey(
+            "INSTRUCTION", DefaultLanguageHighlighterColors.KEYWORD);
+    public static final TextAttributesKey LITERAL = createTextAttributesKey(
+            "LITERAL", DefaultLanguageHighlighterColors.NUMBER);
+    public static final TextAttributesKey NUMBER = createTextAttributesKey(
+            "NUMBER", DefaultLanguageHighlighterColors.NUMBER);
+    public static final TextAttributesKey ODEX_REFERENCE = createTextAttributesKey(
+            "ODEX_REFERENCE", DefaultLanguageHighlighterColors.INSTANCE_METHOD);
+    public static final TextAttributesKey PARENS = createTextAttributesKey(
+            "PARENS", DefaultLanguageHighlighterColors.PARENTHESES);
+    public static final TextAttributesKey REGISTER = createTextAttributesKey(
+            "REGISTER", DefaultLanguageHighlighterColors.LOCAL_VARIABLE);
+    public static final TextAttributesKey STRING = createTextAttributesKey(
+            "STRING", DefaultLanguageHighlighterColors.STRING);
+    public static final TextAttributesKey TYPE = createTextAttributesKey(
+            "TYPE", DefaultLanguageHighlighterColors.CLASS_REFERENCE);
+    public static final TextAttributesKey VERIFICATION_ERROR_TYPE = createTextAttributesKey(
+            "VERIFICATION_ERROR_TYPE", DefaultLanguageHighlighterColors.KEYWORD);
+
+    private static TextAttributesKey createTextAttributesKey(String name, TextAttributesKey defaultColor) {
+        TextAttributesKey key = TextAttributesKey.createTextAttributesKey(name, defaultColor);
+        allKeys.add(key);
+        return key;
+    }
+
+    public static List<TextAttributesKey> getAllKeys() {
+        return Collections.unmodifiableList(allKeys);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliIcons.java b/smalidea/src/main/java/org/jf/smalidea/SmaliIcons.java
new file mode 100644
index 0000000..449e71b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliIcons.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.util.IconLoader;
+
+import javax.swing.*;
+
+public class SmaliIcons {
+    public static final Icon SmaliIcon = IconLoader.getIcon("/icons/smali.png");
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliLanguage.java b/smalidea/src/main/java/org/jf/smalidea/SmaliLanguage.java
new file mode 100644
index 0000000..f437ec2
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliLanguage.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.lang.Language;
+
+public class SmaliLanguage extends Language {
+    public static final SmaliLanguage INSTANCE = new SmaliLanguage();
+
+    private SmaliLanguage() {
+        super("smali", "text/smali");
+    }
+
+    @Override public boolean isCaseSensitive() {
+        return true;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliLexer.java b/smalidea/src/main/java/org/jf/smalidea/SmaliLexer.java
new file mode 100644
index 0000000..67e5945
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliLexer.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.lexer.LexerBase;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.tree.IElementType;
+import org.antlr.runtime.CommonToken;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smali.smaliFlexLexer;
+import org.jf.smali.smaliParser;
+import org.jf.util.BlankReader;
+
+public class SmaliLexer extends LexerBase {
+    private final smaliFlexLexer lexer = new smaliFlexLexer(BlankReader.INSTANCE);
+    private CommonToken token = null;
+    private int state = 0;
+    private int endOffset;
+    private CharSequence text;
+
+    public SmaliLexer() {
+        super();
+        lexer.setSuppressErrors(true);
+    }
+
+    @Override public void start(@NotNull CharSequence buffer, int startOffset, int endOffset, int initialState) {
+        text = buffer;
+        lexer.reset(buffer, startOffset, endOffset, initialState);
+        this.endOffset = endOffset;
+        this.token = null;
+        this.state = 0;
+    }
+
+    @NotNull @Override public CharSequence getTokenSequence() {
+        return getTokenText();
+    }
+
+    @NotNull @Override public String getTokenText() {
+        ensureToken();
+        return token.getText();
+    }
+
+    @Override
+    public int getState() {
+        ensureToken();
+        return state;
+    }
+
+    @Override
+    public IElementType getTokenType() {
+        ensureToken();
+        return mapTokenTypeToElementType(token.getType());
+    }
+
+    private IElementType mapTokenTypeToElementType(int tokenType) {
+        if (tokenType == smaliParser.WHITE_SPACE) {
+            return TokenType.WHITE_SPACE;
+        }
+        if (tokenType == smaliParser.INVALID_TOKEN) {
+            return TokenType.BAD_CHARACTER;
+        }
+        if (tokenType == smaliParser.EOF) {
+            return null;
+        }
+        return SmaliTokens.getElementType(tokenType);
+    }
+
+    @Override
+    public int getTokenStart() {
+        ensureToken();
+        return token.getStartIndex();
+    }
+
+    @Override
+    public int getTokenEnd() {
+        ensureToken();
+        return token.getStopIndex()+1;
+    }
+
+    @Override
+    public void advance() {
+        token = null;
+        state = 0;
+    }
+
+    @NotNull @Override public CharSequence getBufferSequence() {
+        return text;
+    }
+
+    @Override
+    public int getBufferEnd() {
+        return endOffset;
+    }
+
+    private void ensureToken() {
+        if (token == null) {
+            token = (CommonToken)lexer.nextToken();
+            state = lexer.yystate();
+        }
+        assert token != null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliLexicalElementType.java b/smalidea/src/main/java/org/jf/smalidea/SmaliLexicalElementType.java
new file mode 100644
index 0000000..5103dd9
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliLexicalElementType.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.psi.tree.IElementType;
+
+public class SmaliLexicalElementType extends IElementType {
+    public final int tokenId;
+    public final TextAttributesKey[] textAttributesKeys;
+
+    protected SmaliLexicalElementType(int tokenId, String tokenName, TextAttributesKey textAttributesKey) {
+        super(tokenName, SmaliLanguage.INSTANCE);
+        this.tokenId = tokenId;
+        this.textAttributesKeys = new TextAttributesKey[] {textAttributesKey};
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliParser.java b/smalidea/src/main/java/org/jf/smalidea/SmaliParser.java
new file mode 100644
index 0000000..ed0fb19
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliParser.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.PsiBuilder;
+import com.intellij.lang.PsiParser;
+import com.intellij.psi.tree.IElementType;
+import org.antlr.runtime.RecognitionException;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliParser implements PsiParser {
+    @NotNull @Override public ASTNode parse(IElementType root, PsiBuilder builder) {
+        builder.setDebugMode(true);
+
+        PsiBuilder.Marker rootMarker = builder.mark();
+        PsiBuilder.Marker classMarker = builder.mark();
+
+        PsiBuilderTokenStream tokenStream = new PsiBuilderTokenStream(builder);
+        smalideaParser parser = new smalideaParser(tokenStream);
+        parser.setPsiBuilder(builder);
+        try {
+            parser.smali_file();
+        } catch (RecognitionException ex) {
+            // TODO: how to handle this?
+            ex.printStackTrace();
+        }
+
+        classMarker.done(SmaliElementTypes.CLASS);
+        rootMarker.done(root);
+        return builder.getTreeBuilt();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliParserDefinition.java b/smalidea/src/main/java/org/jf/smalidea/SmaliParserDefinition.java
new file mode 100644
index 0000000..77f1623
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliParserDefinition.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.lang.LanguageUtil;
+import com.intellij.lang.ParserDefinition;
+import com.intellij.lang.PsiParser;
+import com.intellij.lexer.Lexer;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.FileViewProvider;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.TokenType;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.tree.IFileElementType;
+import com.intellij.psi.tree.TokenSet;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.stub.element.SmaliStubElementType;
+
+public class SmaliParserDefinition implements ParserDefinition {
+    @NotNull @Override public Lexer createLexer(Project project) {
+        return new SmaliLexer();
+    }
+
+    @Override public PsiParser createParser(Project project) {
+        return new SmaliParser();
+    }
+
+    @Override public IFileElementType getFileNodeType() {
+        return SmaliElementTypes.FILE;
+    }
+
+    private static final TokenSet WHITESPACE = TokenSet.create(TokenType.WHITE_SPACE);
+    @NotNull @Override public TokenSet getWhitespaceTokens() {
+        return WHITESPACE;
+    }
+
+    private static final TokenSet COMMENT = TokenSet.create(SmaliTokens.LINE_COMMENT);
+    @NotNull @Override public TokenSet getCommentTokens() {
+        return COMMENT;
+    }
+
+    private static final TokenSet STRING_LITERAL = TokenSet.create(SmaliTokens.STRING_LITERAL);
+    @NotNull @Override public TokenSet getStringLiteralElements() {
+        return STRING_LITERAL;
+    }
+
+    @NotNull @Override public PsiElement createElement(ASTNode node) {
+        IElementType elementType = node.getElementType();
+        if (elementType instanceof SmaliStubElementType) {
+            return ((SmaliStubElementType)elementType).createPsi(node);
+        }
+        throw new RuntimeException("Unexpected element type");
+    }
+
+    @Override public PsiFile createFile(FileViewProvider viewProvider) {
+        return new SmaliFile(viewProvider);
+    }
+
+    @Override public SpaceRequirements spaceExistanceTypeBetweenTokens(ASTNode left, ASTNode right) {
+        return LanguageUtil.canStickTokensTogetherByLexer(left, right, new SmaliLexer());
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/SmaliTokens.java b/smalidea/src/main/java/org/jf/smalidea/SmaliTokens.java
new file mode 100644
index 0000000..8332764
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/SmaliTokens.java
@@ -0,0 +1,360 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.google.common.collect.Maps;
+import com.intellij.openapi.editor.colors.TextAttributesKey;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.tree.TokenSet;
+import org.jf.smali.smaliParser;
+
+import java.lang.reflect.Field;
+import java.util.Map;
+
+public class SmaliTokens {
+    private static final IElementType[] ELEMENT_TYPES;
+
+    public static IElementType getElementType(int tokenType) {
+        return ELEMENT_TYPES[tokenType];
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType ACCESS_SPEC;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType ANNOTATION_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType ANNOTATION_VISIBILITY;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType ARRAY_DATA_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType ARRAY_TYPE_PREFIX;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType ARROW;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType BOOL_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType BYTE_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType CATCH_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType CATCHALL_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType CHAR_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType CLASS_DESCRIPTOR;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType CLASS_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType CLOSE_BRACE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType CLOSE_PAREN;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType COLON;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType COMMA;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType DOTDOT;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType DOUBLE_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType DOUBLE_LITERAL_OR_ID;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_ANNOTATION_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_ARRAY_DATA_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_FIELD_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_LOCAL_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_METHOD_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_PACKED_SWITCH_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_PARAMETER_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_SPARSE_SWITCH_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType END_SUBANNOTATION_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType ENUM_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType EPILOGUE_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType EQUAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType FIELD_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType FIELD_OFFSET;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType FLOAT_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType FLOAT_LITERAL_OR_ID;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType IMPLEMENTS_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INLINE_INDEX;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT10t;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT10x;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT10x_ODEX;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT11n;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT11x;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT12x;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT12x_OR_ID;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT20bc;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT20t;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21c_FIELD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21c_FIELD_ODEX;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21c_STRING;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21c_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21ih;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21lh;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21s;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT21t;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22b;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22c_FIELD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22c_FIELD_ODEX;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22c_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22cs_FIELD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22s;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22s_OR_ID;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22t;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT22x;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT23x;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT30t;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT31c;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT31i;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT31i_OR_ID;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT31t;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT32x;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT35c_METHOD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT35c_METHOD_ODEX;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT35c_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT35mi_METHOD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT35ms_METHOD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT3rc_METHOD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT3rc_METHOD_ODEX;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT3rc_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT3rmi_METHOD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT3rms_METHOD;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType INSTRUCTION_FORMAT51l;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType LINE_COMMENT;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType LINE_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType LOCAL_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType LOCALS_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType LONG_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType METHOD_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType MEMBER_NAME;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType NEGATIVE_INTEGER_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType NULL_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType OPEN_BRACE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType OPEN_PAREN;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType PACKED_SWITCH_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType PARAM_LIST_OR_ID_PRIMITIVE_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType PARAMETER_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType POSITIVE_INTEGER_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType PRIMITIVE_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType PROLOGUE_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType REGISTER;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType REGISTERS_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType RESTART_LOCAL_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType SHORT_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType SIMPLE_NAME;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType SOURCE_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType SPARSE_SWITCH_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType STRING_LITERAL;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType SUBANNOTATION_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType SUPER_DIRECTIVE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType VERIFICATION_ERROR_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType VOID_TYPE;
+    @SuppressWarnings({"UnusedDeclaration"}) public static IElementType VTABLE_INDEX;
+
+    public static final TokenSet INSTRUCTION_TOKENS;
+
+    static {
+        Map<String, TextAttributesKey> tokenColors = Maps.newHashMap();
+
+        tokenColors.put("ACCESS_SPEC", SmaliHighlightingColors.ACCESS);
+        tokenColors.put("ANNOTATION_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("ANNOTATION_VISIBILITY", SmaliHighlightingColors.ACCESS);
+        tokenColors.put("ARRAY_DATA_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("ARRAY_TYPE_PREFIX", SmaliHighlightingColors.TYPE);
+        tokenColors.put("ARROW", SmaliHighlightingColors.ARROW);
+        tokenColors.put("BOOL_LITERAL", SmaliHighlightingColors.LITERAL);
+        tokenColors.put("BYTE_LITERAL", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("CATCH_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("CATCHALL_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("CHAR_LITERAL", SmaliHighlightingColors.STRING);
+        tokenColors.put("CLASS_DESCRIPTOR", SmaliHighlightingColors.TYPE);
+        tokenColors.put("CLASS_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("CLOSE_BRACE", SmaliHighlightingColors.BRACES);
+        tokenColors.put("CLOSE_PAREN", SmaliHighlightingColors.PARENS);
+        tokenColors.put("COLON", SmaliHighlightingColors.COLON);
+        tokenColors.put("COMMA", SmaliHighlightingColors.COMMA);
+        tokenColors.put("DOTDOT", SmaliHighlightingColors.DOTDOT);
+        tokenColors.put("DOUBLE_LITERAL", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("DOUBLE_LITERAL_OR_ID", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("END_ANNOTATION_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_ARRAY_DATA_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_FIELD_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_LOCAL_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_METHOD_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_PACKED_SWITCH_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_PARAMETER_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_SPARSE_SWITCH_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("END_SUBANNOTATION_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("ENUM_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("EPILOGUE_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("EQUAL", SmaliHighlightingColors.EQUAL);
+        tokenColors.put("FIELD_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("FIELD_OFFSET", SmaliHighlightingColors.ODEX_REFERENCE);
+        tokenColors.put("FLOAT_LITERAL", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("FLOAT_LITERAL_OR_ID", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("IMPLEMENTS_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("INLINE_INDEX", SmaliHighlightingColors.ODEX_REFERENCE);
+        tokenColors.put("INSTRUCTION_FORMAT10t", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT10x", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT10x_ODEX", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT11n", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT11x", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT12x", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT12x_OR_ID", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT20bc", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT20t", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21c_FIELD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21c_FIELD_ODEX", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21c_STRING", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21c_TYPE", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21ih", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21lh", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21s", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT21t", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22b", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22c_FIELD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22c_FIELD_ODEX", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22c_TYPE", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22cs_FIELD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22s", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22s_OR_ID", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22t", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT22x", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT23x", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT30t", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT31c", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT31i", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT31i_OR_ID", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT31t", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT32x", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT35c_METHOD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT35c_METHOD_ODEX", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT35c_TYPE", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT35mi_METHOD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT35ms_METHOD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT3rc_METHOD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT3rc_METHOD_ODEX", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT3rc_TYPE", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT3rmi_METHOD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT3rms_METHOD", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("INSTRUCTION_FORMAT51l", SmaliHighlightingColors.INSTRUCTION);
+        tokenColors.put("LINE_COMMENT", SmaliHighlightingColors.COMMENT);
+        tokenColors.put("LINE_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("LOCAL_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("LOCALS_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("LONG_LITERAL", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("MEMBER_NAME", SmaliHighlightingColors.IDENTIFIER);
+        tokenColors.put("METHOD_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("NEGATIVE_INTEGER_LITERAL", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("NULL_LITERAL", SmaliHighlightingColors.LITERAL);
+        tokenColors.put("OPEN_BRACE", SmaliHighlightingColors.BRACES);
+        tokenColors.put("OPEN_PAREN", SmaliHighlightingColors.PARENS);
+        tokenColors.put("PACKED_SWITCH_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("PARAM_LIST_OR_ID_PRIMITIVE_TYPE", SmaliHighlightingColors.TYPE);
+        tokenColors.put("PARAMETER_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("POSITIVE_INTEGER_LITERAL", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("PRIMITIVE_TYPE", SmaliHighlightingColors.TYPE);
+        tokenColors.put("PROLOGUE_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("REGISTER", SmaliHighlightingColors.REGISTER);
+        tokenColors.put("REGISTERS_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("RESTART_LOCAL_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("SHORT_LITERAL", SmaliHighlightingColors.NUMBER);
+        tokenColors.put("SIMPLE_NAME", SmaliHighlightingColors.IDENTIFIER);
+        tokenColors.put("SOURCE_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("SPARSE_SWITCH_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("STRING_LITERAL", SmaliHighlightingColors.STRING);
+        tokenColors.put("SUBANNOTATION_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("SUPER_DIRECTIVE", SmaliHighlightingColors.DIRECTIVE);
+        tokenColors.put("VERIFICATION_ERROR_TYPE", SmaliHighlightingColors.VERIFICATION_ERROR_TYPE);
+        tokenColors.put("VOID_TYPE", SmaliHighlightingColors.TYPE);
+        tokenColors.put("VTABLE_INDEX", SmaliHighlightingColors.ODEX_REFERENCE);
+
+        int tokenCount = smaliParser.tokenNames.length;
+        ELEMENT_TYPES = new IElementType[tokenCount];
+
+        for (int tokenId=0; tokenId<tokenCount; tokenId++) {
+            String tokenName = smaliParser.tokenNames[tokenId];
+            Field field;
+
+            try {
+                field = SmaliTokens.class.getField(tokenName);
+            } catch (NoSuchFieldException ex) {
+                continue;
+            }
+
+            TextAttributesKey textAttributesKey = tokenColors.get(tokenName);
+
+            if (textAttributesKey == null) {
+                throw new RuntimeException("No color attribute for token " + tokenName);
+            }
+
+            SmaliLexicalElementType elementType = new SmaliLexicalElementType(tokenId, tokenName, textAttributesKey);
+            ELEMENT_TYPES[tokenId] = elementType;
+
+            try {
+                field.set(null, elementType);
+            } catch (IllegalAccessException ex) {
+                throw new RuntimeException(ex);
+            }
+        }
+
+        INSTRUCTION_TOKENS = TokenSet.create(
+                INSTRUCTION_FORMAT10t,
+                INSTRUCTION_FORMAT10x,
+                INSTRUCTION_FORMAT10x_ODEX,
+                INSTRUCTION_FORMAT11n,
+                INSTRUCTION_FORMAT11x,
+                INSTRUCTION_FORMAT12x_OR_ID,
+                INSTRUCTION_FORMAT12x,
+                INSTRUCTION_FORMAT20bc,
+                INSTRUCTION_FORMAT20t,
+                INSTRUCTION_FORMAT21c_FIELD,
+                INSTRUCTION_FORMAT21c_FIELD_ODEX,
+                INSTRUCTION_FORMAT21c_STRING,
+                INSTRUCTION_FORMAT21c_TYPE,
+                INSTRUCTION_FORMAT21ih,
+                INSTRUCTION_FORMAT21lh,
+                INSTRUCTION_FORMAT21s,
+                INSTRUCTION_FORMAT21t,
+                INSTRUCTION_FORMAT22b,
+                INSTRUCTION_FORMAT22c_FIELD,
+                INSTRUCTION_FORMAT22c_FIELD_ODEX,
+                INSTRUCTION_FORMAT22c_TYPE,
+                INSTRUCTION_FORMAT22cs_FIELD,
+                INSTRUCTION_FORMAT22s_OR_ID,
+                INSTRUCTION_FORMAT22s,
+                INSTRUCTION_FORMAT22t,
+                INSTRUCTION_FORMAT22x,
+                INSTRUCTION_FORMAT23x,
+                INSTRUCTION_FORMAT30t,
+                INSTRUCTION_FORMAT31c,
+                INSTRUCTION_FORMAT31i_OR_ID,
+                INSTRUCTION_FORMAT31i,
+                INSTRUCTION_FORMAT31t,
+                INSTRUCTION_FORMAT32x,
+                INSTRUCTION_FORMAT35c_METHOD,
+                INSTRUCTION_FORMAT35c_METHOD_ODEX,
+                INSTRUCTION_FORMAT35c_TYPE,
+                INSTRUCTION_FORMAT35mi_METHOD,
+                INSTRUCTION_FORMAT35ms_METHOD,
+                INSTRUCTION_FORMAT3rc_METHOD,
+                INSTRUCTION_FORMAT3rc_METHOD_ODEX,
+                INSTRUCTION_FORMAT3rc_TYPE,
+                INSTRUCTION_FORMAT3rmi_METHOD,
+                INSTRUCTION_FORMAT3rms_METHOD,
+                INSTRUCTION_FORMAT51l,
+                ARRAY_DATA_DIRECTIVE,
+                PACKED_SWITCH_DIRECTIVE,
+                SPARSE_SWITCH_DIRECTIVE
+        );
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java
new file mode 100644
index 0000000..5e2dd0c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliCodeFragmentFactory.java
@@ -0,0 +1,378 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging;
+
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.evaluation.*;
+import com.intellij.debugger.engine.evaluation.expression.EvaluatorBuilder;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.engine.jdi.StackFrameProxy;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.fileTypes.LanguageFileType;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Computable;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.JavaCodeFragment;
+import com.intellij.psi.JavaRecursiveElementVisitor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiLocalVariable;
+import com.intellij.psi.util.PsiMatchers;
+import com.sun.jdi.*;
+import com.sun.tools.jdi.LocalVariableImpl;
+import com.sun.tools.jdi.LocationImpl;
+import org.jf.dexlib2.analysis.AnalyzedInstruction;
+import org.jf.dexlib2.analysis.RegisterType;
+import org.jf.smalidea.SmaliFileType;
+import org.jf.smalidea.SmaliLanguage;
+import org.jf.smalidea.debugging.value.LazyValue;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+import org.jf.smalidea.util.NameUtils;
+import org.jf.smalidea.util.PsiUtil;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.InvocationTargetException;
+import java.util.List;
+import java.util.Map;
+
+public class SmaliCodeFragmentFactory extends DefaultCodeFragmentFactory {
+    static final Key<List<LazyValue>> SMALI_LAZY_VALUES_KEY = Key.create("_smali_register_value_key_");
+
+    @Override
+    public JavaCodeFragment createCodeFragment(TextWithImports item, PsiElement context, Project project) {
+        context = wrapContext(project, context);
+        JavaCodeFragment fragment = super.createCodeFragment(item, context, project);
+        List<LazyValue> lazyValues = context.getUserData(SMALI_LAZY_VALUES_KEY);
+        if (lazyValues != null) {
+            fragment.putUserData(SMALI_LAZY_VALUES_KEY, lazyValues);
+        }
+        return fragment;
+    }
+
+    @Override
+    public boolean isContextAccepted(PsiElement contextElement) {
+        if (contextElement == null) {
+            return false;
+        }
+        return contextElement.getLanguage() == SmaliLanguage.INSTANCE;
+    }
+
+    @Override
+    public JavaCodeFragment createPresentationCodeFragment(TextWithImports item, PsiElement context, Project project) {
+        context = wrapContext(project, context);
+        JavaCodeFragment fragment = super.createPresentationCodeFragment(item, context, project);
+        List<LazyValue> lazyValues = context.getUserData(SMALI_LAZY_VALUES_KEY);
+        if (lazyValues != null) {
+            fragment.putUserData(SMALI_LAZY_VALUES_KEY, lazyValues);
+        }
+        return fragment;
+    }
+
+    @Override public LanguageFileType getFileType() {
+        return SmaliFileType.INSTANCE;
+    }
+
+    @Override public EvaluatorBuilder getEvaluatorBuilder() {
+        final EvaluatorBuilder builder = super.getEvaluatorBuilder();
+        return new EvaluatorBuilder() {
+
+            @Override
+            public ExpressionEvaluator build(PsiElement codeFragment, SourcePosition position)
+                    throws EvaluateException {
+                return new SmaliExpressionEvaluator(codeFragment, builder.build(codeFragment, position));
+            }
+        };
+    }
+
+    private PsiElement wrapContext(final Project project, final PsiElement originalContext) {
+        if (project.isDefault()) return originalContext;
+
+        final List<LazyValue> lazyValues = Lists.newArrayList();
+
+        SmaliInstruction currentInstruction = (SmaliInstruction)PsiUtil.searchBackward(originalContext,
+                PsiMatchers.hasClass(SmaliInstruction.class),
+                PsiMatchers.hasClass(SmaliMethod.class));
+
+        if (currentInstruction == null) {
+            currentInstruction = (SmaliInstruction)PsiUtil.searchForward(originalContext,
+                    PsiMatchers.hasClass(SmaliInstruction.class),
+                    PsiMatchers.hasClass(SmaliMethod.class));
+            if (currentInstruction == null) {
+                return originalContext;
+            }
+        }
+
+        final SmaliMethod containingMethod = currentInstruction.getParentMethod();
+        AnalyzedInstruction analyzedInstruction = currentInstruction.getAnalyzedInstruction();
+        if (analyzedInstruction == null) {
+            return originalContext;
+        }
+
+        final int firstParameterRegister = containingMethod.getRegisterCount() -
+                containingMethod.getParameterRegisterCount();
+
+        final Map<String, String> registerMap = Maps.newHashMap();
+        StringBuilder variablesText = new StringBuilder();
+        for (int i=0; i<containingMethod.getRegisterCount(); i++) {
+            int parameterRegisterNumber = i - firstParameterRegister;
+
+            RegisterType registerType = analyzedInstruction.getPreInstructionRegisterType(i);
+            switch (registerType.category) {
+                case RegisterType.UNKNOWN:
+                case RegisterType.UNINIT:
+                case RegisterType.CONFLICTED:
+                case RegisterType.LONG_HI:
+                case RegisterType.DOUBLE_HI:
+                    continue;
+                case RegisterType.NULL:
+                case RegisterType.ONE:
+                case RegisterType.INTEGER:
+                    variablesText.append("int v").append(i).append(";\n");
+                    registerMap.put("v" + i, "I");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("int p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "I");
+                    }
+                    break;
+                case RegisterType.BOOLEAN:
+                    variablesText.append("boolean v").append(i).append(";\n");
+                    registerMap.put("v" + i, "Z");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("boolean p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "Z");
+                    }
+                    break;
+                case RegisterType.BYTE:
+                case RegisterType.POS_BYTE:
+                    variablesText.append("byte v").append(i).append(";\n");
+                    registerMap.put("v" + i, "B");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("byte p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "B");
+                    }
+                    break;
+                case RegisterType.SHORT:
+                case RegisterType.POS_SHORT:
+                    variablesText.append("short v").append(i).append(";\n");
+                    registerMap.put("v" + i, "S");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("short p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "S");
+                    }
+                    break;
+                case RegisterType.CHAR:
+                    variablesText.append("char v").append(i).append(";\n");
+                    registerMap.put("v" + i, "C");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("char p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "C");
+                    }
+                    break;
+                case RegisterType.FLOAT:
+                    variablesText.append("float v").append(i).append(";\n");
+                    registerMap.put("v" + i, "F");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("float p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "F");
+                    }
+                    break;
+                case RegisterType.LONG_LO:
+                    variablesText.append("long v").append(i).append(";\n");
+                    registerMap.put("v" + i, "J");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("long p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "J");
+                    }
+                    break;
+                case RegisterType.DOUBLE_LO:
+                    variablesText.append("double v").append(i).append(";\n");
+                    registerMap.put("v" + i, "D");
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append("double p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "D");
+                    }
+                    break;
+                case RegisterType.UNINIT_REF:
+                case RegisterType.UNINIT_THIS:
+                case RegisterType.REFERENCE:
+                    String smaliType = registerType.type.getType();
+                    String javaType = NameUtils.smaliToJavaType(smaliType);
+                    variablesText.append(javaType).append(" v").append(i).append(";\n");
+                    registerMap.put("v" + i, smaliType);
+                    if (parameterRegisterNumber >= 0) {
+                        variablesText.append(javaType).append(" p").append(parameterRegisterNumber).append(";\n");
+                        registerMap.put("p" + parameterRegisterNumber, "Ljava/lang/Object;");
+                    }
+                    break;
+            }
+        }
+        final TextWithImportsImpl textWithImports = new TextWithImportsImpl(CodeFragmentKind.CODE_BLOCK,
+                variablesText.toString(), "", getFileType());
+
+        final JavaCodeFragment codeFragment = super.createCodeFragment(textWithImports, originalContext, project);
+
+        codeFragment.accept(new JavaRecursiveElementVisitor() {
+            @Override
+            public void visitLocalVariable(final PsiLocalVariable variable) {
+                final String name = variable.getName();
+                if (name != null && registerMap.containsKey(name)) {
+                    int registerNumber = Integer.parseInt(name.substring(1));
+                    if (name.charAt(0) == 'p') {
+                        registerNumber += ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
+                            @Override public Integer compute() {
+                                return containingMethod.getRegisterCount() -
+                                        containingMethod.getParameterRegisterCount();
+                            }
+                        });
+                    }
+                    LazyValue lazyValue = LazyValue.create(containingMethod, project, registerNumber,
+                            registerMap.get(name));
+                    variable.putUserData(CodeFragmentFactoryContextWrapper.LABEL_VARIABLE_VALUE_KEY, lazyValue);
+                    lazyValues.add(lazyValue);
+                }
+            }
+        });
+
+        int offset = variablesText.length() - 1;
+
+        final PsiElement newContext = codeFragment.findElementAt(offset);
+        if (newContext != null) {
+            newContext.putUserData(SMALI_LAZY_VALUES_KEY, lazyValues);
+            return newContext;
+        }
+        return originalContext;
+    }
+
+    public static Value evaluateRegister(EvaluationContext context, final SmaliMethod smaliMethod,
+                                         final int registerNum, final String type) throws EvaluateException {
+
+        final StackFrameProxy frameProxy = context.getSuspendContext().getFrameProxy();
+        if (frameProxy == null) {
+            return null;
+        }
+
+        VirtualMachine vm = frameProxy.getStackFrame().virtualMachine();
+        Location currentLocation = frameProxy.location();
+        if (currentLocation == null) {
+            return null;
+        }
+
+        Method method = currentLocation.method();
+
+        try {
+            final Constructor<LocalVariableImpl> localVariableConstructor = LocalVariableImpl.class.getDeclaredConstructor(
+                    VirtualMachine.class, Method.class, Integer.TYPE, Location.class, Location.class, String.class,
+                    String.class, String.class);
+            localVariableConstructor.setAccessible(true);
+
+            Constructor<LocationImpl> locationConstructor = LocationImpl.class.getDeclaredConstructor(
+                    VirtualMachine.class, Method.class, Long.TYPE);
+            locationConstructor.setAccessible(true);
+
+            int methodSize = 0;
+            for (SmaliInstruction instruction: smaliMethod.getInstructions()) {
+                methodSize += instruction.getInstructionSize();
+            }
+            Location endLocation = method.locationOfCodeIndex((methodSize/2) - 1);
+
+            LocalVariable localVariable = localVariableConstructor.newInstance(vm,
+                    method,
+                    mapRegister(frameProxy.getStackFrame().virtualMachine(), smaliMethod, registerNum),
+                    method.locationOfCodeIndex(0),
+                    endLocation,
+                    String.format("v%d", registerNum), type, null);
+
+            return frameProxy.getStackFrame().getValue(localVariable);
+        } catch (NoSuchMethodException e) {
+            return null;
+        } catch (InstantiationException e) {
+            return null;
+        } catch (IllegalAccessException e) {
+            return null;
+        } catch (InvocationTargetException e) {
+            return null;
+        }
+    }
+
+    private static int mapRegister(final VirtualMachine vm, final SmaliMethod smaliMethod, final int register) {
+        if (vm.version().equals("1.5.0")) {
+            return mapRegisterForDalvik(smaliMethod, register);
+        } else {
+            return mapRegisterForArt(smaliMethod, register);
+        }
+    }
+
+    private static int mapRegisterForArt(final SmaliMethod smaliMethod, final int register) {
+        return ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
+            @Override public Integer compute() {
+
+                int totalRegisters = smaliMethod.getRegisterCount();
+                int parameterRegisters = smaliMethod.getParameterRegisterCount();
+
+                if (smaliMethod.getModifierList().hasModifierProperty("static")) {
+                    return register;
+                }
+
+                // For ART, the parameter registers are rotated to the front
+                if (register >= (totalRegisters - parameterRegisters)) {
+                    return register - (totalRegisters - parameterRegisters);
+                }
+                return register + parameterRegisters;
+            }
+        });
+    }
+
+    private static int mapRegisterForDalvik(final SmaliMethod smaliMethod, final int register) {
+        return ApplicationManager.getApplication().runReadAction(new Computable<Integer>() {
+            @Override public Integer compute() {
+                if (smaliMethod.getModifierList().hasModifierProperty("static")) {
+                    return register;
+                }
+
+                int totalRegisters = smaliMethod.getRegisterCount();
+                int parameterRegisters = smaliMethod.getParameterRegisterCount();
+
+                // For dalvik, p0 is mapped to register 1, and register 0 is mapped to register 1000
+                if (register == (totalRegisters - parameterRegisters)) {
+                    return 0;
+                }
+                if (register == 0) {
+                    return 1000;
+                }
+                return register;
+            }
+        });
+    }
+}
+
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliDebuggerSupport.java b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliDebuggerSupport.java
new file mode 100644
index 0000000..f822fd1
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliDebuggerSupport.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging;
+
+import com.intellij.debugger.actions.StepOverActionHandler;
+import com.intellij.debugger.impl.DebuggerSession;
+import com.intellij.debugger.ui.JavaDebuggerSupport;
+import com.intellij.openapi.actionSystem.AnActionEvent;
+import com.intellij.openapi.project.Project;
+import com.intellij.xdebugger.impl.actions.DebuggerActionHandler;
+import com.sun.jdi.request.StepRequest;
+import org.jetbrains.annotations.NotNull;
+
+public class SmaliDebuggerSupport extends JavaDebuggerSupport {
+    private static boolean useModifiedMethod;
+
+    static {
+        try {
+            DebuggerSession.class.getMethod("stepOver", boolean.class, int.class);
+            useModifiedMethod = true;
+        } catch (NoSuchMethodException ex) {
+            useModifiedMethod = false;
+        }
+    }
+
+    private final StepOverActionHandler myStepOverActionHandler = new StepOverActionHandler() {
+        @Override
+        public void perform(@NotNull final Project project, AnActionEvent e) {
+            final DebuggerSession session = getSession(project);
+
+            if (session != null) {
+                if (useModifiedMethod) {
+                    session.stepOver(false, StepRequest.STEP_MIN);
+                } else {
+                    session.stepOver(false);
+                }
+            }
+        }
+
+        @Override public boolean isEnabled(@NotNull Project project, AnActionEvent event) {
+            // TODO: check if we're currently in a smali file?
+            return super.isEnabled(project, event);
+        }
+    };
+
+    @NotNull
+    @Override
+    public DebuggerActionHandler getStepOverHandler() {
+        return myStepOverActionHandler;
+    }
+
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliExpressionEvaluator.java b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliExpressionEvaluator.java
new file mode 100644
index 0000000..89bba29
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliExpressionEvaluator.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging;
+
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.evaluation.expression.ExpressionEvaluator;
+import com.intellij.debugger.engine.evaluation.expression.Modifier;
+import com.intellij.psi.PsiElement;
+import com.sun.jdi.Value;
+import org.jf.smalidea.debugging.value.LazyValue;
+
+import java.util.List;
+
+public class SmaliExpressionEvaluator implements ExpressionEvaluator {
+    private final PsiElement fragment;
+    private final ExpressionEvaluator evaluator;
+
+    public SmaliExpressionEvaluator(PsiElement fragment, ExpressionEvaluator evaluator) {
+        this.fragment = fragment;
+        this.evaluator = evaluator;
+    }
+
+    @Override public Value evaluate(EvaluationContext context) throws EvaluateException {
+        List<LazyValue> lazyValues = fragment.getUserData(SmaliCodeFragmentFactory.SMALI_LAZY_VALUES_KEY);
+        if (lazyValues != null) {
+            for (LazyValue lazyValue: lazyValues) {
+                lazyValue.setEvaluationContext(context);
+            }
+        }
+        return evaluator.evaluate(context);
+    }
+
+    @Override public Value getValue() {
+        return evaluator.getValue();
+    }
+
+    @Override public Modifier getModifier() {
+        return evaluator.getModifier();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliPositionManager.java b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliPositionManager.java
new file mode 100644
index 0000000..781a856
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliPositionManager.java
@@ -0,0 +1,170 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging;
+
+import com.intellij.debugger.NoDataException;
+import com.intellij.debugger.PositionManager;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.requests.ClassPrepareRequestor;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.sun.jdi.Location;
+import com.sun.jdi.ReferenceType;
+import com.sun.jdi.request.ClassPrepareRequest;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+import org.jf.smalidea.psi.index.SmaliClassNameIndex;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class SmaliPositionManager implements PositionManager {
+    private final DebugProcess debugProcess;
+
+    public SmaliPositionManager(DebugProcess debugProcess) {
+        this.debugProcess = debugProcess;
+    }
+
+    public SourcePosition getSourcePosition(String declaringType, String methodName, String methodSignature,
+                                            int codeIndex) throws NoDataException {
+        Collection<SmaliClass> classes = SmaliClassNameIndex.INSTANCE.get(declaringType,
+                debugProcess.getProject(), GlobalSearchScope.projectScope(debugProcess.getProject()));
+
+        if (classes.size() > 0) {
+            SmaliClass smaliClass = classes.iterator().next();
+
+            // TODO: make an index for this?
+            for (SmaliMethod smaliMethod: smaliClass.getMethods()) {
+                if (smaliMethod.getName().equals(methodName) &&
+                        smaliMethod.getMethodPrototype().getText().equals(methodSignature)) {
+                    return smaliMethod.getSourcePositionForCodeOffset(codeIndex * 2);
+                }
+            }
+        }
+
+        throw NoDataException.INSTANCE;
+    }
+
+    @Override
+    public SourcePosition getSourcePosition(@Nullable Location location) throws NoDataException {
+        if (location == null) {
+            throw NoDataException.INSTANCE;
+        }
+
+        return getSourcePosition(location.declaringType().name(), location.method().name(),
+                location.method().signature(), (int)location.codeIndex());
+    }
+
+    @Override @NotNull
+    public List<ReferenceType> getAllClasses(@NotNull SourcePosition classPosition) throws NoDataException {
+        if (!(classPosition.getElementAt().getContainingFile() instanceof SmaliFile)) {
+            throw NoDataException.INSTANCE;
+        }
+
+        String className = getClassFromPosition(classPosition);
+        return debugProcess.getVirtualMachineProxy().classesByName(className);
+    }
+
+    @NotNull
+    private String getClassFromPosition(@NotNull final SourcePosition position) {
+        return ApplicationManager.getApplication().runReadAction(new Computable<String>() {
+            @Override public String compute() {
+                SmaliClass smaliClass = ((SmaliFile)position.getElementAt().getContainingFile()).getPsiClass();
+                if (smaliClass == null) {
+                    return "";
+                }
+                return smaliClass.getQualifiedName();
+            }
+        });
+    }
+
+    @Override @NotNull
+    public List<Location> locationsOfLine(@NotNull final ReferenceType type,
+                                          @NotNull final SourcePosition position) throws NoDataException {
+        if (!(position.getElementAt().getContainingFile() instanceof SmaliFile)) {
+            throw NoDataException.INSTANCE;
+        }
+
+        final ArrayList<Location> locations = new ArrayList<Location>(1);
+
+        ApplicationManager.getApplication().runReadAction(new Runnable() {
+            @Override
+            public void run() {
+                String typeName = type.name();
+                Collection<SmaliClass> classes = SmaliClassNameIndex.INSTANCE.get(typeName, debugProcess.getProject(),
+                        GlobalSearchScope.projectScope(debugProcess.getProject()));
+
+                if (classes.size() > 0) {
+                    final SmaliClass smaliClass = classes.iterator().next();
+
+                    Location location = smaliClass.getLocationForSourcePosition(type, position);
+
+                    if (location != null) {
+                        locations.add(location);
+                    }
+                }
+            }
+        });
+        return locations;
+    }
+
+    @Override
+    public ClassPrepareRequest createPrepareRequest(@NotNull final ClassPrepareRequestor requestor,
+                                                    @NotNull final SourcePosition position) throws NoDataException {
+        Computable<Boolean> isSmaliFile = new Computable<Boolean>() {
+            @Override
+            public Boolean compute() {
+                return position.getFile() instanceof SmaliFile;
+            }
+        };
+
+        ApplicationManager.getApplication().runReadAction(isSmaliFile);
+
+        if (!isSmaliFile.compute()) {
+            throw NoDataException.INSTANCE;
+        }
+
+        String className = getClassFromPosition(position);
+        return debugProcess.getRequestsManager().createClassPrepareRequest(new ClassPrepareRequestor() {
+            @Override
+            public void processClassPrepare(DebugProcess debuggerProcess, ReferenceType referenceType) {
+                requestor.processClassPrepare(debuggerProcess, referenceType);
+            }
+        }, className);
+    }
+}
\ No newline at end of file
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliPositionManagerFactory.java b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliPositionManagerFactory.java
new file mode 100644
index 0000000..d78b57c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/SmaliPositionManagerFactory.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2012, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging;
+
+import com.intellij.debugger.PositionManager;
+import com.intellij.debugger.PositionManagerFactory;
+import com.intellij.debugger.engine.DebugProcess;
+import org.jetbrains.annotations.NotNull;
+
+public class SmaliPositionManagerFactory extends PositionManagerFactory {
+    @Override
+    public PositionManager createPositionManager(@NotNull DebugProcess process) {
+        return new SmaliPositionManager(process);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyArrayReference.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyArrayReference.java
new file mode 100644
index 0000000..ad55a9f
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyArrayReference.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ArrayReference;
+import com.sun.jdi.ClassNotLoadedException;
+import com.sun.jdi.InvalidTypeException;
+import com.sun.jdi.Value;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+import java.util.List;
+
+public class LazyArrayReference extends LazyObjectReference<ArrayReference> implements ArrayReference {
+    public LazyArrayReference(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    public Value getValue(int index) {
+        return getValue().getValue(index);
+    }
+
+    public List<Value> getValues() {
+        return getValue().getValues();
+    }
+
+    public List<Value> getValues(int index, int length) {
+        return getValue().getValues(index, length);
+    }
+
+    public int length() {
+        return getValue().length();
+    }
+
+    public void setValue(int index, Value value) throws InvalidTypeException, ClassNotLoadedException {
+        getValue().setValue(index, value);
+    }
+
+    public void setValues(int index, List<? extends Value> values, int srcIndex, int length) throws InvalidTypeException, ClassNotLoadedException {
+        getValue().setValues(index, values, srcIndex, length);
+    }
+
+    public void setValues(List<? extends Value> values) throws InvalidTypeException, ClassNotLoadedException {
+        getValue().setValues(values);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyBooleanValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyBooleanValue.java
new file mode 100644
index 0000000..e42da25
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyBooleanValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.BooleanValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyBooleanValue extends LazyPrimitiveValue<BooleanValue> implements BooleanValue {
+    public LazyBooleanValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public boolean value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyByteValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyByteValue.java
new file mode 100644
index 0000000..bc0e6a3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyByteValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ByteValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyByteValue extends LazyComparablePrimitiveValue<ByteValue> implements ByteValue {
+    public LazyByteValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public byte value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyCharValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyCharValue.java
new file mode 100644
index 0000000..0c536dd
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyCharValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.CharValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyCharValue extends LazyComparablePrimitiveValue<CharValue> implements CharValue {
+    public LazyCharValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public char value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyClassLoaderReference.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyClassLoaderReference.java
new file mode 100644
index 0000000..dc41bbb
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyClassLoaderReference.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ClassLoaderReference;
+import com.sun.jdi.ReferenceType;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+import java.util.List;
+
+public class LazyClassLoaderReference extends LazyObjectReference<ClassLoaderReference>
+        implements ClassLoaderReference {
+    public LazyClassLoaderReference(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    public List<ReferenceType> definedClasses() {
+        return getValue().definedClasses();
+    }
+
+    public List<ReferenceType> visibleClasses() {
+        return getValue().visibleClasses();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyClassObjectReference.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyClassObjectReference.java
new file mode 100644
index 0000000..62ac420
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyClassObjectReference.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ClassObjectReference;
+import com.sun.jdi.ReferenceType;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyClassObjectReference extends LazyObjectReference<ClassObjectReference>
+        implements ClassObjectReference {
+    public LazyClassObjectReference(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    public ReferenceType reflectedType() {
+        return getValue().reflectedType();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyComparablePrimitiveValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyComparablePrimitiveValue.java
new file mode 100644
index 0000000..e4a0b33
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyComparablePrimitiveValue.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.PrimitiveValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyComparablePrimitiveValue<T extends PrimitiveValue & Comparable<T>> extends LazyPrimitiveValue<T>
+        implements Comparable<T> {
+    public LazyComparablePrimitiveValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    public int compareTo(T o) {
+        return getValue().compareTo(o);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyDoubleValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyDoubleValue.java
new file mode 100644
index 0000000..ae9e17a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyDoubleValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.DoubleValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyDoubleValue extends LazyComparablePrimitiveValue<DoubleValue> implements DoubleValue {
+    public LazyDoubleValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public double value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyFloatValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyFloatValue.java
new file mode 100644
index 0000000..54041cd
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyFloatValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.FloatValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyFloatValue extends LazyComparablePrimitiveValue<FloatValue> implements FloatValue {
+    public LazyFloatValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public float value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyIntegerValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyIntegerValue.java
new file mode 100644
index 0000000..87b13ca
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyIntegerValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.IntegerValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyIntegerValue extends LazyComparablePrimitiveValue<IntegerValue> implements IntegerValue {
+    public LazyIntegerValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public int value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyLongValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyLongValue.java
new file mode 100644
index 0000000..e1c1aaf
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyLongValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.LongValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyLongValue extends LazyComparablePrimitiveValue<LongValue> implements LongValue {
+    public LazyLongValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public long value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyObjectReference.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyObjectReference.java
new file mode 100644
index 0000000..9f50bf3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyObjectReference.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.*;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+import java.util.List;
+import java.util.Map;
+
+public class LazyObjectReference<T extends ObjectReference> extends LazyValue<T> implements ObjectReference {
+
+    public LazyObjectReference(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public void disableCollection() {
+        getValue().disableCollection();
+    }
+
+    @Override public ReferenceType referenceType() {
+        return getValue().referenceType();
+    }
+
+    @Override public Value getValue(Field sig) {
+        return getValue().getValue(sig);
+    }
+
+    @Override public Map<Field, Value> getValues(List<? extends Field> fields) {
+        return getValue().getValues(fields);
+    }
+
+    @Override public void setValue(Field field, Value value) throws InvalidTypeException, ClassNotLoadedException {
+        getValue().setValue(field, value);
+    }
+
+    @Override
+    public Value invokeMethod(ThreadReference thread, Method method, List<? extends Value> arguments, int options)
+            throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException,
+            InvocationException {
+        return getValue().invokeMethod(thread, method, arguments, options);
+    }
+
+    @Override public void enableCollection() {
+        getValue().enableCollection();
+    }
+
+    @Override public boolean isCollected() {
+        return getValue().isCollected();
+    }
+
+    @Override public long uniqueID() {
+        return getValue().uniqueID();
+    }
+
+    @Override public List<ThreadReference> waitingThreads() throws IncompatibleThreadStateException {
+        return getValue().waitingThreads();
+    }
+
+    @Override public ThreadReference owningThread() throws IncompatibleThreadStateException {
+        return getValue().owningThread();
+    }
+
+    @Override public int entryCount() throws IncompatibleThreadStateException {
+        return getValue().entryCount();
+    }
+
+    @Override public List<ObjectReference> referringObjects(long maxReferrers) {
+        return getValue().referringObjects(maxReferrers);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyPrimitiveValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyPrimitiveValue.java
new file mode 100644
index 0000000..65d3635
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyPrimitiveValue.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.PrimitiveValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyPrimitiveValue<T extends PrimitiveValue> extends LazyValue<T> implements PrimitiveValue {
+    public LazyPrimitiveValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public boolean booleanValue() {
+        return getValue().booleanValue();
+    }
+
+    @Override public byte byteValue() {
+        return getValue().byteValue();
+    }
+
+    @Override public char charValue() {
+        return getValue().charValue();
+    }
+
+    @Override public double doubleValue() {
+        return getValue().doubleValue();
+    }
+
+    @Override public float floatValue() {
+        return getValue().floatValue();
+    }
+
+    @Override public int intValue() {
+        return getValue().intValue();
+    }
+
+    @Override public long longValue() {
+        return getValue().longValue();
+    }
+
+    @Override public short shortValue() {
+        return getValue().shortValue();
+    }
+
+    @Override public String toString() {
+        return getValue().toString();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyShortValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyShortValue.java
new file mode 100644
index 0000000..10a5fa3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyShortValue.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ShortValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyShortValue extends LazyComparablePrimitiveValue<ShortValue> implements ShortValue {
+    public LazyShortValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public short value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyStringReference.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyStringReference.java
new file mode 100644
index 0000000..88fd007
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyStringReference.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.StringReference;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyStringReference extends LazyObjectReference<StringReference> implements StringReference {
+    public LazyStringReference(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    public String value() {
+        return getValue().value();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyThreadGroupReference.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyThreadGroupReference.java
new file mode 100644
index 0000000..05f05d6
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyThreadGroupReference.java
@@ -0,0 +1,71 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.ThreadGroupReference;
+import com.sun.jdi.ThreadReference;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+import java.util.List;
+
+public class LazyThreadGroupReference extends LazyObjectReference<ThreadGroupReference>
+        implements ThreadGroupReference {
+
+    public LazyThreadGroupReference(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    @Override public String name() {
+        return getValue().name();
+    }
+
+    @Override public ThreadGroupReference parent() {
+        return getValue().parent();
+    }
+
+    @Override public void resume() {
+        getValue().resume();
+    }
+
+    @Override public void suspend() {
+        getValue().suspend();
+    }
+
+    @Override public List<ThreadGroupReference> threadGroups() {
+        return getValue().threadGroups();
+    }
+
+    @Override public List<ThreadReference> threads() {
+        return getValue().threads();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyThreadReference.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyThreadReference.java
new file mode 100644
index 0000000..61a13f8
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyThreadReference.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.*;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+import java.util.List;
+
+public class LazyThreadReference extends LazyObjectReference<ThreadReference> implements ThreadReference {
+    public LazyThreadReference(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+
+    public ObjectReference currentContendedMonitor() throws IncompatibleThreadStateException {
+        return getValue().currentContendedMonitor();
+    }
+
+    public void forceEarlyReturn(Value value) throws InvalidTypeException, ClassNotLoadedException, IncompatibleThreadStateException {
+        getValue().forceEarlyReturn(value);
+    }
+
+    public StackFrame frame(int index) throws IncompatibleThreadStateException {
+        return getValue().frame(index);
+    }
+
+    public int frameCount() throws IncompatibleThreadStateException {
+        return getValue().frameCount();
+    }
+
+    public List<StackFrame> frames() throws IncompatibleThreadStateException {
+        return getValue().frames();
+    }
+
+    public List<StackFrame> frames(int start, int length) throws IncompatibleThreadStateException {
+        return getValue().frames(start, length);
+    }
+
+    public void interrupt() {
+        getValue().interrupt();
+    }
+
+    public boolean isAtBreakpoint() {
+        return getValue().isAtBreakpoint();
+    }
+
+    public boolean isSuspended() {
+        return getValue().isSuspended();
+    }
+
+    public String name() {
+        return getValue().name();
+    }
+
+    public List<ObjectReference> ownedMonitors() throws IncompatibleThreadStateException {
+        return getValue().ownedMonitors();
+    }
+
+    public List<MonitorInfo> ownedMonitorsAndFrames() throws IncompatibleThreadStateException {
+        return getValue().ownedMonitorsAndFrames();
+    }
+
+    public void popFrames(StackFrame frame) throws IncompatibleThreadStateException {
+        getValue().popFrames(frame);
+    }
+
+    public void resume() {
+        getValue().resume();
+    }
+
+    public int status() {
+        return getValue().status();
+    }
+
+    public void stop(ObjectReference throwable) throws InvalidTypeException {
+        getValue().stop(throwable);
+    }
+
+    public void suspend() {
+        getValue().suspend();
+    }
+
+    public int suspendCount() {
+        return getValue().suspendCount();
+    }
+
+    public ThreadGroupReference threadGroup() {
+        return getValue().threadGroup();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyValue.java
new file mode 100644
index 0000000..746a629
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyValue.java
@@ -0,0 +1,179 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.debugger.DebuggerManagerEx;
+import com.intellij.debugger.engine.DebugProcessImpl;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.impl.DebuggerContextImpl;
+import com.intellij.debugger.jdi.VirtualMachineProxyImpl;
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.Type;
+import com.sun.jdi.Value;
+import com.sun.jdi.VirtualMachine;
+import org.jf.smalidea.debugging.SmaliCodeFragmentFactory;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class LazyValue<T extends Value> implements Value {
+    private final int registerNumber;
+    private final Project project;
+    private final SmaliMethod method;
+    private final String type;
+
+    private EvaluationContext evaluationContext;
+    private Value value;
+
+    public LazyValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        this.method = method;
+        this.project = project;
+        this.registerNumber = registerNumber;
+        this.type = type;
+    }
+
+    public static LazyValue create(@Nonnull SmaliMethod method, @Nonnull Project project, int registerNumber,
+                                   @Nonnull String type) {
+        if (type.equals("B")) {
+            return new LazyByteValue(method, project, registerNumber, type);
+        } else if (type.equals("S")) {
+            return new LazyShortValue(method, project, registerNumber, type);
+        } else if (type.equals("J")) {
+            return new LazyLongValue(method, project, registerNumber, type);
+        } else if (type.equals("I")) {
+            return new LazyIntegerValue(method, project, registerNumber, type);
+        } else if (type.equals("F")) {
+            return new LazyFloatValue(method, project, registerNumber, type);
+        } else if (type.equals("D")) {
+            return new LazyDoubleValue(method, project, registerNumber, type);
+        } else if (type.equals("Z")) {
+            return new LazyBooleanValue(method, project, registerNumber, type);
+        } else if (type.equals("C")) {
+            return new LazyCharValue(method, project, registerNumber, type);
+        } else if (type.equals("V")) {
+            return new LazyVoidValue(method, project, registerNumber, type);
+        } else if (type.startsWith("[")) {
+            return new LazyArrayReference(method, project, registerNumber, type);
+        } else if (type.equals("Ljava/lang/String;")) {
+            return new LazyStringReference(method, project, registerNumber, type);
+        } else if (type.equals("Ljava/lang/Class;")) {
+            return new LazyClassObjectReference(method, project, registerNumber, type);
+        } else if (type.equals("Ljava/lang/ThreadGroup;")) {
+            return new LazyThreadGroupReference(method, project, registerNumber, type);
+        } else if (type.equals("Ljava/lang/Thread;")) {
+            return new LazyThreadReference(method, project, registerNumber, type);
+        } else if (type.equals("Ljava/lang/ClassLoader;")) {
+            return new LazyClassLoaderReference(method, project, registerNumber, type);
+        } else if (type.startsWith("L")) {
+            return new LazyObjectReference(method, project, registerNumber, type);
+        }
+        return new LazyValue(method, project, registerNumber, type);
+    }
+
+    @Nullable
+    private T getNullableValue() {
+        if (value == null) {
+            try {
+                if (evaluationContext == null) {
+                    final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext();
+                    evaluationContext = debuggerContext.createEvaluationContext();
+                    if (evaluationContext == null) {
+                        return null;
+                    }
+                }
+
+                value = SmaliCodeFragmentFactory.evaluateRegister(evaluationContext, method, registerNumber, type);
+                evaluationContext = null;
+            } catch (EvaluateException ex) {
+                return null;
+            }
+        }
+        return (T)value;
+    }
+
+    @Nonnull
+    protected T getValue() {
+        T value = getNullableValue();
+        assert value != null;
+        return value;
+    }
+
+    @Override
+    public Type type() {
+        return getValue().type();
+    }
+
+    @Override
+    public VirtualMachine virtualMachine() {
+        if (evaluationContext != null) {
+            return ((VirtualMachineProxyImpl)evaluationContext.getDebugProcess().getVirtualMachineProxy())
+                    .getVirtualMachine();
+        } else {
+            final DebuggerContextImpl debuggerContext = DebuggerManagerEx.getInstanceEx(project).getContext();
+            final DebugProcessImpl process = debuggerContext.getDebugProcess();
+            if (process != null) {
+                return process.getVirtualMachineProxy().getVirtualMachine();
+            }
+        }
+        return null;
+    }
+
+    public void setEvaluationContext(@Nonnull EvaluationContext evaluationContext) {
+        this.evaluationContext = evaluationContext;
+    }
+
+    @Override public boolean equals(Object obj) {
+        Value value = getNullableValue();
+        if (value != null) {
+            return value.equals(obj);
+        }
+        return super.equals(obj);
+    }
+
+    @Override public int hashCode() {
+        Value value = getNullableValue();
+        if (value != null) {
+            return value.hashCode();
+        }
+        return super.hashCode();
+    }
+
+    @Override public String toString() {
+        Value value = getNullableValue();
+        if (value != null) {
+            return value.toString();
+        }
+        return super.toString();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyVoidValue.java b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyVoidValue.java
new file mode 100644
index 0000000..a7f6bdc
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/debugging/value/LazyVoidValue.java
@@ -0,0 +1,42 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.debugging.value;
+
+import com.intellij.openapi.project.Project;
+import com.sun.jdi.VoidValue;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class LazyVoidValue extends LazyValue<VoidValue> implements VoidValue {
+    public LazyVoidValue(SmaliMethod method, Project project, int registerNumber, String type) {
+        super(method, project, registerNumber, type);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaClassDef.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaClassDef.java
new file mode 100644
index 0000000..c954702
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaClassDef.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib;
+
+import com.google.common.base.Function;
+import com.google.common.base.Predicate;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Iterables;
+import com.google.common.collect.Lists;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifierList;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.base.reference.BaseTypeReference;
+import org.jf.dexlib2.iface.Annotation;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.dexlib2.iface.Field;
+import org.jf.dexlib2.iface.Method;
+import org.jf.smalidea.util.NameUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class SmalideaClassDef extends BaseTypeReference implements ClassDef {
+    private final PsiClass psiClass;
+
+    public SmalideaClassDef(PsiClass psiClass) {
+        this.psiClass = psiClass;
+    }
+
+    @Override public int getAccessFlags() {
+        PsiModifierList modifierList = psiClass.getModifierList();
+        int flags = 0;
+
+        if (modifierList == null) {
+            return flags;
+        }
+
+        if (modifierList.hasModifierProperty("public")) {
+            flags |= AccessFlags.PUBLIC.getValue();
+        }
+
+        if (modifierList.hasModifierProperty("final")) {
+            flags |= AccessFlags.FINAL.getValue();
+        }
+
+        if (modifierList.hasModifierProperty("abstract")) {
+            flags |= AccessFlags.ABSTRACT.getValue();
+        }
+
+        if (psiClass.isInterface()) {
+            flags |= AccessFlags.INTERFACE.getValue();
+        }
+
+        if (psiClass.isEnum()) {
+            flags |= AccessFlags.ENUM.getValue();
+        }
+
+        if (psiClass.isAnnotationType()) {
+            flags |= AccessFlags.ANNOTATION.getValue();
+        }
+
+        return flags;
+    }
+
+    @Nonnull @Override public String getType() {
+        return NameUtils.javaToSmaliType(psiClass);
+    }
+
+    @Nullable @Override public String getSuperclass() {
+        PsiClass superClass = psiClass.getSuperClass();
+        if (superClass == null) {
+            return null;
+        }
+        return NameUtils.javaToSmaliType(superClass);
+    }
+
+    @Nonnull @Override public List<String> getInterfaces() {
+        List<String> interfaceList = Lists.newArrayList();
+        PsiClass[] interfaces = psiClass.getInterfaces();
+        if (interfaces == null) {
+            return interfaceList;
+        }
+
+        for (PsiClass psiClass: interfaces) {
+            interfaceList.add(NameUtils.javaToSmaliType(psiClass));
+        }
+
+        return interfaceList;
+    }
+
+    @Nullable @Override public String getSourceFile() {
+        return null;
+    }
+
+    @Nonnull @Override public Set<? extends Annotation> getAnnotations() {
+        return ImmutableSet.of();
+    }
+
+    @Nonnull @Override public Iterable<? extends Field> getStaticFields() {
+        return Iterables.transform(
+                Iterables.filter(Arrays.asList(psiClass.getFields()), new Predicate<PsiField>() {
+                    @Override public boolean apply(PsiField psiField) {
+                        PsiModifierList modifierList = psiField.getModifierList();
+                        if (modifierList == null) {
+                            return false;
+                        }
+                        return modifierList.hasModifierProperty("static");
+                    }
+                }),
+                new Function<PsiField, Field>() {
+                    @Nullable @Override public Field apply(@Nullable PsiField psiField) {
+                        return new SmalideaField(psiField);
+                    }
+                });
+    }
+
+    @Nonnull @Override public Iterable<? extends Field> getInstanceFields() {
+        return Iterables.transform(
+                Iterables.filter(Arrays.asList(psiClass.getFields()), new Predicate<PsiField>() {
+                    @Override public boolean apply(PsiField psiField) {
+                        PsiModifierList modifierList = psiField.getModifierList();
+                        if (modifierList == null) {
+                            return true;
+                        }
+                        return !modifierList.hasModifierProperty("static");
+                    }
+                }),
+                new Function<PsiField, Field>() {
+                    @Nullable @Override public Field apply(@Nullable PsiField psiField) {
+                        return new SmalideaField(psiField);
+                    }
+                });
+    }
+
+    @Nonnull @Override public Iterable<? extends Field> getFields() {
+        return Iterables.concat(getStaticFields(), getInstanceFields());
+    }
+
+    @Nonnull @Override public Iterable<? extends Method> getDirectMethods() {
+        return Iterables.transform(
+                Iterables.filter(
+                        Iterables.concat(
+                            Arrays.asList(psiClass.getConstructors()),
+                            Arrays.asList(psiClass.getMethods())),
+                        new Predicate<PsiMethod>() {
+                    @Override public boolean apply(PsiMethod psiMethod) {
+                        PsiModifierList modifierList = psiMethod.getModifierList();
+                        return modifierList.hasModifierProperty("static") ||
+                                modifierList.hasModifierProperty("private") ||
+                                modifierList.hasModifierProperty("constructor");
+                    }
+                }),
+                new Function<PsiMethod, Method>() {
+                    @Nullable @Override public Method apply(PsiMethod psiMethod) {
+                        return new SmalideaMethod(psiMethod);
+                    }
+                });
+    }
+
+    @Nonnull @Override public Iterable<? extends Method> getVirtualMethods() {
+        return Iterables.transform(
+                Iterables.filter(Arrays.asList(psiClass.getMethods()), new Predicate<PsiMethod>() {
+                    @Override public boolean apply(PsiMethod psiMethod) {
+                        PsiModifierList modifierList = psiMethod.getModifierList();
+                        return !modifierList.hasModifierProperty("static") &&
+                                !modifierList.hasModifierProperty("private") &&
+                                !modifierList.hasModifierProperty("constructor");
+                    }
+                }),
+                new Function<PsiMethod, Method>() {
+                    @Nullable @Override public Method apply(PsiMethod psiMethod) {
+                        return new SmalideaMethod(psiMethod);
+                    }
+                });
+    }
+
+    @Nonnull @Override public Iterable<? extends Method> getMethods() {
+        return Iterables.concat(getDirectMethods(), getVirtualMethods());
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaExceptionHandler.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaExceptionHandler.java
new file mode 100644
index 0000000..72909b6
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaExceptionHandler.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib;
+
+import org.jf.dexlib2.base.BaseExceptionHandler;
+import org.jf.smalidea.psi.impl.SmaliCatchStatement;
+import org.jf.smalidea.psi.impl.SmaliClassTypeElement;
+import org.jf.smalidea.psi.impl.SmaliLabel;
+import org.jf.smalidea.psi.impl.SmaliLabelReference;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class SmalideaExceptionHandler extends BaseExceptionHandler {
+    @Nonnull private final SmaliCatchStatement catchStatement;
+
+    public SmalideaExceptionHandler(@Nonnull SmaliCatchStatement catchStatement) {
+        this.catchStatement = catchStatement;
+    }
+
+    @Nullable @Override public String getExceptionType() {
+        SmaliClassTypeElement exceptionType = catchStatement.getExceptionType();
+        if (exceptionType == null) {
+            return null;
+        }
+        return exceptionType.getText();
+    }
+
+    @Override public int getHandlerCodeAddress() {
+        SmaliLabelReference handlerLabel = catchStatement.getHandlerLabel();
+        // TODO: how to handle a reference to a non-existent label..
+        SmaliLabel smaliLabel = handlerLabel.resolve();
+        return smaliLabel.getOffset() / 2;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaField.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaField.java
new file mode 100644
index 0000000..7b98174
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaField.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib;
+
+import com.google.common.collect.ImmutableSet;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiModifierList;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.base.reference.BaseFieldReference;
+import org.jf.dexlib2.iface.Annotation;
+import org.jf.dexlib2.iface.Field;
+import org.jf.dexlib2.iface.value.EncodedValue;
+import org.jf.smalidea.psi.impl.SmaliField;
+import org.jf.smalidea.util.NameUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.Set;
+
+public class SmalideaField extends BaseFieldReference implements Field {
+    private final PsiField psiField;
+
+    public SmalideaField(PsiField psiField) {
+        this.psiField = psiField;
+    }
+
+    @Override public int getAccessFlags() {
+        if (psiField instanceof SmaliField) {
+            return ((SmaliField)psiField).getModifierList().getAccessFlags();
+        } else {
+            int flags = 0;
+            PsiModifierList modifierList = psiField.getModifierList();
+            if (modifierList == null) {
+                return flags;
+            }
+            if (modifierList.hasModifierProperty("public")) {
+                flags |= AccessFlags.PUBLIC.getValue();
+            } else if (modifierList.hasModifierProperty("protected")) {
+                flags |= AccessFlags.PROTECTED.getValue();
+            } else if (modifierList.hasModifierProperty("private")) {
+                flags |= AccessFlags.PRIVATE.getValue();
+            }
+
+            if (modifierList.hasModifierProperty("static")) {
+                flags |= AccessFlags.STATIC.getValue();
+            }
+
+            if (modifierList.hasModifierProperty("final")) {
+                flags |= AccessFlags.FINAL.getValue();
+            }
+
+            if (modifierList.hasModifierProperty("volatile")) {
+                flags |= AccessFlags.VOLATILE.getValue();
+            }
+            // TODO: how do we tell if it's an enum?
+
+            return flags;
+        }
+    }
+
+    @Nonnull @Override public String getDefiningClass() {
+        PsiClass containingClass = psiField.getContainingClass();
+        if (containingClass == null) {
+            throw new RuntimeException("I don't know what to do here... Is this even possible?");
+        }
+        return NameUtils.javaToSmaliType(containingClass);
+    }
+
+    @Nonnull @Override public String getName() {
+        return psiField.getNameIdentifier().getText();
+    }
+
+    @Nonnull @Override public String getType() {
+        return NameUtils.javaToSmaliType(psiField.getType());
+    }
+
+    @Nullable @Override public EncodedValue getInitialValue() {
+        // TODO: implement this. Not needed for method analysis
+        return null;
+    }
+
+    @Nonnull @Override public Set<? extends Annotation> getAnnotations() {
+        // TODO: implement this. Not needed for method analysis
+        return ImmutableSet.of();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java
new file mode 100644
index 0000000..abc4dda
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethod.java
@@ -0,0 +1,214 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib;
+
+import com.google.common.base.Function;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.Lists;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiParameter;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.base.reference.BaseMethodReference;
+import org.jf.dexlib2.iface.*;
+import org.jf.dexlib2.iface.debug.DebugItem;
+import org.jf.dexlib2.iface.instruction.Instruction;
+import org.jf.smalidea.dexlib.instruction.SmalideaInstruction;
+import org.jf.smalidea.psi.impl.SmaliCatchStatement;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+import org.jf.smalidea.util.NameUtils;
+
+import javax.annotation.Nonnull;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Set;
+
+public class SmalideaMethod extends BaseMethodReference implements Method {
+    private final PsiMethod psiMethod;
+
+    public SmalideaMethod(@NotNull PsiMethod psiMethod) {
+        this.psiMethod = psiMethod;
+    }
+
+    @Nonnull @Override public String getDefiningClass() {
+        PsiClass cls = psiMethod.getContainingClass();
+        assert cls != null;
+        return NameUtils.javaToSmaliType(cls);
+    }
+
+    @Nonnull @Override public List<? extends MethodParameter> getParameters() {
+        PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+
+        return Lists.transform(Arrays.asList(parameters), new Function<PsiParameter, MethodParameter>() {
+            @Nullable @Override
+            public MethodParameter apply(@Nullable PsiParameter psiParameter) {
+                if (psiParameter == null) {
+                    return null;
+                }
+                return new SmalideaMethodParameter(psiParameter);
+            }
+        });
+    }
+
+    @Override public int getAccessFlags() {
+        if (psiMethod instanceof SmaliMethod) {
+            return ((SmaliMethod)psiMethod).getModifierList().getAccessFlags();
+        } else {
+            int flags = 0;
+            PsiModifierList modifierList = psiMethod.getModifierList();
+            if (modifierList.hasModifierProperty("public")) {
+                flags |= AccessFlags.PUBLIC.getValue();
+            } else if (modifierList.hasModifierProperty("protected")) {
+                flags |= AccessFlags.PROTECTED.getValue();
+            } else if (modifierList.hasModifierProperty("private")) {
+                flags |= AccessFlags.PRIVATE.getValue();
+            }
+
+            if (modifierList.hasModifierProperty("static")) {
+                flags |= AccessFlags.STATIC.getValue();
+            }
+
+            if (modifierList.hasModifierProperty("final")) {
+                flags |= AccessFlags.FINAL.getValue();
+            }
+
+            boolean isNative = false;
+            if (modifierList.hasModifierProperty("native")) {
+                flags |= AccessFlags.NATIVE.getValue();
+                isNative = true;
+            }
+
+            if (modifierList.hasModifierProperty("synchronized")) {
+                if (isNative) {
+                    flags |= AccessFlags.SYNCHRONIZED.getValue();
+                } else {
+                    flags |= AccessFlags.DECLARED_SYNCHRONIZED.getValue();
+                }
+            }
+
+            if (psiMethod.isVarArgs()) {
+                flags |= AccessFlags.VARARGS.getValue();
+            }
+
+            if (modifierList.hasModifierProperty("abstract")) {
+                flags |= AccessFlags.ABSTRACT.getValue();
+            }
+
+            if (modifierList.hasModifierProperty("strictfp")) {
+                flags |= AccessFlags.STRICTFP.getValue();
+            }
+
+            if (psiMethod.isConstructor()) {
+                flags |= AccessFlags.CONSTRUCTOR.getValue();
+            }
+            return flags;
+        }
+    }
+
+    @Nonnull @Override public Set<? extends Annotation> getAnnotations() {
+        // TODO: implement this
+        return ImmutableSet.of();
+    }
+
+    @Nullable @Override public MethodImplementation getImplementation() {
+        if (psiMethod instanceof SmaliMethod) {
+            final SmaliMethod smaliMethod = (SmaliMethod)this.psiMethod;
+
+            List<SmaliInstruction> instructions = smaliMethod.getInstructions();
+            if (instructions.size() == 0) {
+                return null;
+            }
+
+            // TODO: cache this?
+            return new MethodImplementation() {
+                @Override public int getRegisterCount() {
+                    return smaliMethod.getRegisterCount();
+                }
+
+                @Nonnull @Override public Iterable<? extends Instruction> getInstructions() {
+                    return Lists.transform(smaliMethod.getInstructions(),
+                            new Function<SmaliInstruction, Instruction>() {
+                                @Override
+                                public Instruction apply(SmaliInstruction smaliInstruction) {
+                                    return SmalideaInstruction.of(smaliInstruction);
+                                }
+                            });
+                }
+
+                @Nonnull @Override public List<? extends TryBlock<? extends ExceptionHandler>> getTryBlocks() {
+                    return Lists.transform(smaliMethod.getCatchStatements(),
+                            new Function<SmaliCatchStatement, TryBlock<? extends ExceptionHandler>>() {
+                                @Override
+                                public TryBlock<? extends ExceptionHandler> apply(
+                                        SmaliCatchStatement smaliCatchStatement) {
+                                    assert smaliCatchStatement != null;
+                                    return new SmalideaTryBlock(smaliCatchStatement);
+                                }
+                            });
+                }
+
+                @Nonnull @Override public Iterable<? extends DebugItem> getDebugItems() {
+                    // TODO: implement this
+                    return ImmutableList.of();
+                }
+            };
+        }
+        return null;
+    }
+
+    @Nonnull @Override public String getName() {
+        return psiMethod.getName();
+    }
+
+    @Nonnull @Override public List<? extends CharSequence> getParameterTypes() {
+        PsiParameter[] parameters = psiMethod.getParameterList().getParameters();
+
+        return Lists.transform(Arrays.asList(parameters), new Function<PsiParameter, CharSequence>() {
+            @Nullable @Override
+            public CharSequence apply(@Nullable PsiParameter psiParameter) {
+                if (psiParameter == null) {
+                    return null;
+                }
+                return psiParameter.getText();
+            }
+        });
+    }
+
+    @Nonnull @Override public String getReturnType() {
+        return psiMethod.getReturnTypeElement().getText();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java
new file mode 100644
index 0000000..49d75c8
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaMethodParameter.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib;
+
+import com.google.common.collect.ImmutableSet;
+import com.intellij.psi.PsiParameter;
+import org.jetbrains.annotations.Nullable;
+import org.jf.dexlib2.base.BaseMethodParameter;
+import org.jf.dexlib2.iface.Annotation;
+import org.jf.smalidea.util.NameUtils;
+import org.jf.smalidea.util.StringUtils;
+
+import javax.annotation.Nonnull;
+import java.util.Set;
+
+public class SmalideaMethodParameter extends BaseMethodParameter {
+    private final PsiParameter psiParameter;
+
+    public SmalideaMethodParameter(PsiParameter psiParameter) {
+        this.psiParameter = psiParameter;
+    }
+
+    @Nonnull @Override public Set<? extends Annotation> getAnnotations() {
+        // TODO: implement this
+        return ImmutableSet.of();
+    }
+
+    @Nullable @Override public String getName() {
+        return StringUtils.parseQuotedString(psiParameter.getName());
+    }
+
+    @Nonnull @Override public String getType() {
+        return NameUtils.javaToSmaliType(psiParameter.getType());
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaTryBlock.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaTryBlock.java
new file mode 100644
index 0000000..6fbab59
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/SmalideaTryBlock.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib;
+
+import org.jf.dexlib2.base.BaseTryBlock;
+import org.jf.smalidea.psi.impl.SmaliCatchStatement;
+
+import javax.annotation.Nonnull;
+import java.util.Arrays;
+import java.util.List;
+
+public class SmalideaTryBlock extends BaseTryBlock<SmalideaExceptionHandler> {
+    @Nonnull private final SmaliCatchStatement catchStatement;
+
+    public SmalideaTryBlock(@Nonnull SmaliCatchStatement catchStatement) {
+        this.catchStatement = catchStatement;
+    }
+
+    @Override public int getCodeUnitCount() {
+        int endOffset = catchStatement.getEndLabel().resolve().getOffset() / 2;
+        return endOffset - getStartCodeAddress();
+    }
+
+    @Override public int getStartCodeAddress() {
+        // TODO: how to handle references to non-existent labels?
+        return catchStatement.getStartLabel().resolve().getOffset() / 2;
+    }
+
+    @Nonnull @Override public List<? extends SmalideaExceptionHandler> getExceptionHandlers() {
+        return Arrays.asList(new SmalideaExceptionHandler(catchStatement));
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/analysis/SmalideaClassProvider.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/analysis/SmalideaClassProvider.java
new file mode 100644
index 0000000..9d2a14a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/analysis/SmalideaClassProvider.java
@@ -0,0 +1,32 @@
+package org.jf.smalidea.dexlib.analysis;
+
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.impl.ResolveScopeManager;
+import org.jf.dexlib2.analysis.ClassProvider;
+import org.jf.dexlib2.iface.ClassDef;
+import org.jf.smalidea.dexlib.SmalideaClassDef;
+import org.jf.smalidea.util.NameUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+public class SmalideaClassProvider implements ClassProvider {
+    private final Project project;
+    private final VirtualFile file;
+
+    public SmalideaClassProvider(@Nonnull Project project, @Nonnull VirtualFile file) {
+        this.project = project;
+        this.file = file;
+    }
+
+    @Nullable @Override public ClassDef getClassDef(String type) {
+        ResolveScopeManager manager = ResolveScopeManager.getInstance(project);
+        PsiClass psiClass = NameUtils.resolveSmaliType(project, manager.getDefaultResolveScope(file), type);
+        if (psiClass != null) {
+            return new SmalideaClassDef(psiClass);
+        }
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaArrayPayload.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaArrayPayload.java
new file mode 100644
index 0000000..26f2daf
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaArrayPayload.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import org.jf.dexlib2.iface.instruction.formats.ArrayPayload;
+import org.jf.smalidea.psi.impl.SmaliArrayDataElement;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+
+public class SmalideaArrayPayload extends SmalideaInstruction implements ArrayPayload {
+    public SmalideaArrayPayload(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+
+    @Override public int getElementWidth() {
+        return (int)psiInstruction.getArrayDataWidth().getIntegralValue();
+    }
+
+    @Nonnull @Override public List<Number> getArrayElements() {
+        return Lists.transform(psiInstruction.getArrayDataElements(), new Function<SmaliArrayDataElement, Number>() {
+            @Nullable @Override public Number apply(SmaliArrayDataElement smaliArrayDataElement) {
+                return smaliArrayDataElement.getValue().getIntegralValue();
+            }
+        });
+    }
+
+    @Override public int getCodeUnits() {
+        return psiInstruction.getInstructionSize()/2;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction.java
new file mode 100644
index 0000000..b5b259a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction.java
@@ -0,0 +1,220 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import com.intellij.psi.PsiType;
+import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.ReferenceType;
+import org.jf.dexlib2.iface.instruction.Instruction;
+import org.jf.dexlib2.iface.reference.Reference;
+import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
+import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
+import org.jf.dexlib2.immutable.reference.ImmutableStringReference;
+import org.jf.dexlib2.immutable.reference.ImmutableTypeReference;
+import org.jf.smalidea.psi.impl.*;
+import org.jf.smalidea.util.NameUtils;
+import org.jf.smalidea.util.StringUtils;
+
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+import java.util.List;
+
+public abstract class SmalideaInstruction implements Instruction {
+    @Nonnull protected final SmaliInstruction psiInstruction;
+
+    protected SmalideaInstruction(@Nonnull SmaliInstruction instruction) {
+        this.psiInstruction = instruction;
+    }
+
+    @Nonnull
+    public static SmalideaInstruction of(SmaliInstruction instruction) {
+        switch (instruction.getOpcode().format) {
+            case Format10t:
+                return new SmalideaInstruction10t(instruction);
+            case Format10x:
+                return new SmalideaInstruction10x(instruction);
+            case Format11n:
+                return new SmalideaInstruction11n(instruction);
+            case Format11x:
+                return new SmalideaInstruction11x(instruction);
+            case Format12x:
+                return new SmalideaInstruction12x(instruction);
+            case Format20t:
+                return new SmalideaInstruction20t(instruction);
+            case Format21c:
+                return new SmalideaInstruction21c(instruction);
+            case Format21ih:
+                return new SmalideaInstruction21ih(instruction);
+            case Format21lh:
+                return new SmalideaInstruction21lh(instruction);
+            case Format21s:
+                return new SmalideaInstruction21s(instruction);
+            case Format21t:
+                return new SmalideaInstruction21t(instruction);
+            case Format22b:
+                return new SmalideaInstruction22b(instruction);
+            case Format22c:
+                return new SmalideaInstruction22c(instruction);
+            case Format22s:
+                return new SmalideaInstruction22s(instruction);
+            case Format22t:
+                return new SmalideaInstruction22t(instruction);
+            case Format22x:
+                return new SmalideaInstruction22x(instruction);
+            case Format23x:
+                return new SmalideaInstruction23x(instruction);
+            case Format30t:
+                return new SmalideaInstruction30t(instruction);
+            case Format31c:
+                return new SmalideaInstruction31c(instruction);
+            case Format31i:
+                return new SmalideaInstruction31i(instruction);
+            case Format31t:
+                return new SmalideaInstruction31t(instruction);
+            case Format32x:
+                return new SmalideaInstruction32x(instruction);
+            case Format35c:
+                return new SmalideaInstruction35c(instruction);
+            case Format3rc:
+                return new SmalideaInstruction3rc(instruction);
+            case Format51l:
+                return new SmalideaInstruction51l(instruction);
+            case PackedSwitchPayload:
+                return new SmalideaPackedSwitchPayload(instruction);
+            case SparseSwitchPayload:
+                return new SmalideaSparseSwitchPayload(instruction);
+            case ArrayPayload:
+                return new SmalideaArrayPayload(instruction);
+            default:
+                throw new RuntimeException("Unexpected instruction type");
+        }
+    }
+
+    @Nonnull public Opcode getOpcode() {
+        return psiInstruction.getOpcode();
+    }
+
+    public int getCodeUnits() {
+        return getOpcode().format.size / 2;
+    }
+
+    public int getCodeOffset() {
+        SmaliLabelReference labelReference = psiInstruction.getTarget();
+        if (labelReference == null) {
+            return -1;
+        }
+
+        SmaliLabel label = labelReference.resolve();
+        if (label == null) {
+            return -1;
+        }
+        return (label.getOffset() - psiInstruction.getOffset())/2;
+    }
+
+    public int getRegisterCount() {
+        return psiInstruction.getRegisterCount();
+    }
+
+    public int getRegisterA() {
+        return psiInstruction.getRegister(0);
+    }
+
+    public int getRegisterB() {
+        return psiInstruction.getRegister(1);
+    }
+
+    public int getRegisterC() {
+        return psiInstruction.getRegister(2);
+    }
+
+    public int getNarrowLiteral() {
+        SmaliLiteral literal = psiInstruction.getLiteral();
+        if (literal == null) {
+            return 0;
+        }
+        return (int)literal.getIntegralValue();
+    }
+
+    public long getWideLiteral() {
+        SmaliLiteral literal = psiInstruction.getLiteral();
+        if (literal == null) {
+            return 0;
+        }
+        return literal.getIntegralValue();
+    }
+
+    @Nonnull public Reference getReference() {
+        switch (getReferenceType()) {
+            case ReferenceType.STRING:
+                return new ImmutableStringReference(StringUtils.parseQuotedString(
+                        psiInstruction.getLiteral().getText()));
+            case ReferenceType.TYPE:
+                SmaliTypeElement typeReference = psiInstruction.getTypeReference();
+                assert typeReference != null;
+                return new ImmutableTypeReference(typeReference.getText());
+            case ReferenceType.METHOD:
+                SmaliMethodReference methodReference = psiInstruction.getMethodReference();
+                assert methodReference != null;
+                String containingClass = methodReference.getContainingType().getText();
+                List<String> paramTypes =
+                        Lists.transform(methodReference.getParameterTypes(), new Function<PsiType, String>() {
+                            @Nullable @Override public String apply(@Nullable PsiType psiType) {
+                                if (psiType == null) {
+                                    return null;
+                                }
+                                return NameUtils.javaToSmaliType(psiType);
+                            }
+                        });
+
+                return new ImmutableMethodReference(containingClass,
+                        methodReference.getName(),
+                        paramTypes,
+                        methodReference.getReturnType().getText());
+            case ReferenceType.FIELD:
+                SmaliFieldReference fieldReference = psiInstruction.getFieldReference();
+                assert fieldReference != null;
+                containingClass = fieldReference.getContainingType().getText();
+                return new ImmutableFieldReference(containingClass,
+                        fieldReference.getName(),
+                        fieldReference.getFieldType().getText());
+        }
+        assert false;
+        return null;
+    }
+
+    public int getReferenceType() {
+        return psiInstruction.getOpcode().referenceType;
+    }
+
+}
\ No newline at end of file
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction10t.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction10t.java
new file mode 100644
index 0000000..80a4ba6
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction10t.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction10t;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction10t extends SmalideaInstruction implements Instruction10t {
+    public SmalideaInstruction10t(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction10x.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction10x.java
new file mode 100644
index 0000000..0480451
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction10x.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction10x;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction10x extends SmalideaInstruction implements Instruction10x {
+    public SmalideaInstruction10x(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction11n.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction11n.java
new file mode 100644
index 0000000..1b185dd
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction11n.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction11n;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction11n extends SmalideaInstruction implements Instruction11n {
+    public SmalideaInstruction11n(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction11x.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction11x.java
new file mode 100644
index 0000000..7e8fbf4
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction11x.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction11x;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction11x extends SmalideaInstruction implements Instruction11x {
+    public SmalideaInstruction11x(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction12x.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction12x.java
new file mode 100644
index 0000000..c6cba0a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction12x.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction12x;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction12x extends SmalideaInstruction implements Instruction12x {
+    public SmalideaInstruction12x(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction20t.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction20t.java
new file mode 100644
index 0000000..1c0a921
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction20t.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction20t;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction20t extends SmalideaInstruction implements Instruction20t {
+    public SmalideaInstruction20t(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21c.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21c.java
new file mode 100644
index 0000000..f2ac6f1
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21c.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction21c;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction21c extends SmalideaInstruction implements Instruction21c {
+    public SmalideaInstruction21c(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21ih.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21ih.java
new file mode 100644
index 0000000..ce5f598
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21ih.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction21ih;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction21ih extends SmalideaInstruction implements Instruction21ih {
+    public SmalideaInstruction21ih(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+
+    @Override public short getHatLiteral() {
+        return (short)(getNarrowLiteral() >>> 16);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21lh.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21lh.java
new file mode 100644
index 0000000..3ef0850
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21lh.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction21lh;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction21lh extends SmalideaInstruction implements Instruction21lh {
+    public SmalideaInstruction21lh(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+
+    @Override public short getHatLiteral() {
+        return (short)(getWideLiteral() >>> 48);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21s.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21s.java
new file mode 100644
index 0000000..5ae7506
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21s.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction21s;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction21s extends SmalideaInstruction implements Instruction21s {
+    public SmalideaInstruction21s(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21t.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21t.java
new file mode 100644
index 0000000..11e1756
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction21t.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction21t;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction21t extends SmalideaInstruction implements Instruction21t {
+    public SmalideaInstruction21t(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22b.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22b.java
new file mode 100644
index 0000000..f136046
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22b.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction22b;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction22b extends SmalideaInstruction implements Instruction22b {
+    public SmalideaInstruction22b(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22c.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22c.java
new file mode 100644
index 0000000..4d7dacb
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22c.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction22c;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction22c extends SmalideaInstruction implements Instruction22c {
+    public SmalideaInstruction22c(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22s.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22s.java
new file mode 100644
index 0000000..8189768
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22s.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction22s;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction22s extends SmalideaInstruction implements Instruction22s {
+    public SmalideaInstruction22s(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22t.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22t.java
new file mode 100644
index 0000000..8f4811d
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22t.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction22t;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction22t extends SmalideaInstruction implements Instruction22t {
+    public SmalideaInstruction22t(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22x.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22x.java
new file mode 100644
index 0000000..5cc8b00
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction22x.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction22x;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction22x extends SmalideaInstruction implements Instruction22x {
+    public SmalideaInstruction22x(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction23x.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction23x.java
new file mode 100644
index 0000000..3cc60d8
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction23x.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction23x;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction23x extends SmalideaInstruction implements Instruction23x {
+    public SmalideaInstruction23x(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction30t.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction30t.java
new file mode 100644
index 0000000..324fc77
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction30t.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction30t;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction30t extends SmalideaInstruction implements Instruction30t {
+    public SmalideaInstruction30t(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31c.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31c.java
new file mode 100644
index 0000000..ffea217
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31c.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction31c;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction31c extends SmalideaInstruction implements Instruction31c {
+    public SmalideaInstruction31c(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31i.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31i.java
new file mode 100644
index 0000000..0fef667
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31i.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction31i;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction31i extends SmalideaInstruction implements Instruction31i {
+    public SmalideaInstruction31i(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31t.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31t.java
new file mode 100644
index 0000000..142fd44
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction31t.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction31t;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction31t extends SmalideaInstruction implements Instruction31t {
+    public SmalideaInstruction31t(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction32x.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction32x.java
new file mode 100644
index 0000000..1ac27de
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction32x.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction32x;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction32x extends SmalideaInstruction implements Instruction32x {
+    public SmalideaInstruction32x(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction35c.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction35c.java
new file mode 100644
index 0000000..a3ddcce
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction35c.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction35c;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction35c extends SmalideaInstruction implements Instruction35c {
+    public SmalideaInstruction35c(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+
+    @Override public int getRegisterC() {
+        return psiInstruction.getRegister(0);
+    }
+
+    @Override public int getRegisterD() {
+        return psiInstruction.getRegister(1);
+    }
+
+    @Override public int getRegisterE() {
+        return psiInstruction.getRegister(2);
+    }
+
+    @Override public int getRegisterF() {
+        return psiInstruction.getRegister(3);
+    }
+
+    @Override public int getRegisterG() {
+        return psiInstruction.getRegister(4);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction3rc.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction3rc.java
new file mode 100644
index 0000000..509e6b7
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction3rc.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction3rc;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction3rc extends SmalideaInstruction implements Instruction3rc {
+    public SmalideaInstruction3rc(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+
+    @Override public int getStartRegister() {
+        return psiInstruction.getRegister(0);
+    }
+
+    @Override public int getRegisterCount() {
+        return psiInstruction.getRegister(1) - getStartRegister() + 1;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction51l.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction51l.java
new file mode 100644
index 0000000..3b58628
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaInstruction51l.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import org.jf.dexlib2.iface.instruction.formats.Instruction51l;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+
+import javax.annotation.Nonnull;
+
+public class SmalideaInstruction51l extends SmalideaInstruction implements Instruction51l {
+    public SmalideaInstruction51l(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaPackedSwitchPayload.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaPackedSwitchPayload.java
new file mode 100644
index 0000000..9d2a0fc
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaPackedSwitchPayload.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import com.google.common.collect.Lists;
+import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.iface.instruction.SwitchElement;
+import org.jf.dexlib2.iface.instruction.formats.PackedSwitchPayload;
+import org.jf.smalidea.psi.impl.*;
+import org.jf.smalidea.util.InstructionUtils;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+
+public class SmalideaPackedSwitchPayload extends SmalideaInstruction implements PackedSwitchPayload {
+    public SmalideaPackedSwitchPayload(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+
+    @Nonnull @Override public List<? extends SwitchElement> getSwitchElements() {
+        final SmaliLiteral startKey = psiInstruction.getPackedSwitchStartKey();
+        assert startKey != null;
+        List<SmaliPackedSwitchElement> elements = psiInstruction.getPackedSwitchElements();
+
+        SmaliMethod smaliMethod = psiInstruction.getParentMethod();
+        SmaliInstruction packedSwitchInstruction = InstructionUtils.findFirstInstructionWithTarget(
+                smaliMethod, Opcode.PACKED_SWITCH, psiInstruction.getOffset());
+        final int baseOffset;
+
+        if (packedSwitchInstruction == null) {
+            baseOffset = 0;
+        } else {
+            baseOffset = packedSwitchInstruction.getOffset();
+        }
+
+        List<SwitchElement> newElements = Lists.newArrayList();
+        // TODO: check for integer wraparound (how does art/dalvik handle that?)
+        int initialKey = (int)startKey.getIntegralValue();
+        for (int i=0; i<elements.size(); i++) {
+            final SmaliPackedSwitchElement element = elements.get(i);
+
+            final int key = initialKey + i;
+
+            newElements.add(new SwitchElement() {
+                @Override public int getKey() {
+                    return key;
+                }
+
+                @Override public int getOffset() {
+                    SmaliLabelReference labelReference = element.getTarget();
+                    if (labelReference == null) {
+                        return 0;
+                    }
+
+                    SmaliLabel label = labelReference.resolve();
+                    if (label == null) {
+                        return 0;
+                    }
+
+                    return label.getOffset() - baseOffset;
+                }
+            });
+        }
+
+        return newElements;
+    }
+
+    @Override public int getCodeUnits() {
+        return psiInstruction.getInstructionSize()/2;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaSparseSwitchPayload.java b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaSparseSwitchPayload.java
new file mode 100644
index 0000000..15eaea2
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/dexlib/instruction/SmalideaSparseSwitchPayload.java
@@ -0,0 +1,92 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib.instruction;
+
+import com.google.common.base.Function;
+import com.google.common.collect.Lists;
+import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.iface.instruction.SwitchElement;
+import org.jf.dexlib2.iface.instruction.formats.SparseSwitchPayload;
+import org.jf.smalidea.psi.impl.*;
+import org.jf.smalidea.util.InstructionUtils;
+
+import javax.annotation.Nonnull;
+import java.util.List;
+
+public class SmalideaSparseSwitchPayload extends SmalideaInstruction implements SparseSwitchPayload {
+    public SmalideaSparseSwitchPayload(@Nonnull SmaliInstruction instruction) {
+        super(instruction);
+    }
+
+    @Nonnull @Override public List<? extends SwitchElement> getSwitchElements() {
+        List<SmaliSparseSwitchElement> elements = psiInstruction.getSparseSwitchElements();
+
+        SmaliMethod smaliMethod = psiInstruction.getParentMethod();
+        SmaliInstruction sparseSwitchInstruction = InstructionUtils.findFirstInstructionWithTarget(
+                smaliMethod, Opcode.SPARSE_SWITCH, psiInstruction.getOffset());
+        final int baseOffset;
+
+        if (sparseSwitchInstruction == null) {
+            baseOffset = 0;
+        } else {
+            baseOffset = sparseSwitchInstruction.getOffset();
+        }
+
+        return Lists.transform(elements, new Function<SmaliSparseSwitchElement, SwitchElement>() {
+            @Override public SwitchElement apply(final SmaliSparseSwitchElement element) {
+                return new SwitchElement() {
+                    @Override public int getKey() {
+                        return (int)element.getKey().getIntegralValue();
+                    }
+
+                    @Override public int getOffset() {
+                        SmaliLabelReference labelReference = element.getTarget();
+                        if (labelReference == null) {
+                            return 0;
+                        }
+
+                        SmaliLabel label = labelReference.resolve();
+                        if (label == null) {
+                            return 0;
+                        }
+
+                        return label.getOffset() - baseOffset;
+                    }
+                };
+            }
+        });
+    }
+
+    @Override public int getCodeUnits() {
+        return psiInstruction.getInstructionSize()/2;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/errorReporting/ErrorReporter.java b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ErrorReporter.java
new file mode 100644
index 0000000..92aef72
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ErrorReporter.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jf.smalidea.errorReporting;
+
+import com.intellij.diagnostic.IdeErrorsDialog;
+import com.intellij.diagnostic.LogMessageEx;
+import com.intellij.diagnostic.ReportMessages;
+import com.intellij.errorreport.bean.ErrorBean;
+import com.intellij.ide.DataManager;
+import com.intellij.ide.plugins.IdeaPluginDescriptor;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.idea.IdeaLogger;
+import com.intellij.notification.NotificationListener;
+import com.intellij.notification.NotificationType;
+import com.intellij.openapi.actionSystem.CommonDataKeys;
+import com.intellij.openapi.actionSystem.DataContext;
+import com.intellij.openapi.diagnostic.ErrorReportSubmitter;
+import com.intellij.openapi.diagnostic.IdeaLoggingEvent;
+import com.intellij.openapi.diagnostic.SubmittedReportInfo;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.progress.EmptyProgressIndicator;
+import com.intellij.openapi.progress.ProgressManager;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.Consumer;
+
+import java.awt.*;
+import java.util.Map;
+
+/**
+ * Sends crash reports to Github.
+ *
+ * Based on the go-lang plugin's error reporter
+ * (https://github.com/dlsniper/google-go-lang-idea-plugin/commit/c451006cc9fc926ca347189951baa94f4032c5c4)
+ */
+public class ErrorReporter extends ErrorReportSubmitter {
+
+  @Override
+  public String getReportActionText() {
+    return "Report as issue on smali's github repo";
+  }
+
+  @Override
+  public boolean submit(IdeaLoggingEvent[] events, String additionalInfo, Component parentComponent,
+                        final Consumer<SubmittedReportInfo> consumer) {
+    IdeaLoggingEvent event = events[0];
+    ErrorBean bean = new ErrorBean(event.getThrowable(), IdeaLogger.ourLastActionId);
+
+    final DataContext dataContext = DataManager.getInstance().getDataContext(parentComponent);
+
+    bean.setDescription(additionalInfo);
+    bean.setMessage(event.getMessage());
+
+    Throwable throwable = event.getThrowable();
+    if (throwable != null) {
+      final PluginId pluginId = IdeErrorsDialog.findPluginId(throwable);
+      if (pluginId != null) {
+        final IdeaPluginDescriptor ideaPluginDescriptor = PluginManager.getPlugin(pluginId);
+        if (ideaPluginDescriptor != null && !ideaPluginDescriptor.isBundled()) {
+          bean.setPluginName(ideaPluginDescriptor.getName());
+          bean.setPluginVersion(ideaPluginDescriptor.getVersion());
+        }
+      }
+    }
+
+    Object data = event.getData();
+
+    if (data instanceof LogMessageEx) {
+      bean.setAttachments(((LogMessageEx)data).getAttachments());
+    }
+
+    Map<String, String> reportValues = ITNProxy.createParameters(bean);
+
+    final Project project = CommonDataKeys.PROJECT.getData(dataContext);
+
+    Consumer<String> successCallback = new Consumer<String>() {
+      @Override
+      public void consume(String token) {
+        final SubmittedReportInfo reportInfo = new SubmittedReportInfo(
+                null, "Issue " + token, SubmittedReportInfo.SubmissionStatus.NEW_ISSUE);
+        consumer.consume(reportInfo);
+
+        ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT,
+                "Submitted",
+                NotificationType.INFORMATION,
+                null).setImportant(false).notify(project);
+      }
+    };
+
+    Consumer<Exception> errorCallback = new Consumer<Exception>() {
+      @Override
+      public void consume(Exception e) {
+        String message = String.format("<html>There was an error while creating a GitHub issue: %s<br>" +
+                "Please consider manually creating an issue on the " +
+                "<a href=\"https://github.com/JesusFreke/smali/issues\">Smali Issue Tracker</a></html>",
+                e.getMessage());
+        ReportMessages.GROUP.createNotification(ReportMessages.ERROR_REPORT,
+                message,
+                NotificationType.ERROR,
+                NotificationListener.URL_OPENING_LISTENER).setImportant(false).notify(project);
+      }
+    };
+
+    GithubFeedbackTask task = new GithubFeedbackTask(project, "Submitting error report", true,  reportValues,
+            successCallback, errorCallback);
+
+    if (project == null) {
+      task.run(new EmptyProgressIndicator());
+    } else {
+      ProgressManager.getInstance().run(task);
+    }
+    return true;
+  }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/errorReporting/GithubFeedbackTask.java b/smalidea/src/main/java/org/jf/smalidea/errorReporting/GithubFeedbackTask.java
new file mode 100644
index 0000000..ab54dc6
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/errorReporting/GithubFeedbackTask.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jf.smalidea.errorReporting;
+
+import com.google.common.io.CharStreams;
+import com.google.gson.Gson;
+import com.intellij.ide.plugins.IdeaPluginDescriptorImpl;
+import com.intellij.ide.plugins.PluginManager;
+import com.intellij.openapi.extensions.PluginId;
+import com.intellij.openapi.progress.ProgressIndicator;
+import com.intellij.openapi.progress.Task;
+import com.intellij.openapi.project.Project;
+import com.intellij.util.Consumer;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+import java.nio.charset.Charset;
+import java.util.LinkedHashMap;
+import java.util.Map;
+
+public class GithubFeedbackTask extends Task.Backgroundable {
+    private final Consumer<String> myCallback;
+    private final Consumer<Exception> myErrorCallback;
+    private final Map<String, String> myParams;
+
+    public GithubFeedbackTask(@Nullable Project project,
+                              @NotNull String title,
+                              boolean canBeCancelled,
+                              Map<String, String> params,
+                              final Consumer<String> callback,
+                              final Consumer<Exception> errorCallback) {
+        super(project, title, canBeCancelled);
+
+        myParams = params;
+        myCallback = callback;
+        myErrorCallback = errorCallback;
+    }
+
+    @Override
+    public void run(@NotNull ProgressIndicator indicator) {
+        indicator.setIndeterminate(true);
+        try {
+            String token = sendFeedback(myParams);
+            myCallback.consume(token);
+        }
+        catch (Exception e) {
+            myErrorCallback.consume(e);
+        }
+    }
+
+    private static String getToken() {
+        InputStream stream = GithubFeedbackTask.class.getClassLoader().getResourceAsStream("token");
+        if (stream == null) {
+            return null;
+        }
+        try {
+            return CharStreams.toString(new InputStreamReader(stream, "UTF-8"));
+        } catch (IOException ex) {
+            return null;
+        }
+    }
+
+    public static String sendFeedback(Map<String, String> environmentDetails) throws IOException {
+        String url = "https://api.github.com/repos/JesusFreke/smalidea-issues/issues";
+        String userAgent = "smalidea plugin";
+
+        IdeaPluginDescriptorImpl pluginDescriptor =
+                (IdeaPluginDescriptorImpl) PluginManager.getPlugin(PluginId.getId("org.jf.smalidea"));
+
+        if (pluginDescriptor != null) {
+            String name = pluginDescriptor.getName();
+            String version = pluginDescriptor.getVersion();
+            userAgent = name + " (" + version + ")";
+        }
+
+        HttpURLConnection httpURLConnection = connect(url);
+        httpURLConnection.setDoOutput(true);
+        httpURLConnection.setRequestMethod("POST");
+        httpURLConnection.setRequestProperty("User-Agent", userAgent);
+        httpURLConnection.setRequestProperty("Content-Type", "application/json");
+
+        String token = getToken();
+        if (token != null) {
+            httpURLConnection.setRequestProperty("Authorization", "token " + token);
+        }
+        OutputStream outputStream = httpURLConnection.getOutputStream();
+
+        try {
+            outputStream.write(convertToGithubIssueFormat(environmentDetails));
+        } finally {
+            outputStream.close();
+        }
+
+        int responseCode = httpURLConnection.getResponseCode();
+        if (responseCode != 201) {
+            throw new RuntimeException("Expected HTTP_CREATED (201), obtained " + responseCode);
+        }
+
+        return Long.toString(System.currentTimeMillis());
+    }
+
+    private static byte[] convertToGithubIssueFormat(Map<String, String> environmentDetails) {
+        LinkedHashMap<String, String> result = new LinkedHashMap<String, String>(5);
+        result.put("title", "[auto-generated] Crash in plugin");
+        result.put("body", generateGithubIssueBody(environmentDetails));
+
+        return ((new Gson()).toJson(result)).getBytes(Charset.forName("UTF-8"));
+    }
+
+    private static String generateGithubIssueBody(Map<String, String> body) {
+        String errorDescription = body.get("error.description");
+        if (errorDescription == null) {
+            errorDescription = "";
+        }
+        body.remove("error.description");
+
+        String errorMessage = body.get("error.message");
+        if (errorMessage == null || errorMessage.isEmpty()) {
+            errorMessage = "invalid error";
+        }
+        body.remove("error.message");
+
+        String stackTrace = body.get("error.stacktrace");
+        if (stackTrace == null || stackTrace.isEmpty()) {
+            stackTrace = "invalid stacktrace";
+        }
+        body.remove("error.stacktrace");
+
+        String result = "";
+
+        if (!errorDescription.isEmpty()) {
+            result += errorDescription + "\n\n";
+        }
+
+        for (Map.Entry<String, String> entry : body.entrySet()) {
+            result += entry.getKey() + ": " + entry.getValue() + "\n";
+        }
+
+        result += "\n```\n" + stackTrace + "\n```\n";
+
+        result += "\n```\n" + errorMessage + "\n```";
+
+        return result;
+    }
+
+    private static HttpURLConnection connect(String url) throws IOException {
+        HttpURLConnection connection = (HttpURLConnection) ((new URL(url)).openConnection());
+        connection.setConnectTimeout(5000);
+        connection.setReadTimeout(5000);
+        return connection;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/errorReporting/ITNProxy.java b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ITNProxy.java
new file mode 100644
index 0000000..5bf93e3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/errorReporting/ITNProxy.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jf.smalidea.errorReporting;
+
+import com.intellij.errorreport.bean.ErrorBean;
+import com.intellij.idea.IdeaLogger;
+import com.intellij.openapi.application.Application;
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.ApplicationNamesInfo;
+import com.intellij.openapi.application.ex.ApplicationInfoEx;
+import com.intellij.openapi.diagnostic.Attachment;
+import com.intellij.openapi.updateSettings.impl.UpdateSettings;
+import com.intellij.openapi.util.text.StringUtil;
+import com.intellij.util.SystemProperties;
+import com.intellij.util.containers.ContainerUtil;
+
+import java.util.Calendar;
+import java.util.Map;
+
+/**
+ * @author stathik
+ * @since Aug 4, 2003
+ */
+public class ITNProxy {
+
+  public static Map<String, String> createParameters(ErrorBean error) {
+    Map<String, String> params = ContainerUtil.newLinkedHashMap(40);
+
+    params.put("protocol.version", "1");
+
+    params.put("os.name", SystemProperties.getOsName());
+    params.put("java.version", SystemProperties.getJavaVersion());
+    params.put("java.vm.vendor", SystemProperties.getJavaVmVendor());
+
+    ApplicationInfoEx appInfo = ApplicationInfoEx.getInstanceEx();
+    ApplicationNamesInfo namesInfo = ApplicationNamesInfo.getInstance();
+    Application application = ApplicationManager.getApplication();
+    params.put("app.name", namesInfo.getProductName());
+    params.put("app.name.full", namesInfo.getFullProductName());
+    params.put("app.name.version", appInfo.getVersionName());
+    params.put("app.eap", Boolean.toString(appInfo.isEAP()));
+    params.put("app.internal", Boolean.toString(application.isInternal()));
+    params.put("app.build", appInfo.getBuild().asString());
+    params.put("app.version.major", appInfo.getMajorVersion());
+    params.put("app.version.minor", appInfo.getMinorVersion());
+    params.put("app.build.date", format(appInfo.getBuildDate()));
+    params.put("app.build.date.release", format(appInfo.getMajorReleaseBuildDate()));
+    params.put("app.compilation.timestamp", IdeaLogger.getOurCompilationTimestamp());
+
+    UpdateSettings updateSettings = UpdateSettings.getInstance();
+    params.put("update.channel.status", updateSettings.getSelectedChannelStatus().getCode());
+    params.put("update.ignored.builds", StringUtil.join(updateSettings.getIgnoredBuildNumbers(), ","));
+
+    params.put("plugin.name", error.getPluginName());
+    params.put("plugin.version", error.getPluginVersion());
+
+    params.put("last.action", error.getLastAction());
+    params.put("previous.exception", error.getPreviousException() == null ? null : Integer.toString(error.getPreviousException()));
+
+    params.put("error.message", error.getMessage());
+    params.put("error.stacktrace", error.getStackTrace());
+    params.put("error.description", error.getDescription());
+
+    params.put("assignee.id", error.getAssigneeId() == null ? null : Integer.toString(error.getAssigneeId()));
+
+    for (Attachment attachment : error.getAttachments()) {
+      params.put("attachment.name", attachment.getName());
+      params.put("attachment.value", attachment.getEncodedBytes());
+    }
+
+    return params;
+  }
+
+  private static String format(Calendar calendar) {
+    return calendar == null ?  null : Long.toString(calendar.getTime().getTime());
+  }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliClassReferenceSearcher.java b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliClassReferenceSearcher.java
new file mode 100644
index 0000000..63e4d6f
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliClassReferenceSearcher.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.openapi.application.QueryExecutorBase;
+import com.intellij.openapi.progress.EmptyProgressIndicator;
+import com.intellij.openapi.util.Computable;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.impl.search.LowLevelSearchUtil;
+import com.intellij.psi.search.*;
+import com.intellij.psi.search.searches.ReferencesSearch;
+import com.intellij.psi.search.searches.ReferencesSearch.SearchParameters;
+import com.intellij.util.Processor;
+import com.intellij.util.text.StringSearcher;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.util.NameUtils;
+
+public class SmaliClassReferenceSearcher extends QueryExecutorBase<PsiReference, ReferencesSearch.SearchParameters> {
+    @Override public void processQuery(final SearchParameters queryParameters, final Processor<PsiReference> consumer) {
+        final PsiElement element = queryParameters.getElementToSearch();
+        if (!(element instanceof PsiClass)) {
+            return;
+        }
+
+        String smaliType = ApplicationManager.getApplication().runReadAction(
+                new Computable<String>() {
+                    @Override public String compute() {
+                        String qualifiedName = ((PsiClass)element).getQualifiedName();
+                        if (qualifiedName != null) {
+                            return NameUtils.javaToSmaliType((PsiClass)element);
+                        }
+                        return null;
+                    }
+                });
+        if (smaliType == null) {
+            return;
+        }
+
+        final StringSearcher stringSearcher = new StringSearcher(smaliType, true, true, false, false);
+
+        final SingleTargetRequestResultProcessor processor = new SingleTargetRequestResultProcessor(element);
+
+        SearchScope querySearchScope = ApplicationManager.getApplication().runReadAction(
+                new Computable<SearchScope>() {
+                    @Override public SearchScope compute() {
+                        return queryParameters.getEffectiveSearchScope();
+                    }
+                });
+
+        if (querySearchScope instanceof LocalSearchScope) {
+            for (final PsiElement scopeElement : ((LocalSearchScope)querySearchScope).getScope()) {
+                ApplicationManager.getApplication().runReadAction(new Runnable() {
+                    @Override
+                    public void run() {
+                        LowLevelSearchUtil.processElementsContainingWordInElement(
+                                new TextOccurenceProcessor() {
+                                    @Override public boolean execute(
+                                            @NotNull PsiElement element, int offsetInElement) {
+                                        return processor.processTextOccurrence(element, offsetInElement, consumer);
+                                    }
+                                },
+                                scopeElement, stringSearcher, true, new EmptyProgressIndicator());
+                    }
+                });
+            }
+        } else if (querySearchScope instanceof GlobalSearchScope) {
+            PsiSearchHelper helper = PsiSearchHelper.SERVICE.getInstance(element.getProject());
+            // TODO: limit search scope to only smali files. See, e.g. AnnotatedPackagesSearcher.PackageInfoFilesOnly
+            helper.processAllFilesWithWord(smaliType, (GlobalSearchScope)querySearchScope,
+                    new Processor<PsiFile>() {
+                        @Override
+                        public boolean process(PsiFile file) {
+                            LowLevelSearchUtil.processElementsContainingWordInElement(
+                                    new TextOccurenceProcessor() {
+                                        @Override public boolean execute(
+                                                @NotNull PsiElement element, int offsetInElement) {
+                                            return processor.processTextOccurrence(element, offsetInElement, consumer);
+                                        }
+                                    },
+                                    file, stringSearcher, true, new EmptyProgressIndicator());
+                            return true;
+                        }
+                    }, true);
+        } else {
+            assert false;
+            return;
+        }
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliFindUsagesProvider.java b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliFindUsagesProvider.java
new file mode 100644
index 0000000..335bce2
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliFindUsagesProvider.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+import com.intellij.lang.cacheBuilder.WordsScanner;
+import com.intellij.lang.java.JavaFindUsagesProvider;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public class SmaliFindUsagesProvider extends JavaFindUsagesProvider {
+    @Override public boolean canFindUsagesFor(@NotNull PsiElement element) {
+        return element instanceof PsiClass;
+    }
+
+    @Nullable @Override public WordsScanner getWordsScanner() {
+        return new SmaliWordScanner();
+    }
+
+
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTargetProvider.java b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTargetProvider.java
new file mode 100644
index 0000000..e87782b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTargetProvider.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+import com.intellij.codeInsight.TargetElementUtilBase;
+import com.intellij.find.findUsages.PsiElement2UsageTargetAdapter;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.usages.UsageTarget;
+import com.intellij.usages.UsageTargetProvider;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.impl.SmaliMemberName;
+
+/**
+ * A usage target provider for smali member names consisting of primitive types.
+ *
+ * For member names like IIII, the default logic to find the usage target doesn't work, due to the member
+ * name being split up into multiple leaf tokens.
+ */
+public class SmaliUsageTargetProvider implements UsageTargetProvider {
+    @Nullable @Override public UsageTarget[] getTargets(Editor editor, PsiFile file) {
+        PsiElement element = file.findElementAt(
+                TargetElementUtilBase.adjustOffset(file, editor.getDocument(), editor.getCaretModel().getOffset()));
+        if (element == null) {
+            return null;
+        }
+        return getTargets(element);
+    }
+
+    @Nullable @Override public UsageTarget[] getTargets(PsiElement element) {
+        ASTNode node = element.getNode();
+        if (node == null) {
+            return null;
+        }
+
+        if (node.getElementType() == SmaliTokens.PARAM_LIST_OR_ID_PRIMITIVE_TYPE) {
+            PsiElement parent = element.getParent();
+            if (parent instanceof SmaliMemberName) {
+                return new UsageTarget[] { new PsiElement2UsageTargetAdapter(parent.getParent()) };
+            }
+        }
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTypeProvider.java b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTypeProvider.java
new file mode 100644
index 0000000..ad86942
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliUsageTypeProvider.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+import com.intellij.psi.*;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.usages.impl.rules.UsageType;
+import com.intellij.usages.impl.rules.UsageTypeProvider;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.dexlib2.Opcode;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.impl.*;
+
+import java.util.EnumSet;
+import java.util.Set;
+
+public class SmaliUsageTypeProvider implements UsageTypeProvider {
+
+    static final UsageType CLASS_DECLARATION = new UsageType("Class declaration");
+    static final UsageType VERIFICATION_ERROR = new UsageType("Usage in verification error");
+    static final UsageType FIELD_TYPE_REFERENCE = new UsageType("Usage as field type in a field reference");
+    static final UsageType FIELD_DECLARING_TYPE_REFERENCE = new UsageType("Usage as a declaring type in a field reference");
+    static final UsageType METHOD_RETURN_TYPE_REFERENCE = new UsageType("Usage as return type in a method reference");
+    static final UsageType METHOD_PARAM_REFERENCE = new UsageType("Usage as parameter in a method reference");
+    static final UsageType METHOD_DECLARING_TYPE_REFERENCE = new UsageType("Usage as a declaring type in a method reference");
+    static final UsageType LITERAL = new UsageType("Usage as a literal");
+
+    @Nullable @Override public UsageType getUsageType(PsiElement element) {
+        if (element instanceof PsiReference) {
+            PsiElement referenced = ((PsiReference) element).resolve();
+            if (referenced != null) {
+                if (referenced instanceof PsiClass) {
+                    return findClassUsageType(element);
+                } else if (referenced instanceof PsiField) {
+                    return findFieldUsageType(element);
+                } else if (referenced instanceof PsiMethod) {
+                    return findMethodUsageType(element);
+                }
+            }
+        }
+        return UsageType.UNCLASSIFIED;
+    }
+
+    private final Set<Opcode> newArrayInstructions = EnumSet.of(Opcode.FILLED_NEW_ARRAY, Opcode.NEW_ARRAY,
+            Opcode.FILLED_NEW_ARRAY_RANGE);
+
+    private final Set<Opcode> fieldReadInstructions = EnumSet.of(Opcode.IGET, Opcode.IGET_BOOLEAN, Opcode.IGET_BYTE,
+            Opcode.IGET_CHAR, Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_VOLATILE, Opcode.IGET_SHORT, Opcode.IGET_VOLATILE,
+            Opcode.IGET_WIDE, Opcode.IGET_WIDE_VOLATILE, Opcode.SGET, Opcode.SGET_BOOLEAN, Opcode.SGET_BYTE,
+            Opcode.SGET_CHAR, Opcode.SGET_OBJECT, Opcode.SGET_OBJECT_VOLATILE, Opcode.SGET_SHORT, Opcode.SGET_VOLATILE,
+            Opcode.SGET_WIDE, Opcode.SGET_WIDE_VOLATILE);
+
+    private final Set<Opcode> fieldWriteInstructions = EnumSet.of(Opcode.IPUT, Opcode.IPUT_BOOLEAN, Opcode.IPUT_BYTE,
+            Opcode.IPUT_CHAR, Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_VOLATILE, Opcode.IPUT_SHORT, Opcode.IPUT_VOLATILE,
+            Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_VOLATILE, Opcode.SPUT, Opcode.SPUT_BOOLEAN, Opcode.SPUT_BYTE,
+            Opcode.SPUT_CHAR, Opcode.SPUT_OBJECT, Opcode.SPUT_OBJECT_VOLATILE, Opcode.SPUT_SHORT, Opcode.SPUT_VOLATILE,
+            Opcode.SPUT_WIDE, Opcode.SPUT_WIDE_VOLATILE);
+
+    @Nullable
+    private UsageType findClassUsageType(@NotNull PsiElement element) {
+        PsiElement originalElement = element;
+
+        while (element != null) {
+            if (element instanceof SmaliFieldReference) {
+                PsiElement prev = originalElement.getPrevSibling();
+                while (prev != null) {
+                    // if the element is to the right of a colon, then it is the field type, otherwise it is
+                    // the declaring class
+                    if (prev.getNode().getElementType() == SmaliTokens.COLON) {
+                        return FIELD_TYPE_REFERENCE;
+                    }
+                    prev = prev.getPrevSibling();
+                }
+                return FIELD_DECLARING_TYPE_REFERENCE;
+            } else if (element instanceof SmaliMethodReferenceParamList) {
+                return METHOD_PARAM_REFERENCE;
+            } else if (element instanceof SmaliMethodReference) {
+                PsiElement prev = originalElement.getPrevSibling();
+                while (prev != null) {
+                    IElementType elementType = prev.getNode().getElementType();
+                    // if the element is to the right of a close paren, then it is the return type,
+                    // otherwise it is the declaring class. Any parameter type will be taken care of by the previous
+                    // "if" for SmaliMethodReferenceParamList
+                    if (elementType == SmaliTokens.CLOSE_PAREN) {
+                        return METHOD_RETURN_TYPE_REFERENCE;
+                    }
+                    prev = prev.getPrevSibling();
+                }
+                return METHOD_DECLARING_TYPE_REFERENCE;
+            } else if (element instanceof SmaliInstruction) {
+                Opcode opcode = ((SmaliInstruction) element).getOpcode();
+                if (opcode == Opcode.INSTANCE_OF) {
+                    return UsageType.CLASS_INSTANCE_OF;
+                } else if (opcode == Opcode.CHECK_CAST) {
+                    return UsageType.CLASS_CAST_TO;
+                } else if (newArrayInstructions.contains(opcode)) {
+                    return UsageType.CLASS_NEW_ARRAY;
+                } else if (opcode == Opcode.NEW_INSTANCE) {
+                    return UsageType.CLASS_NEW_OPERATOR;
+                } else if (opcode == Opcode.CONST_CLASS) {
+                    return UsageType.CLASS_CLASS_OBJECT_ACCESS;
+                } else if (opcode == Opcode.THROW_VERIFICATION_ERROR) {
+                    return VERIFICATION_ERROR;
+                }
+            } else if (element instanceof SmaliSuperStatement || element instanceof SmaliImplementsStatement) {
+                return UsageType.CLASS_EXTENDS_IMPLEMENTS_LIST;
+            } else if (element instanceof SmaliClassStatement) {
+                return CLASS_DECLARATION;
+            } else if (element instanceof SmaliMethodParamList) {
+                return UsageType.CLASS_METHOD_PARAMETER_DECLARATION;
+            } else if (element instanceof SmaliMethodPrototype) {
+                return UsageType.CLASS_METHOD_RETURN_TYPE;
+            } else if (element instanceof SmaliField) {
+                return UsageType.CLASS_FIELD_DECLARATION;
+            } else if (element instanceof SmaliCatchStatement) {
+                return UsageType.CLASS_CATCH_CLAUSE_PARAMETER_DECLARATION;
+            } else if (element instanceof SmaliLocalDebugStatement) {
+                return UsageType.CLASS_LOCAL_VAR_DECLARATION;
+            } else if (element instanceof SmaliAnnotation) {
+                return UsageType.ANNOTATION;
+            } else if (element instanceof SmaliLiteral) {
+                return LITERAL;
+            }
+            element = element.getParent();
+        }
+        return UsageType.UNCLASSIFIED;
+    }
+
+    @Nullable
+    private UsageType findFieldUsageType(@NotNull PsiElement element) {
+        PsiElement originalElement = element;
+
+        while (element != null) {
+            element = element.getParent();
+
+            if (element instanceof SmaliInstruction) {
+                Opcode opcode = ((SmaliInstruction) element).getOpcode();
+                if (fieldReadInstructions.contains(opcode)) {
+                    return UsageType.READ;
+                } else if (fieldWriteInstructions.contains(opcode)) {
+                    return UsageType.WRITE;
+                } else if (opcode == Opcode.THROW_VERIFICATION_ERROR) {
+                    return VERIFICATION_ERROR;
+                }
+            } if (element instanceof SmaliLiteral) {
+                return LITERAL;
+            }
+        }
+        return UsageType.UNCLASSIFIED;
+    }
+
+    @Nullable
+    private UsageType findMethodUsageType(@NotNull PsiElement element) {
+        PsiElement originalElement = element;
+
+        while (element != null) {
+            element = element.getParent();
+
+            if (element instanceof SmaliInstruction) {
+                Opcode opcode = ((SmaliInstruction) element).getOpcode();
+                if (opcode == Opcode.THROW_VERIFICATION_ERROR) {
+                    return VERIFICATION_ERROR;
+                }
+            } if (element instanceof SmaliLiteral) {
+                return LITERAL;
+            }
+        }
+        return UsageType.UNCLASSIFIED;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliWordScanner.java b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliWordScanner.java
new file mode 100644
index 0000000..56bbdaf
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/findUsages/SmaliWordScanner.java
@@ -0,0 +1,118 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+import com.intellij.lang.cacheBuilder.WordOccurrence;
+import com.intellij.lang.cacheBuilder.WordOccurrence.Kind;
+import com.intellij.lang.cacheBuilder.WordsScanner;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.psi.tree.TokenSet;
+import com.intellij.util.Processor;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.SmaliLexer;
+import org.jf.smalidea.SmaliTokens;
+
+public class SmaliWordScanner implements WordsScanner {
+
+    private static final TokenSet MEMBER_NAME_TOKENS = TokenSet.create(
+            SmaliTokens.MEMBER_NAME,
+            SmaliTokens.SIMPLE_NAME,
+            SmaliTokens.ACCESS_SPEC,
+            SmaliTokens.VERIFICATION_ERROR_TYPE,
+            SmaliTokens.POSITIVE_INTEGER_LITERAL,
+            SmaliTokens.NEGATIVE_INTEGER_LITERAL,
+            SmaliTokens.FLOAT_LITERAL_OR_ID,
+            SmaliTokens.DOUBLE_LITERAL_OR_ID,
+            SmaliTokens.BOOL_LITERAL,
+            SmaliTokens.NULL_LITERAL,
+            SmaliTokens.REGISTER,
+            SmaliTokens.PRIMITIVE_TYPE,
+            SmaliTokens.VOID_TYPE,
+            SmaliTokens.ANNOTATION_VISIBILITY,
+            SmaliTokens.INSTRUCTION_FORMAT10t,
+            SmaliTokens.INSTRUCTION_FORMAT10x,
+            SmaliTokens.INSTRUCTION_FORMAT10x_ODEX,
+            SmaliTokens.INSTRUCTION_FORMAT11x,
+            SmaliTokens.INSTRUCTION_FORMAT12x_OR_ID,
+            SmaliTokens.INSTRUCTION_FORMAT21c_FIELD,
+            SmaliTokens.INSTRUCTION_FORMAT21c_FIELD_ODEX,
+            SmaliTokens.INSTRUCTION_FORMAT21c_STRING,
+            SmaliTokens.INSTRUCTION_FORMAT21c_TYPE,
+            SmaliTokens.INSTRUCTION_FORMAT21t,
+            SmaliTokens.INSTRUCTION_FORMAT22c_FIELD,
+            SmaliTokens.INSTRUCTION_FORMAT22c_FIELD_ODEX,
+            SmaliTokens.INSTRUCTION_FORMAT22c_TYPE,
+            SmaliTokens.INSTRUCTION_FORMAT22cs_FIELD,
+            SmaliTokens.INSTRUCTION_FORMAT22s_OR_ID,
+            SmaliTokens.INSTRUCTION_FORMAT22t,
+            SmaliTokens.INSTRUCTION_FORMAT23x,
+            SmaliTokens.INSTRUCTION_FORMAT31i_OR_ID,
+            SmaliTokens.INSTRUCTION_FORMAT31t,
+            SmaliTokens.INSTRUCTION_FORMAT35c_METHOD,
+            SmaliTokens.INSTRUCTION_FORMAT35c_METHOD_ODEX,
+            SmaliTokens.INSTRUCTION_FORMAT35c_TYPE,
+            SmaliTokens.INSTRUCTION_FORMAT35mi_METHOD,
+            SmaliTokens.INSTRUCTION_FORMAT35ms_METHOD,
+            SmaliTokens.INSTRUCTION_FORMAT51l);
+
+    @Override public void processWords(CharSequence fileText, Processor<WordOccurrence> processor) {
+        SmaliLexer lexer = new SmaliLexer();
+        lexer.start(fileText);
+
+        IElementType type = lexer.getTokenType();
+        while (type != null) {
+            if (type == SmaliTokens.CLASS_DESCRIPTOR) {
+                processClassDescriptor(fileText, lexer.getTokenStart(), lexer.getTokenEnd(), processor);
+            } else if (MEMBER_NAME_TOKENS.contains(type)) {
+                processor.process(new WordOccurrence(fileText, lexer.getTokenStart(), lexer.getTokenEnd(), Kind.CODE));
+            } else if (type == SmaliTokens.PARAM_LIST_OR_ID_PRIMITIVE_TYPE) {
+                int tokenStart = lexer.getTokenStart();
+                while (type == SmaliTokens.PARAM_LIST_OR_ID_PRIMITIVE_TYPE) {
+                    lexer.advance();
+                    type = lexer.getTokenType();
+                }
+                int tokenEnd = lexer.getTokenStart();
+                processor.process(new WordOccurrence(fileText, tokenStart, tokenEnd, Kind.CODE));
+            }
+            lexer.advance();
+            type = lexer.getTokenType();
+        }
+    }
+
+    private void processClassDescriptor(CharSequence fileText, int tokenStart, int tokenEnd,
+                                        @NotNull Processor<WordOccurrence> processor) {
+        CharSequence tokenText = fileText.subSequence(tokenStart, tokenEnd);
+
+        assert tokenText.charAt(0) == 'L' && tokenText.charAt(tokenText.length()-1) == ';';
+        processor.process(new WordOccurrence(fileText, tokenStart, tokenEnd, Kind.CODE));
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/SmaliCompositeElementFactory.java b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliCompositeElementFactory.java
new file mode 100644
index 0000000..6d5bb10
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliCompositeElementFactory.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi;
+
+import org.jf.smalidea.psi.impl.SmaliCompositeElement;
+
+public interface SmaliCompositeElementFactory {
+    SmaliCompositeElement createElement();
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/SmaliCompositeElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliCompositeElementType.java
new file mode 100644
index 0000000..ae5fc53
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliCompositeElementType.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi;
+
+import com.intellij.psi.tree.ICompositeElementType;
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.SmaliLanguage;
+import org.jf.smalidea.psi.impl.SmaliCompositeElement;
+
+public class SmaliCompositeElementType extends IElementType implements ICompositeElementType {
+    @NotNull private final SmaliCompositeElementFactory factory;
+
+    public SmaliCompositeElementType(@NotNull @NonNls String debugName,
+                                     @NotNull SmaliCompositeElementFactory factory) {
+        super(debugName, SmaliLanguage.INSTANCE);
+        this.factory = factory;
+    }
+
+    @NotNull @Override public SmaliCompositeElement createCompositeNode() {
+        return factory.createElement();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/SmaliElementTypes.java b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliElementTypes.java
new file mode 100644
index 0000000..bb4bd37
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/SmaliElementTypes.java
@@ -0,0 +1,122 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi;
+
+import org.jf.smalidea.psi.impl.*;
+import org.jf.smalidea.psi.stub.element.*;
+
+public class SmaliElementTypes {
+    public static final SmaliFileElementType FILE = SmaliFileElementType.INSTANCE;
+    public static final SmaliClassElementType CLASS = SmaliClassElementType.INSTANCE;
+    public static final SmaliFieldElementType FIELD = SmaliFieldElementType.INSTANCE;
+    public static final SmaliMethodElementType METHOD = SmaliMethodElementType.INSTANCE;
+    public static final SmaliClassStatementElementType CLASS_STATEMENT = SmaliClassStatementElementType.INSTANCE;
+    public static final SmaliMethodPrototypeElementType METHOD_PROTOTYPE = SmaliMethodPrototypeElementType.INSTANCE;
+    public static final SmaliMethodParamListElementType METHOD_PARAM_LIST = SmaliMethodParamListElementType.INSTANCE;
+    public static final SmaliMethodParameterElementType METHOD_PARAMETER = SmaliMethodParameterElementType.INSTANCE;
+    public static final SmaliAnnotationElementType ANNOTATION = SmaliAnnotationElementType.INSTANCE;
+    public static final SmaliModifierListElementType MODIFIER_LIST = SmaliModifierListElementType.INSTANCE;
+    public static final SmaliExtendsListElementType EXTENDS_LIST = SmaliExtendsListElementType.INSTANCE;
+    public static final SmaliImplementsListElementType IMPLEMENTS_LIST = SmaliImplementsListElementType.INSTANCE;
+    public static final SmaliThrowsListElementType THROWS_LIST = SmaliThrowsListElementType.INSTANCE;
+
+    public static final SmaliCompositeElementType LITERAL =
+            new SmaliCompositeElementType("LITERAL", SmaliLiteral.FACTORY);
+    public static final SmaliCompositeElementType SUPER_STATEMENT =
+            new SmaliCompositeElementType("SUPER_STATEMENT", SmaliSuperStatement.FACTORY);
+    public static final SmaliCompositeElementType IMPLEMENTS_STATEMENT =
+            new SmaliCompositeElementType("IMPLEMENTS_STATEMENT", SmaliImplementsStatement.FACTORY);
+    public static final SmaliCompositeElementType SOURCE_STATEMENT =
+            new SmaliCompositeElementType("SOURCE_STATEMENT", SmaliSourceStatement.FACTORY);
+    public static final SmaliCompositeElementType REGISTERS_STATEMENT =
+            new SmaliCompositeElementType("REGISTERS_STATEMENT", SmaliRegistersStatement.FACTORY);
+    public static final SmaliCompositeElementType REGISTER_REFERENCE =
+            new SmaliCompositeElementType("REGISTER_REFERENCE", SmaliRegisterReference.FACTORY);
+    public static final SmaliCompositeElementType MEMBER_NAME =
+            new SmaliCompositeElementType("MEMBER_NAME", SmaliMemberName.FACTORY);
+    public static final SmaliCompositeElementType LOCAL_NAME =
+            new SmaliCompositeElementType("LOCAL_NAME", SmaliLocalName.FACTORY);
+    public static final SmaliCompositeElementType PARAMETER_STATEMENT =
+            new SmaliCompositeElementType("PARAMETER_STATEMENT", SmaliParameterStatement.FACTORY);
+    public static final SmaliCompositeElementType FIELD_INITIALIZER =
+            new SmaliCompositeElementType("FIELD_INITIALIZER", SmaliFieldInitializer.FACTORY);
+    public static final SmaliCompositeElementType INSTRUCTION =
+            new SmaliCompositeElementType("INSTRUCTION", SmaliInstruction.FACTORY);
+    public static final SmaliCompositeElementType ANNOTATION_PARAMETER_LIST =
+            new SmaliCompositeElementType("ANNOTATION_PARAMETER_LIST", SmaliAnnotationParameterList.FACTORY);
+    public static final SmaliCompositeElementType ANNOTATION_ELEMENT =
+            new SmaliCompositeElementType("ANNOTATION_ELEMENT", SmaliAnnotationElement.FACTORY);
+    public static final SmaliCompositeElementType ANNOTATION_ELEMENT_NAME =
+            new SmaliCompositeElementType("ANNOTATION_ELEMENT_NAME", SmaliAnnotationElementName.FACTORY);
+    public static final SmaliCompositeElementType FIELD_REFERENCE =
+            new SmaliCompositeElementType("FIELD_REFERENCE", SmaliFieldReference.FACTORY);
+    public static final SmaliCompositeElementType METHOD_REFERENCE =
+            new SmaliCompositeElementType("METHOD_REFERENCE", SmaliMethodReference.FACTORY);
+    public static final SmaliCompositeElementType METHOD_REFERENCE_PARAM_LIST =
+            new SmaliCompositeElementType("METHOD_REFERENCE_PARAM_LIST", SmaliMethodReferenceParamList.FACTORY);
+    public static final SmaliCompositeElementType LABEL =
+            new SmaliCompositeElementType("LABEL", SmaliLabel.FACTORY);
+    public static final SmaliCompositeElementType LABEL_REFERENCE =
+            new SmaliCompositeElementType("LABEL_REFERENCE", SmaliLabelReference.FACTORY);
+    public static final SmaliCompositeElementType LINE_DEBUG_STATEMENT =
+            new SmaliCompositeElementType("LINE_DEBUG_STATEMENT", SmaliLineDebugStatement.FACTORY);
+    public static final SmaliCompositeElementType LOCAL_DEBUG_STATEMENT =
+            new SmaliCompositeElementType("LOCAL_DEBUG_STATEMENT", SmaliLocalDebugStatement.FACTORY);
+    public static final SmaliCompositeElementType END_LOCAL_DEBUG_STATEMENT =
+            new SmaliCompositeElementType("END_LOCAL_DEBUG_STATEMENT", SmaliEndLocalDebugStatement.FACTORY);
+    public static final SmaliCompositeElementType RESTART_LOCAL_DEBUG_STATEMENT =
+            new SmaliCompositeElementType("RESTART_LOCAL_DEBUG_STATEMENT", SmaliRestartLocalDebugStatement.FACTORY);
+    public static final SmaliCompositeElementType PROLOGUE_DEBUG_STATEMENT =
+            new SmaliCompositeElementType("PROLOGUE_DEBUG_STATEMENT", SmaliPrologueDebugStatement.FACTORY);
+    public static final SmaliCompositeElementType EPILOGUE_DEBUG_STATEMENT =
+            new SmaliCompositeElementType("EPILOGUE_DEBUG_STATEMENT", SmaliEpilogueDebugStatement.FACTORY);
+    public static final SmaliCompositeElementType SOURCE_DEBUG_STATEMENT =
+            new SmaliCompositeElementType("SOURCE_DEBUG_STATEMENT", SmaliSourceDebugStatement.FACTORY);
+    public static final SmaliCompositeElementType PRIMITIVE_TYPE =
+            new SmaliCompositeElementType("PRIMITIVE_TYPE", SmaliPrimitiveTypeElement.FACTORY);
+    public static final SmaliCompositeElementType CLASS_TYPE =
+            new SmaliCompositeElementType("CLASS_TYPE", SmaliClassTypeElement.FACTORY);
+    public static final SmaliCompositeElementType ARRAY_TYPE =
+            new SmaliCompositeElementType("ARRAY_TYPE", SmaliArrayTypeElement.FACTORY);
+    public static final SmaliCompositeElementType VOID_TYPE =
+            new SmaliCompositeElementType("VOID_TYPE", SmaliVoidTypeElement.FACTORY);
+    public static final SmaliCompositeElementType CATCH_STATEMENT =
+            new SmaliCompositeElementType("CATCH_STATEMENT", SmaliCatchStatement.FACTORY);
+    public static final SmaliCompositeElementType CATCH_ALL_STATEMENT =
+            new SmaliCompositeElementType("CATCH_ALL_STATEMENT", SmaliCatchAllStatement.FACTORY);
+    public static final SmaliCompositeElementType PACKED_SWITCH_ELEMENT =
+            new SmaliCompositeElementType("PACKED_SWITCH_ELEMENT", SmaliPackedSwitchElement.FACTORY);
+    public static final SmaliCompositeElementType SPARSE_SWITCH_ELEMENT =
+            new SmaliCompositeElementType("SPARSE_SWITCH_ELEMENT", SmaliSparseSwitchElement.FACTORY);
+    public static final SmaliCompositeElementType ARRAY_DATA_ELEMENT =
+            new SmaliCompositeElementType("ARRAY_DATA_ELEMENT", SmaliArrayDataElement.FACTORY);
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/iface/SmaliModifierListOwner.java b/smalidea/src/main/java/org/jf/smalidea/psi/iface/SmaliModifierListOwner.java
new file mode 100644
index 0000000..3da47c9
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/iface/SmaliModifierListOwner.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.iface;
+
+import com.intellij.psi.PsiAnnotationOwner;
+import com.intellij.psi.PsiModifierListOwner;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.impl.SmaliAnnotation;
+import org.jf.smalidea.psi.impl.SmaliModifierList;
+
+public interface SmaliModifierListOwner extends PsiModifierListOwner, PsiAnnotationOwner {
+    @NotNull @Override SmaliAnnotation[] getAnnotations();
+    @NotNull @Override SmaliAnnotation[] getApplicableAnnotations();
+    @Nullable @Override SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName);
+    @NotNull @Override SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName);
+    @Nullable @Override SmaliModifierList getModifierList();
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/LightSmaliClassTypeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/LightSmaliClassTypeElement.java
new file mode 100644
index 0000000..acd9a02
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/LightSmaliClassTypeElement.java
@@ -0,0 +1,187 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.light.LightElement;
+import com.intellij.psi.infos.CandidateInfo;
+import com.intellij.psi.scope.PsiScopeProcessor;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliLanguage;
+import org.jf.smalidea.util.NameUtils;
+
+public class LightSmaliClassTypeElement extends LightElement
+        implements PsiTypeElement, PsiReference, PsiJavaCodeReferenceElement {
+    @NotNull
+    private final String smaliName;
+
+    public LightSmaliClassTypeElement(@NotNull PsiManager manager, @NotNull String smaliName) {
+        super(manager, SmaliLanguage.INSTANCE);
+        this.smaliName = smaliName;
+    }
+
+    @Override public String toString() {
+        return "LightSmaliClassTypeElement:" + smaliName;
+    }
+
+    @NotNull @Override public PsiType getType() {
+        return new SmaliClassType(this);
+    }
+
+    @Nullable @Override public LightSmaliClassTypeElement getInnermostComponentReferenceElement() {
+        return this;
+    }
+
+    @Override public String getText() {
+        return smaliName;
+    }
+
+    @Override public PsiReference getReference() {
+        return this;
+    }
+
+    @Override public PsiElement getElement() {
+        return this;
+    }
+
+    @Override public TextRange getRangeInElement() {
+        return new TextRange(0, getTextLength());
+    }
+
+    @Nullable @Override public PsiClass resolve() {
+        return NameUtils.resolveSmaliType(this, smaliName);
+    }
+
+    @NotNull @Override public String getCanonicalText() {
+        return NameUtils.resolveSmaliToJavaType(this, smaliName);
+    }
+
+    @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public boolean isReferenceTo(PsiElement element) {
+        if (!(element instanceof PsiClassType)) {
+            return false;
+        }
+        return element.getManager().areElementsEquivalent(element, resolve());
+    }
+
+    @NotNull @Override public Object[] getVariants() {
+        throw new RuntimeException("Variants are not available for light references");
+    }
+
+    @Override public boolean isSoft() {
+        return false;
+    }
+
+    @NotNull @Override public PsiAnnotation[] getAnnotations() {
+        return new PsiAnnotation[0];
+    }
+
+    @NotNull @Override public PsiAnnotation[] getApplicableAnnotations() {
+        return new PsiAnnotation[0];
+    }
+
+    @Nullable @Override public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        return null;
+    }
+
+    @NotNull @Override public PsiAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        throw new UnsupportedOperationException();
+    }
+
+    // ***************************************************************************
+    // Below are the PsiJavaCodeReferenceElement-specific methods
+
+    @Override public void processVariants(@NotNull PsiScopeProcessor processor) {
+        // TODO: maybe just do nothing?
+        throw new UnsupportedOperationException();
+    }
+
+    @Nullable @Override public PsiElement getReferenceNameElement() {
+        // TODO: implement if needed
+        throw new UnsupportedOperationException();
+    }
+
+    @Nullable @Override public PsiReferenceParameterList getParameterList() {
+        // TODO: (generics) implement this
+        return null;
+    }
+
+    @NotNull @Override public PsiType[] getTypeParameters() {
+        // TODO: (generics) implement this
+        return new PsiType[0];
+    }
+
+    @Override public boolean isQualified() {
+        // TODO: should this return false for classes in the top level package?
+        return true;
+    }
+
+    @Override public String getQualifiedName() {
+        return getCanonicalText();
+    }
+
+    @NotNull @Override public JavaResolveResult advancedResolve(boolean incompleteCode) {
+        PsiClass element = resolve();
+        if (element == null) {
+            return JavaResolveResult.EMPTY;
+        }
+        return new CandidateInfo(element, PsiSubstitutor.EMPTY);
+    }
+
+    @NotNull @Override public JavaResolveResult[] multiResolve(boolean incompleteCode) {
+        PsiClass element = resolve();
+        if (element == null) {
+            return JavaResolveResult.EMPTY_ARRAY;
+        }
+        return new CandidateInfo[] { new CandidateInfo(element, PsiSubstitutor.EMPTY) };
+    }
+
+    @Nullable @Override public PsiElement getQualifier() {
+        // TODO: implement this if needed
+        throw new UnsupportedOperationException();
+    }
+
+    @Nullable @Override public String getReferenceName() {
+        return getName();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotation.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotation.java
new file mode 100644
index 0000000..e36313b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotation.java
@@ -0,0 +1,116 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.PsiImplUtil;
+import com.intellij.psi.meta.PsiMetaData;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.stub.SmaliAnnotationStub;
+
+public class SmaliAnnotation extends SmaliStubBasedPsiElement<SmaliAnnotationStub> implements PsiAnnotation {
+    public SmaliAnnotation(@NotNull SmaliAnnotationStub stub) {
+        super(stub, SmaliElementTypes.ANNOTATION);
+    }
+
+    public SmaliAnnotation(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @NotNull @Override public SmaliAnnotationParameterList getParameterList() {
+        SmaliAnnotationParameterList paramList = findChildByClass(SmaliAnnotationParameterList.class);
+        // The structure of the parser should ensure the param list is always present, even if there are syntax errors
+        assert paramList != null;
+        return paramList;
+    }
+
+    @Nullable @Override public String getQualifiedName() {
+        PsiJavaCodeReferenceElement nameElement = getNameReferenceElement();
+        if (nameElement != null) {
+            return nameElement.getQualifiedName();
+        }
+        return null;
+    }
+
+    @Nullable public String getSmaliName() {
+        SmaliAnnotationStub stub = getStub();
+        if (stub != null) {
+            return stub.getAnnotationSmaliTypeName();
+        }
+
+        SmaliClassTypeElement classType = findChildByClass(SmaliClassTypeElement.class);
+        if (classType == null) {
+            return null;
+        }
+        return classType.getSmaliName();
+    }
+
+    @Nullable @Override public PsiJavaCodeReferenceElement getNameReferenceElement() {
+        SmaliAnnotationStub stub = getStub();
+        if (stub != null) {
+            String smaliName = stub.getAnnotationSmaliTypeName();
+            if (smaliName != null) {
+                return new LightSmaliClassTypeElement(getManager(), smaliName);
+            }
+        }
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+
+    @Nullable @Override public PsiAnnotationMemberValue findAttributeValue(@Nullable @NonNls String attributeName) {
+        return PsiImplUtil.findAttributeValue(this, attributeName);
+    }
+
+    @Nullable @Override
+    public PsiAnnotationMemberValue findDeclaredAttributeValue(@Nullable @NonNls String attributeName) {
+        return PsiImplUtil.findDeclaredAttributeValue(this, attributeName);
+    }
+
+    @Override
+    public <T extends PsiAnnotationMemberValue> T setDeclaredAttributeValue(
+            @Nullable @NonNls String attributeName, @Nullable T value) {
+        // TODO: implement this
+        throw new UnsupportedOperationException();
+    }
+
+    @Nullable @Override public PsiAnnotationOwner getOwner() {
+        return (PsiAnnotationOwner)getStubOrPsiParent();
+    }
+
+    @Nullable @Override public PsiMetaData getMetaData() {
+        // I have no idea what this is
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationElement.java
new file mode 100644
index 0000000..10784f8
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationElement.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiAnnotationMemberValue;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiNameValuePair;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliAnnotationElement extends SmaliCompositeElement implements PsiNameValuePair {
+    // TODO: consider making this a stub
+
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliAnnotationElement();
+        }
+    };
+
+    public SmaliAnnotationElement() {
+        super(SmaliElementTypes.ANNOTATION_ELEMENT);
+    }
+
+    @Override public String getName() {
+        SmaliAnnotationElementName identifier = getNameIdentifier();
+        if (identifier != null) {
+            return identifier.getName();
+        }
+        return null;
+    }
+
+    @Nullable @Override public SmaliAnnotationElementName getNameIdentifier() {
+        return findChildByClass(SmaliAnnotationElementName.class);
+    }
+
+    @Nullable @Override public PsiAnnotationMemberValue getValue() {
+        ASTNode equalNode = findChildByType(SmaliTokens.EQUAL);
+        if (equalNode == null) {
+            return null;
+        }
+
+        PsiElement nextElement = equalNode.getPsi().getNextSibling();
+        while (nextElement != null) {
+            if (nextElement instanceof PsiAnnotationMemberValue) {
+                return (PsiAnnotationMemberValue)nextElement;
+            }
+            nextElement = nextElement.getNextSibling();
+        }
+        return null;
+    }
+
+    @NotNull @Override public PsiAnnotationMemberValue setValue(@NotNull PsiAnnotationMemberValue newValue) {
+        // TODO: implement this
+        throw new UnsupportedOperationException();
+    }
+
+    @Nullable @Override public String getLiteralValue() {
+        // Not applicable for smali
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationElementName.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationElementName.java
new file mode 100644
index 0000000..656b4ef
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationElementName.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.*;
+import com.intellij.psi.tree.IElementType;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliAnnotationElementName extends SmaliCompositeElement implements PsiIdentifier, PsiReference {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliAnnotationElementName();
+        }
+    };
+
+    public SmaliAnnotationElementName() {
+        super(SmaliElementTypes.ANNOTATION_ELEMENT_NAME);
+    }
+
+    @Override public IElementType getTokenType() {
+        return getElementType();
+    }
+
+    @Override public String getName() {
+        return getText();
+    }
+
+    @Nullable
+    public SmaliAnnotation getContainingAnnotation() {
+        return findAncestorByClass(SmaliAnnotation.class);
+    }
+
+    @Override public PsiElement bindToElement(PsiElement element) throws IncorrectOperationException {
+        //TODO: implement this if needed
+        throw new IncorrectOperationException();
+    }
+
+    @Override public PsiElement getElement() {
+        return this;
+    }
+
+    @Override public TextRange getRangeInElement() {
+        return new TextRange(0, getTextLength());
+    }
+
+    @Nullable @Override public PsiElement resolve() {
+        SmaliAnnotation smaliAnnotation = getContainingAnnotation();
+        if (smaliAnnotation == null) {
+            return null;
+        }
+
+        String annotationType = smaliAnnotation.getQualifiedName();
+        if (annotationType == null) {
+            return null;
+        }
+
+        JavaPsiFacade facade = JavaPsiFacade.getInstance(getProject());
+        PsiClass annotationClass = facade.findClass(annotationType, getResolveScope());
+        if (annotationClass == null) {
+            return null;
+        }
+
+        for (PsiMethod method : annotationClass.findMethodsByName(getName(), true)) {
+            if (method.getParameterList().getParametersCount() == 0) {
+                return method;
+            }
+        }
+        return null;
+    }
+
+    @NotNull @Override public String getCanonicalText() {
+        // TODO: return a full method reference here?
+        String name = getName();
+        if (name == null) {
+            return "";
+        }
+        return name;
+    }
+
+    @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+        //TODO: implement this
+        throw new IncorrectOperationException();
+    }
+
+    @Override public boolean isReferenceTo(PsiElement element) {
+        return resolve() == element;
+    }
+
+    @NotNull @Override public Object[] getVariants() {
+        return ArrayUtil.EMPTY_OBJECT_ARRAY;
+    }
+
+    @Override public boolean isSoft() {
+        return false;
+    }
+
+    @Override public PsiReference getReference() {
+        return this;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationParameterList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationParameterList.java
new file mode 100644
index 0000000..7feaa9e
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliAnnotationParameterList.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.PsiAnnotationParameterList;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliAnnotationParameterList extends SmaliCompositeElement implements PsiAnnotationParameterList {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliAnnotationParameterList();
+        }
+    };
+
+    public SmaliAnnotationParameterList() {
+        super(SmaliElementTypes.ANNOTATION_PARAMETER_LIST);
+    }
+
+    @NotNull @Override public SmaliAnnotationElement[] getAttributes() {
+        return findChildrenByClass(SmaliAnnotationElement.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliArrayDataElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliArrayDataElement.java
new file mode 100644
index 0000000..85f6213
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliArrayDataElement.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliArrayDataElement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliArrayDataElement();
+        }
+    };
+
+    public SmaliArrayDataElement() {
+        super(SmaliElementTypes.ARRAY_DATA_ELEMENT);
+    }
+
+    @Nullable
+    public SmaliLiteral getValue() {
+        return findChildByClass(SmaliLiteral.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliArrayTypeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliArrayTypeElement.java
new file mode 100644
index 0000000..1b54a9c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliArrayTypeElement.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiArrayType;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.PsiTypeElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliArrayTypeElement extends SmaliTypeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliArrayTypeElement();
+        }
+    };
+
+    public SmaliArrayTypeElement() {
+        super(SmaliElementTypes.ARRAY_TYPE);
+    }
+
+    @NotNull @Override public PsiType getType() {
+        ASTNode token = findChildByType(SmaliTokens.ARRAY_TYPE_PREFIX);
+        assert token != null;
+        PsiTypeElement baseType = findChildByClass(PsiTypeElement.class);
+        assert baseType != null;
+
+        PsiArrayType arrayType = new PsiArrayType(baseType.getType());
+        int dimensions = token.getTextLength() - 1;
+        while (dimensions > 0) {
+            arrayType = new PsiArrayType(arrayType);
+            dimensions--;
+        }
+        return arrayType;
+    }
+
+    @Nullable @Override public SmaliClassTypeElement getInnermostComponentReferenceElement() {
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliBaseReferenceList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliBaseReferenceList.java
new file mode 100644
index 0000000..b47d512
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliBaseReferenceList.java
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiReferenceList;
+import com.intellij.psi.StubBasedPsiElement;
+import com.intellij.psi.stubs.IStubElementType;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.stub.SmaliBaseReferenceListStub;
+import org.jf.smalidea.util.NameUtils;
+
+public abstract class SmaliBaseReferenceList<StubT extends SmaliBaseReferenceListStub>
+        extends SmaliStubBasedPsiElement<StubT> implements StubBasedPsiElement<StubT>, PsiReferenceList {
+    protected SmaliBaseReferenceList(@NotNull StubT stub, @NotNull IStubElementType nodeType) {
+        super(stub, nodeType);
+    }
+
+    protected SmaliBaseReferenceList(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @NotNull @Override public SmaliClassType[] getReferencedTypes() {
+        StubT stub = getStub();
+        if (stub != null) {
+            return stub.getReferencedTypes();
+        }
+
+        SmaliClassTypeElement[] references = getReferenceElements();
+
+        SmaliClassType[] referenceTypes = new SmaliClassType[references.length];
+
+        for (int i=0; i<references.length; i++) {
+            referenceTypes[i] = references[i].getType();
+        }
+        return referenceTypes;
+    }
+
+    @NotNull public String[] getReferenceNames() {
+        SmaliBaseReferenceListStub stub = getStub();
+
+        if (stub != null) {
+            String[] smaliNames = stub.getSmaliTypeNames();
+            String[] referenceNames = new String[smaliNames.length];
+
+            for (int i=0; i<smaliNames.length; i++) {
+                referenceNames[i] = NameUtils.resolveSmaliToJavaType(this, smaliNames[i]);
+            }
+
+            return referenceNames;
+        }
+
+        SmaliClassTypeElement[] references = getReferenceElements();
+
+        String[] referenceNames = new String[references.length];
+
+        for (int i=0; i<references.length; i++) {
+            referenceNames[i] = references[i].getCanonicalText();
+        }
+        return referenceNames;
+    }
+
+    @NotNull public String[] getSmaliNames() {
+        SmaliBaseReferenceListStub stub = getStub();
+
+        if (stub != null) {
+            return stub.getSmaliTypeNames();
+        }
+
+        SmaliClassTypeElement[] references = getReferenceElements();
+
+        String[] smaliNames = new String[references.length];
+
+        for (int i=0; i<references.length; i++) {
+            smaliNames[i] = references[i].getSmaliName();
+        }
+        return smaliNames;
+    }
+
+    @Override public boolean isWritable() {
+        return false;
+    }
+
+    @NotNull @Override public abstract SmaliClassTypeElement[] getReferenceElements();
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCatchAllStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCatchAllStatement.java
new file mode 100644
index 0000000..f5c7107
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCatchAllStatement.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliCatchAllStatement extends SmaliCatchStatement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliCatchAllStatement();
+        }
+    };
+
+    public SmaliCatchAllStatement() {
+        super(SmaliElementTypes.CATCH_ALL_STATEMENT);
+    }
+
+    @Nullable @Override public SmaliClassTypeElement getExceptionType() {
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCatchStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCatchStatement.java
new file mode 100644
index 0000000..b8442a6
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCatchStatement.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliCatchStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliCatchStatement();
+        }
+    };
+
+    public SmaliCatchStatement() {
+        super(SmaliElementTypes.CATCH_STATEMENT);
+    }
+
+    protected SmaliCatchStatement(IElementType elementType) {
+        super(elementType);
+    }
+
+    @Nullable
+    public SmaliClassTypeElement getExceptionType() {
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+
+    @Nullable
+    public SmaliLabelReference getStartLabel() {
+        return findChildByClass(SmaliLabelReference.class);
+    }
+
+    @Nullable
+    public SmaliLabelReference getEndLabel() {
+        SmaliLabelReference startLabel = getStartLabel();
+        if (startLabel == null) {
+            return null;
+        }
+        return startLabel.findNextSiblingByClass(SmaliLabelReference.class);
+    }
+
+    @Nullable
+    public SmaliLabelReference getHandlerLabel() {
+        SmaliLabelReference endLabel = getEndLabel();
+        if (endLabel == null) {
+            return null;
+        }
+        return endLabel.findNextSiblingByClass(SmaliLabelReference.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClass.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClass.java
new file mode 100644
index 0000000..684293f
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClass.java
@@ -0,0 +1,415 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.util.Pair;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.*;
+import com.intellij.psi.PsiModifier.ModifierConstant;
+import com.intellij.psi.impl.InheritanceImplUtil;
+import com.intellij.psi.impl.PsiClassImplUtil;
+import com.intellij.psi.impl.PsiImplUtil;
+import com.intellij.psi.javadoc.PsiDocComment;
+import com.intellij.psi.scope.PsiScopeProcessor;
+import com.intellij.psi.util.PsiUtil;
+import com.intellij.util.IncorrectOperationException;
+import com.sun.jdi.Location;
+import com.sun.jdi.Method;
+import com.sun.jdi.ReferenceType;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliIcons;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
+import org.jf.smalidea.psi.leaf.SmaliClassDescriptor;
+import org.jf.smalidea.psi.stub.SmaliClassStub;
+
+import javax.annotation.Nonnull;
+import javax.swing.*;
+import java.util.Collection;
+import java.util.List;
+
+public class SmaliClass extends SmaliStubBasedPsiElement<SmaliClassStub> implements PsiClass, SmaliModifierListOwner {
+    public SmaliClass(@NotNull SmaliClassStub stub) {
+        super(stub, SmaliElementTypes.CLASS);
+    }
+
+    public SmaliClass(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @Nonnull
+    @Override
+    public String getName() {
+        String name = getQualifiedName();
+        if (name == null) {
+            return "";
+        }
+        int lastDot = name.lastIndexOf('.');
+        if (lastDot < 0) {
+            return name;
+        }
+        return name.substring(lastDot+1);
+    }
+
+    @Nullable @Override public String getQualifiedName() {
+        SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
+        if (classStatement == null) {
+            return null;
+        }
+        return classStatement.getQualifiedName();
+    }
+
+    @NotNull public String getPackageName() {
+        String name = getQualifiedName();
+        if (name == null) {
+            return "";
+        }
+        int lastDot = name.lastIndexOf('.');
+        if (lastDot < 0) {
+            return "";
+        }
+        return name.substring(0, lastDot);
+    }
+
+    @Override public boolean hasTypeParameters() {
+        // TODO: implement generics
+        return false;
+    }
+
+    @Override public boolean isInterface() {
+        return hasModifierProperty("interface");
+    }
+
+    @Override public boolean isAnnotationType() {
+        return hasModifierProperty("annotation");
+    }
+
+    @Override public boolean isEnum() {
+        return hasModifierProperty("enum");
+    }
+
+    @Nullable public SmaliSuperStatement getSuperStatement() {
+        return findChildByClass(SmaliSuperStatement.class);
+    }
+
+    @NotNull @Override public SmaliExtendsList getExtendsList() {
+        return getRequiredStubOrPsiChild(SmaliElementTypes.EXTENDS_LIST);
+    }
+
+    @NotNull public SmaliImplementsStatement[] getImplementsStatements() {
+        return findChildrenByClass(SmaliImplementsStatement.class);
+    }
+
+    @NotNull @Override public SmaliImplementsList getImplementsList() {
+        return getRequiredStubOrPsiChild(SmaliElementTypes.IMPLEMENTS_LIST);
+    }
+
+    @NotNull @Override public SmaliClassType[] getExtendsListTypes() {
+        return getExtendsList().getReferencedTypes();
+    }
+
+    @NotNull @Override public SmaliClassType[] getImplementsListTypes() {
+        return getImplementsList().getReferencedTypes();
+    }
+
+    @Nullable @Override public PsiClass getSuperClass() {
+        return PsiClassImplUtil.getSuperClass(this);
+    }
+
+    @Override public PsiClass[] getInterfaces() {
+        return PsiClassImplUtil.getInterfaces(this);
+    }
+
+    @NotNull @Override public PsiClass[] getSupers() {
+        return PsiClassImplUtil.getSupers(this);
+    }
+
+    @NotNull @Override public PsiClassType[] getSuperTypes() {
+        return PsiClassImplUtil.getSuperTypes(this);
+    }
+
+    @NotNull @Override public SmaliField[] getFields() {
+        SmaliField[] fields = getStubOrPsiChildren(SmaliElementTypes.FIELD, new SmaliField[0]);
+        List<SmaliField> filteredFields = null;
+        for (int i=fields.length-1; i>=0; i--) {
+            SmaliField field = fields[i];
+            if (field.getName() == null) {
+                if (filteredFields == null) {
+                    filteredFields = Lists.newArrayList(fields);
+                }
+                filteredFields.remove(i);
+            }
+        }
+        if (filteredFields != null) {
+            return filteredFields.toArray(new SmaliField[filteredFields.size()]);
+        }
+        return fields;
+    }
+
+    @NotNull @Override public SmaliMethod[] getMethods() {
+        return getStubOrPsiChildren(SmaliElementTypes.METHOD, new SmaliMethod[0]);
+    }
+
+    @NotNull @Override public PsiMethod[] getConstructors() {
+        return PsiImplUtil.getConstructors(this);
+    }
+
+    @NotNull @Override public PsiClass[] getInnerClasses() {
+        return new PsiClass[0];
+    }
+
+    @NotNull @Override public PsiClassInitializer[] getInitializers() {
+        // TODO: do we need to return the <clinit> method here?
+        return new PsiClassInitializer[0];
+    }
+
+    @NotNull @Override public PsiField[] getAllFields() {
+        return PsiClassImplUtil.getAllFields(this);
+    }
+
+    @NotNull @Override public PsiMethod[] getAllMethods() {
+        return PsiClassImplUtil.getAllMethods(this);
+    }
+
+    @NotNull @Override public PsiClass[] getAllInnerClasses() {
+        return new PsiClass[0];
+    }
+
+    @Nullable @Override public PsiField findFieldByName(@NonNls String name, boolean checkBases) {
+        return PsiClassImplUtil.findFieldByName(this, name, checkBases);
+    }
+
+    @Nullable @Override public PsiMethod findMethodBySignature(PsiMethod patternMethod, boolean checkBases) {
+        return PsiClassImplUtil.findMethodBySignature(this, patternMethod, checkBases);
+    }
+
+    @NotNull @Override public PsiMethod[] findMethodsBySignature(PsiMethod patternMethod, boolean checkBases) {
+        return PsiClassImplUtil.findMethodsBySignature(this, patternMethod, checkBases);
+    }
+
+    @NotNull @Override public PsiMethod[] findMethodsByName(@NonNls String name, boolean checkBases) {
+        return PsiClassImplUtil.findMethodsByName(this, name, checkBases);
+    }
+
+    @NotNull @Override
+    public List<Pair<PsiMethod, PsiSubstitutor>> findMethodsAndTheirSubstitutorsByName(@NonNls String name, boolean checkBases) {
+        return PsiClassImplUtil.findMethodsAndTheirSubstitutorsByName(this, name, checkBases);
+    }
+
+    @NotNull @Override public List<Pair<PsiMethod, PsiSubstitutor>> getAllMethodsAndTheirSubstitutors() {
+        return PsiClassImplUtil.getAllWithSubstitutorsByMap(this, PsiClassImplUtil.MemberType.METHOD);
+    }
+
+    @Nullable @Override public PsiClass findInnerClassByName(@NonNls String name, boolean checkBases) {
+        return null;
+    }
+
+    @Nullable @Override public PsiElement getLBrace() {
+        return null;
+    }
+
+    @Nullable @Override public PsiElement getRBrace() {
+        return null;
+    }
+
+    @Nullable public SmaliClassStatement getClassStatement() {
+        return getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
+    }
+
+    @Nullable @Override public SmaliClassDescriptor getNameIdentifier() {
+        SmaliClassStatement classStatement = getClassStatement();
+        if (classStatement == null) {
+            return null;
+        }
+        return classStatement.getNameIdentifier();
+    }
+
+    @Override public PsiElement getScope() {
+        return null;
+    }
+
+    @Override public boolean isInheritor(@NotNull PsiClass baseClass, boolean checkDeep) {
+        return InheritanceImplUtil.isInheritor(this, baseClass, checkDeep);
+    }
+
+    @Override public boolean isInheritorDeep(PsiClass baseClass, @Nullable PsiClass classToByPass) {
+        return InheritanceImplUtil.isInheritorDeep(this, baseClass, classToByPass);
+    }
+
+    @Nullable @Override public PsiClass getContainingClass() {
+        return null;
+    }
+
+    @NotNull @Override public Collection<HierarchicalMethodSignature> getVisibleSignatures() {
+        return ImmutableList.of();
+    }
+
+    @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
+        SmaliClassStatement classStatement = getClassStatement();
+        if (classStatement == null) {
+            throw new IncorrectOperationException();
+        }
+
+        SmaliClassTypeElement classTypeElement = classStatement.getNameElement();
+        if (classTypeElement == null) {
+            throw new IncorrectOperationException();
+        }
+
+        String expectedPath = "/" + getName() + ".smali";
+
+        VirtualFile virtualFile = this.getContainingFile().getVirtualFile();
+        if (virtualFile != null) {
+            String actualPath = virtualFile.getPath();
+            if (actualPath.endsWith(expectedPath)) {
+                getContainingFile().setName(name + ".smali");
+            }
+        }
+
+        String packageName = this.getPackageName();
+        String newName;
+        if (packageName.length() > 0) {
+            newName = packageName + "." + name;
+        } else {
+            newName = name;
+        }
+        classTypeElement.handleElementRename(newName);
+        return this;
+    }
+
+    public void setPackageName(@NonNls @NotNull String packageName) {
+        SmaliClassStatement classStatement = getClassStatement();
+        if (classStatement == null) {
+            throw new IncorrectOperationException();
+        }
+
+        SmaliClassTypeElement classTypeElement = classStatement.getNameElement();
+        if (classTypeElement == null) {
+            throw new IncorrectOperationException();
+        }
+
+        String newName;
+        if (packageName.length() > 0) {
+            newName = packageName + "." + getName();
+        } else {
+            newName = getName();
+        }
+
+        classTypeElement.handleElementRename(newName);
+    }
+
+    @Nullable @Override public PsiDocComment getDocComment() {
+        return null;
+    }
+
+    @Override public boolean isDeprecated() {
+        return false;
+    }
+
+    @Nullable @Override public PsiTypeParameterList getTypeParameterList() {
+        return null;
+    }
+
+    @NotNull @Override public PsiTypeParameter[] getTypeParameters() {
+        return new PsiTypeParameter[0];
+    }
+
+    @Nullable @Override public SmaliModifierList getModifierList() {
+        SmaliClassStatement classStatement = getStubOrPsiChild(SmaliElementTypes.CLASS_STATEMENT);
+        if (classStatement == null) {
+            return null;
+        }
+        return classStatement.getModifierList();
+    }
+
+    @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
+        SmaliModifierList smaliModifierList = getModifierList();
+        return smaliModifierList != null && smaliModifierList.hasModifierProperty(name);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getAnnotations() {
+        return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
+        return getAnnotations();
+    }
+
+    @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        for (SmaliAnnotation annotation: getAnnotations()) {
+            if (qualifiedName.equals(annotation.getQualifiedName())) {
+                return annotation;
+            }
+        }
+        return null;
+    }
+
+    @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        // TODO: implement this
+        return null;
+    }
+
+    @Nullable public Location getLocationForSourcePosition(@Nonnull ReferenceType type,
+                                                           @Nonnull SourcePosition position) {
+
+        SmaliMethod[] smaliMethods = findChildrenByType(SmaliElementTypes.METHOD, SmaliMethod.class);
+
+        for (SmaliMethod smaliMethod: smaliMethods) {
+            //TODO: check the start line+end line of the method
+            int offset = smaliMethod.getOffsetForLine(position.getLine());
+            if (offset != -1) {
+                List<Method> methods = type.methodsByName(smaliMethod.getName(),
+                        smaliMethod.getMethodPrototype().getText());
+                if (methods.size() > 0) {
+                    return methods.get(0).locationOfCodeIndex(offset/2);
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public boolean processDeclarations(@NotNull PsiScopeProcessor processor, @NotNull ResolveState state,
+                                       PsiElement lastParent, @NotNull PsiElement place) {
+        return PsiClassImplUtil.processDeclarationsInClass(this, processor, state, null, lastParent, place,
+                PsiUtil.getLanguageLevel(place), false);
+    }
+
+    @Nullable @Override protected Icon getElementIcon(@IconFlags int flags) {
+        return SmaliIcons.SmaliIcon;
+    }
+}
\ No newline at end of file
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassStatement.java
new file mode 100644
index 0000000..add3c1b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassStatement.java
@@ -0,0 +1,146 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
+import org.jf.smalidea.psi.leaf.SmaliClassDescriptor;
+import org.jf.smalidea.psi.stub.SmaliClassStatementStub;
+import org.jf.smalidea.util.NameUtils;
+
+public class SmaliClassStatement extends SmaliStubBasedPsiElement<SmaliClassStatementStub>
+        implements SmaliModifierListOwner {
+    public SmaliClassStatement(@NotNull SmaliClassStatementStub stub) {
+        super(stub, SmaliElementTypes.CLASS_STATEMENT);
+    }
+
+    public SmaliClassStatement(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @Nullable
+    public SmaliClassTypeElement getNameElement() {
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+
+    @Nullable
+    public SmaliClass getContainingClass() {
+        return getStubOrPsiParentOfType(SmaliClass.class);
+    }
+
+    @Nullable
+    public SmaliClassDescriptor getNameIdentifier() {
+        SmaliClassTypeElement classTypeElement = getNameElement();
+        if (classTypeElement == null) {
+            return null;
+        }
+        return classTypeElement.getReferenceNameElement();
+    }
+
+    /**
+     * @return the fully qualified java-style name of the class in this .class statement
+     */
+    @Nullable
+    public String getQualifiedName() {
+        SmaliClassStatementStub stub = getStub();
+        if (stub != null) {
+            return stub.getQualifiedName();
+        }
+
+        SmaliClassTypeElement classType = findChildByClass(SmaliClassTypeElement.class);
+        if (classType == null) {
+            return null;
+        }
+        // Since this is a class declared in smali, we don't have to worry about handling inner classes,
+        // so we can do a pure textual translation of the class name
+        return NameUtils.smaliToJavaType(classType.getSmaliName());
+    }
+
+    @Nullable
+    public SmaliModifierList getModifierList() {
+        return getStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST);
+    }
+
+    @NotNull
+    @Override
+    public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        SmaliClass containingClass = getContainingClass();
+        if (containingClass == null) {
+            // TODO: what should we do here?
+            return null;
+        }
+        return containingClass.addAnnotation(qualifiedName);
+    }
+
+    @NotNull
+    @Override
+    public SmaliAnnotation[] getAnnotations() {
+        SmaliClass containingClass = getContainingClass();
+        if (containingClass == null) {
+            return new SmaliAnnotation[0];
+        }
+        return containingClass.getAnnotations();
+    }
+
+    @NotNull
+    @Override
+    public SmaliAnnotation[] getApplicableAnnotations() {
+        SmaliClass containingClass = getContainingClass();
+        if (containingClass == null) {
+            return new SmaliAnnotation[0];
+        }
+        return containingClass.getApplicableAnnotations();
+    }
+
+    @Nullable
+    @Override
+    public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        SmaliClass containingClass = getContainingClass();
+        if (containingClass == null) {
+            return null;
+        }
+        return containingClass.findAnnotation(qualifiedName);
+    }
+
+    @Override
+    public boolean hasModifierProperty(@NonNls @NotNull String name) {
+        SmaliClass containingClass = getContainingClass();
+        if (containingClass == null) {
+            return false;
+        }
+        return containingClass.hasModifierProperty(name);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassType.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassType.java
new file mode 100644
index 0000000..6d5bbab
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassType.java
@@ -0,0 +1,185 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.*;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.util.NameUtils;
+
+public class SmaliClassType extends PsiClassType {
+    private final PsiTypeElement element;
+
+    public SmaliClassType(PsiTypeElement element) {
+        this(element, LanguageLevel.JDK_1_5);
+    }
+
+    public SmaliClassType(PsiTypeElement element, LanguageLevel languageLevel) {
+        super(languageLevel);
+        this.element = element;
+    }
+
+    @Nullable
+    @Override
+    public PsiClass resolve() {
+        PsiReference reference = element.getReference();
+        if (reference == null) {
+            return null;
+        }
+        PsiElement resolved = reference.resolve();
+        if (resolved instanceof PsiClass) {
+            return (PsiClass)resolved;
+        }
+        return null;
+    }
+
+    @Override
+    public String getClassName() {
+        PsiClass resolved = resolve();
+        if (resolved != null) {
+            return NameUtils.shortNameFromQualifiedName(resolved.getQualifiedName());
+        }
+        return NameUtils.shortNameFromQualifiedName(element.getText());
+    }
+
+    @NotNull
+    @Override
+    public PsiType[] getParameters() {
+        // TODO: (generics) implement this
+        return PsiType.EMPTY_ARRAY;
+    }
+
+    @NotNull
+    @Override
+    public ClassResolveResult resolveGenerics() {
+        // TODO: (generics) implement this
+        return new ClassResolveResult() {
+            @Override
+            public PsiClass getElement() {
+                return resolve();
+            }
+
+            @Override
+            public PsiSubstitutor getSubstitutor() {
+                return PsiSubstitutor.EMPTY;
+            }
+
+            @Override
+            public boolean isPackagePrefixPackageReference() {
+                return false;
+            }
+
+            @Override
+            public boolean isAccessible() {
+                return true;
+            }
+
+            @Override
+            public boolean isStaticsScopeCorrect() {
+                return true;
+            }
+
+            @Override
+            public PsiElement getCurrentFileResolveScope() {
+                return null;
+            }
+
+            @Override
+            public boolean isValidResult() {
+                return true;
+            }
+        };
+    }
+
+    @NotNull
+    @Override
+    public SmaliClassType rawType() {
+        // TODO: (generics) implement this
+        return this;
+    }
+
+    @Override
+    @NotNull
+    public String getPresentableText() {
+        return getCanonicalText();
+    }
+
+    @Override
+    @NotNull
+    public String getCanonicalText() {
+        PsiClass psiClass = resolve();
+        if (psiClass != null) {
+            String qualifiedName = psiClass.getQualifiedName();
+            if (qualifiedName != null) {
+                return qualifiedName;
+            }
+        }
+        return NameUtils.smaliToJavaType(element.getText());
+    }
+
+    @Override
+    @NotNull
+    public String getInternalCanonicalText() {
+        return getCanonicalText();
+    }
+
+    @Override
+    public boolean isValid() {
+        return element.isValid();
+    }
+
+    @Override
+    public boolean equalsToText(@NonNls String text) {
+        return text.equals(getCanonicalText());
+    }
+
+    @NotNull
+    @Override
+    public GlobalSearchScope getResolveScope() {
+        return element.getResolveScope();
+    }
+
+    @NotNull
+    @Override
+    public LanguageLevel getLanguageLevel() {
+        return myLanguageLevel;
+    }
+
+    @NotNull
+    @Override
+    public PsiClassType setLanguageLevel(@NotNull LanguageLevel languageLevel) {
+        return new SmaliClassType(element, languageLevel);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassTypeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassTypeElement.java
new file mode 100644
index 0000000..b491f6d
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliClassTypeElement.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
+import com.intellij.psi.infos.CandidateInfo;
+import com.intellij.psi.scope.PsiScopeProcessor;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.leaf.SmaliClassDescriptor;
+import org.jf.smalidea.util.NameUtils;
+
+public class SmaliClassTypeElement extends SmaliTypeElement implements PsiJavaCodeReferenceElement {
+    public static final SmaliClassTypeElement[] EMPTY_ARRAY = new SmaliClassTypeElement[0];
+
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliClassTypeElement();
+        }
+    };
+
+    @Nullable private SmaliClassType classType = null;
+
+    public SmaliClassTypeElement() {
+        super(SmaliElementTypes.CLASS_TYPE);
+    }
+
+    @NotNull @Override public SmaliClassType getType() {
+        if (classType == null) {
+            classType = new SmaliClassType(this);
+        }
+        return classType;
+    }
+
+    @Override public String getName() {
+        return NameUtils.shortNameFromQualifiedName(getCanonicalText());
+    }
+
+    @Nullable @Override public SmaliClassTypeElement getInnermostComponentReferenceElement() {
+        return this;
+    }
+
+    @Override public PsiElement getElement() {
+        return this;
+    }
+
+    @Override public PsiReference getReference() {
+        return this;
+    }
+
+    @Override public TextRange getRangeInElement() {
+        return new TextRange(0, getTextLength());
+    }
+
+    @Nullable @Override public PsiClass resolve() {
+        return NameUtils.resolveSmaliType(this, getText());
+    }
+
+    @NotNull @Override public String getCanonicalText() {
+        return getQualifiedName();
+    }
+
+    @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+        SmaliClassDescriptor descriptor = getReferenceNameElement();
+        if (descriptor == null) {
+            throw new IncorrectOperationException();
+        }
+
+        SmaliClassDescriptor newDescriptor = new SmaliClassDescriptor(NameUtils.javaToSmaliType(newElementName));
+        CodeEditUtil.setNodeGenerated(newDescriptor, true);
+
+        this.replaceChild(descriptor, newDescriptor);
+        return this;
+    }
+
+    @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+        if (element instanceof PsiClass) {
+            handleElementRename(((PsiClass) element).getQualifiedName());
+            return this;
+        }
+        throw new IncorrectOperationException();
+    }
+
+    @Override public boolean isReferenceTo(PsiElement element) {
+        if (!(element instanceof PsiClass)) {
+            return false;
+        }
+        return element.getManager().areElementsEquivalent(element, resolve());
+    }
+
+    @NotNull @Override public Object[] getVariants() {
+        // TODO: implement this?
+        return new Object[0];
+    }
+
+    @Override public boolean isSoft() {
+        return false;
+    }
+
+    // ***************************************************************************
+    // Below are the PsiJavaCodeReferenceElement-specific methods
+
+    @Override public void processVariants(@NotNull PsiScopeProcessor processor) {
+        // TODO: maybe just do nothing?
+        throw new UnsupportedOperationException();
+    }
+
+    @Nullable @Override public SmaliClassDescriptor getReferenceNameElement() {
+        return findChildByClass(SmaliClassDescriptor.class);
+    }
+
+    @Nullable @Override public PsiReferenceParameterList getParameterList() {
+        // TODO: (generics) implement this
+        return null;
+    }
+
+    @NotNull @Override public PsiType[] getTypeParameters() {
+        // TODO: (generics) implement this
+        return new PsiType[0];
+    }
+
+    @Override public boolean isQualified() {
+        // TODO: should this return false for classes in the top level package?
+        return true;
+    }
+
+    @Override public String getQualifiedName() {
+        PsiClass psiClass = resolve();
+        if (psiClass != null) {
+            return psiClass.getQualifiedName();
+        }
+        return NameUtils.smaliToJavaType(getText());
+    }
+
+    @NotNull @Override public JavaResolveResult advancedResolve(boolean incompleteCode) {
+        PsiClass element = resolve();
+        if (element == null) {
+            return JavaResolveResult.EMPTY;
+        }
+        return new CandidateInfo(element, PsiSubstitutor.EMPTY);
+    }
+
+    @NotNull @Override public JavaResolveResult[] multiResolve(boolean incompleteCode) {
+        PsiClass element = resolve();
+        if (element == null) {
+            return JavaResolveResult.EMPTY_ARRAY;
+        }
+        return new CandidateInfo[] { new CandidateInfo(element, PsiSubstitutor.EMPTY) };
+    }
+
+    @Nullable @Override public PsiElement getQualifier() {
+        return null;
+    }
+
+    @Nullable @Override public String getReferenceName() {
+        return getName();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCompositeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCompositeElement.java
new file mode 100644
index 0000000..d9c38c4
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliCompositeElement.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.google.common.collect.ImmutableList;
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.impl.source.tree.CompositePsiElement;
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.lang.reflect.Array;
+import java.util.ArrayList;
+import java.util.List;
+
+public abstract class SmaliCompositeElement extends CompositePsiElement {
+    public SmaliCompositeElement(IElementType type) {
+        super(type);
+    }
+
+    @NotNull
+    @SuppressWarnings("unchecked")
+    protected List<ASTNode> findChildrenByType(IElementType elementType) {
+        List<ASTNode> result = ImmutableList.of();
+        ASTNode child = getNode().getFirstChildNode();
+        while (child != null) {
+            if (elementType == child.getElementType()) {
+                if (result.size() == 0) {
+                    result = new ArrayList<ASTNode>();
+                }
+                result.add((ASTNode)child.getPsi());
+            }
+            child = child.getTreeNext();
+        }
+        return result;
+    }
+
+    @NotNull
+    @SuppressWarnings("unchecked")
+    protected <T> T[] findChildrenByClass(Class<T> aClass) {
+        List<T> result = new ArrayList<T>();
+        for (PsiElement cur = getFirstChild(); cur != null; cur = cur.getNextSibling()) {
+            if (aClass.isInstance(cur)) result.add((T)cur);
+        }
+        return result.toArray((T[]) Array.newInstance(aClass, result.size()));
+    }
+
+    @Nullable
+    @SuppressWarnings("unchecked")
+    protected <T> T findChildByClass(Class<T> aClass) {
+        for (PsiElement cur = getFirstChild(); cur != null; cur = cur.getNextSibling()) {
+            if (aClass.isInstance(cur)) return (T)cur;
+        }
+        return null;
+    }
+
+    @Nullable
+    @SuppressWarnings("unchecked")
+    protected <T> T findAncestorByClass(Class<T> aClass) {
+        PsiElement parent = getParent();
+        while (parent != null) {
+            if (aClass.isInstance(parent)) {
+                return (T)parent;
+            }
+            parent = parent.getParent();
+        }
+        return null;
+    }
+
+    @Nullable
+    @SuppressWarnings("unchecked")
+    public <T> T findNextSiblingByClass(@NotNull Class<T> cls) {
+        PsiElement prev = getNextSibling();
+        while (true) {
+            if (prev == null) {
+                return null;
+            } else if (cls.isInstance(prev)) {
+                return (T)prev;
+            }
+            prev = prev.getNextSibling();
+        }
+    }
+
+    @Nullable
+    @SuppressWarnings("unchecked")
+    public <T> T findPrevSiblingByClass(@NotNull Class<T> cls) {
+        PsiElement prev = getPrevSibling();
+        while (true) {
+            if (prev == null) {
+                return null;
+            } else if (cls.isInstance(prev)) {
+                return (T)prev;
+            }
+            prev = prev.getPrevSibling();
+        }
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliEndLocalDebugStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliEndLocalDebugStatement.java
new file mode 100644
index 0000000..042596c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliEndLocalDebugStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliEndLocalDebugStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliEndLocalDebugStatement();
+        }
+    };
+
+    public SmaliEndLocalDebugStatement() {
+        super(SmaliElementTypes.END_LOCAL_DEBUG_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliEpilogueDebugStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliEpilogueDebugStatement.java
new file mode 100644
index 0000000..faaef09
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliEpilogueDebugStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliEpilogueDebugStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliEpilogueDebugStatement();
+        }
+    };
+
+    public SmaliEpilogueDebugStatement() {
+        super(SmaliElementTypes.EPILOGUE_DEBUG_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliExtendsList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliExtendsList.java
new file mode 100644
index 0000000..ace9a97
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliExtendsList.java
@@ -0,0 +1,96 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.google.common.collect.Lists;
+import com.intellij.lang.ASTNode;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.stub.SmaliExtendsListStub;
+
+import java.util.List;
+
+public class SmaliExtendsList extends SmaliBaseReferenceList<SmaliExtendsListStub> {
+    public SmaliExtendsList(@NotNull SmaliExtendsListStub stub) {
+        super(stub, SmaliElementTypes.EXTENDS_LIST);
+    }
+
+    public SmaliExtendsList(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @NotNull @Override public SmaliClassTypeElement[] getReferenceElements() {
+        if (((SmaliClass)getParent()).isInterface()) {
+            return getImplementsElements();
+        } else {
+            return getExtendsElement();
+        }
+    }
+
+    @NotNull private SmaliClassTypeElement[] getImplementsElements() {
+        SmaliClass smaliClass = getStubOrPsiParentOfType(SmaliClass.class);
+        assert smaliClass != null;
+
+        SmaliImplementsStatement[] implementsStatements = smaliClass.getImplementsStatements();
+        if (implementsStatements.length > 0) {
+            // all implemented interfaces go in the extends list for an interface
+            List<SmaliClassTypeElement> types = Lists.newArrayList();
+
+            for (SmaliImplementsStatement implementsStatement: implementsStatements) {
+                SmaliClassTypeElement classReference = implementsStatement.getClassReference();
+                if (classReference != null) {
+                    types.add(classReference);
+                }
+            }
+            return types.toArray(new SmaliClassTypeElement[types.size()]);
+        }
+        return new SmaliClassTypeElement[0];
+    }
+
+    @NotNull private SmaliClassTypeElement[] getExtendsElement() {
+        SmaliClass smaliClass = getStubOrPsiParentOfType(SmaliClass.class);
+        assert smaliClass != null;
+
+        SmaliSuperStatement superStatement = smaliClass.getSuperStatement();
+        if (superStatement != null) {
+            SmaliClassTypeElement classReference = superStatement.getClassReference();
+            if (classReference != null) {
+                return new SmaliClassTypeElement[] { classReference };
+            }
+        }
+        return new SmaliClassTypeElement[0];
+    }
+
+    @Override public Role getRole() {
+        return Role.EXTENDS_LIST;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliField.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliField.java
new file mode 100644
index 0000000..7bef4e9
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliField.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.*;
+import com.intellij.psi.PsiModifier.ModifierConstant;
+import com.intellij.psi.impl.PsiImplUtil;
+import com.intellij.psi.javadoc.PsiDocComment;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
+import org.jf.smalidea.psi.stub.SmaliFieldStub;
+import org.jf.smalidea.util.NameUtils;
+
+public class SmaliField extends SmaliStubBasedPsiElement<SmaliFieldStub> implements PsiField, SmaliModifierListOwner {
+    public SmaliField(@NotNull SmaliFieldStub stub) {
+        super(stub, SmaliElementTypes.FIELD);
+    }
+
+    public SmaliField(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @Nullable @Override public String getName() {
+        SmaliFieldStub stub = getStub();
+        if (stub != null) {
+            return stub.getName();
+        }
+
+        SmaliMemberName smaliMemberName = findChildByClass(SmaliMemberName.class);
+        if (smaliMemberName == null || smaliMemberName.getText().isEmpty()) {
+            return null;
+        }
+        return smaliMemberName.getText();
+    }
+
+    @NotNull @Override public SmaliModifierList getModifierList() {
+        SmaliModifierList modifierList = getStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST);
+        assert modifierList != null;
+        return modifierList;
+    }
+
+    @NotNull @Override public SmaliMemberName getNameIdentifier() {
+        SmaliMemberName memberName = findChildByClass(SmaliMemberName.class);
+        assert memberName != null;
+        return memberName;
+    }
+
+    @Nullable @Override public PsiDocComment getDocComment() {
+        return null;
+    }
+
+    @Override public boolean isDeprecated() {
+        return PsiImplUtil.isDeprecatedByAnnotation(this);
+    }
+
+    @Nullable @Override public PsiClass getContainingClass() {
+        return (PsiClass)getStubOrPsiParent();
+    }
+
+    @NotNull @Override public PsiType getType() {
+        SmaliFieldStub stub = getStub();
+        if (stub != null) {
+            return NameUtils.resolveSmaliToPsiType(this, stub.getSmaliTypeName());
+        }
+        PsiTypeElement typeElement = getTypeElement();
+        if (typeElement == null) {
+            // If we don't have a type (i.e. syntax error), use Object as a safe-ish fallback
+            PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
+            return factory.createTypeFromText("java.lang.Object", this);
+        }
+        return getTypeElement().getType();
+    }
+
+    @Nullable @Override public SmaliTypeElement getTypeElement() {
+        return findChildByClass(SmaliTypeElement.class);
+    }
+
+    @Nullable @Override public PsiExpression getInitializer() {
+        // TODO: implement this
+        return null;
+    }
+
+    @Override public boolean hasInitializer() {
+        // TODO: implement this
+        return false;
+    }
+
+    @Override public void normalizeDeclaration() throws IncorrectOperationException {
+        // not applicable
+    }
+
+    @Nullable @Override public Object computeConstantValue() {
+        // TODO: implement this
+        return null;
+    }
+
+    @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
+        SmaliMemberName smaliMemberName = getNameIdentifier();
+        smaliMemberName.setName(name);
+        return this;
+    }
+
+    @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
+        return getModifierList().hasModifierProperty(name);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getAnnotations() {
+        return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
+        return getAnnotations();
+    }
+
+    @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        for (SmaliAnnotation annotation: getAnnotations()) {
+            if (qualifiedName.equals(annotation.getQualifiedName())) {
+                return annotation;
+            }
+        }
+        return null;
+    }
+
+    @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        // TODO: implement this
+        return null;
+    }
+
+    @Override public void setInitializer(@Nullable PsiExpression initializer) throws IncorrectOperationException {
+        // TODO: implement this
+    }
+
+    @Override public int getTextOffset() {
+        SmaliMemberName smaliMemberName = getNameIdentifier();
+        if (smaliMemberName != null) {
+            return smaliMemberName.getTextOffset();
+        }
+        return super.getTextOffset();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFieldInitializer.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFieldInitializer.java
new file mode 100644
index 0000000..dcf0e85
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFieldInitializer.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliFieldInitializer extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliFieldInitializer();
+        }
+    };
+
+    public SmaliFieldInitializer() {
+        super(SmaliElementTypes.FIELD_INITIALIZER);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFieldReference.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFieldReference.java
new file mode 100644
index 0000000..0626c3a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFieldReference.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiReference;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliFieldReference extends SmaliCompositeElement implements PsiReference {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliFieldReference();
+        }
+    };
+
+    public SmaliFieldReference() {
+        super(SmaliElementTypes.FIELD_REFERENCE);
+    }
+
+    @Nullable
+    public PsiClass getContainingClass() {
+        SmaliClassTypeElement containingClassReference = getContainingType();
+        if (containingClassReference == null) {
+            return null;
+        }
+        PsiClass containingClass = containingClassReference.resolve();
+        if (containingClass == null) {
+            return null;
+        }
+
+        return containingClass;
+    }
+
+    @Nullable
+    public SmaliClassTypeElement getContainingType() {
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+
+    @Nullable
+    public SmaliMemberName getMemberName() {
+        return findChildByClass(SmaliMemberName.class);
+    }
+
+    @Nullable
+    public SmaliTypeElement getFieldType() {
+        SmaliTypeElement[] types = findChildrenByClass(SmaliTypeElement.class);
+        assert types.length == 2;
+        return types[1];
+    }
+
+    @Override public PsiReference getReference() {
+        return this;
+    }
+
+    @Override public String getName() {
+        SmaliMemberName memberName = getMemberName();
+        if (memberName == null) {
+            return null;
+        }
+        return memberName.getText();
+    }
+
+    @Override public PsiElement getElement() {
+        return this;
+    }
+
+    @Override public TextRange getRangeInElement() {
+        return new TextRange(0, getTextLength());
+    }
+
+    @NotNull @Override public String getCanonicalText() {
+        return getText();
+    }
+
+    @Nullable @Override public PsiField resolve() {
+        PsiClass containingClass = getContainingClass();
+        if (containingClass == null) {
+            return null;
+        }
+
+        SmaliMemberName memberName = getMemberName();
+        if (memberName == null) {
+            return null;
+        }
+
+        return containingClass.findFieldByName(memberName.getText(), true);
+    }
+
+    @Override public boolean isReferenceTo(PsiElement element) {
+        return resolve() == element;
+    }
+
+    @NotNull @Override public Object[] getVariants() {
+        return ArrayUtil.EMPTY_OBJECT_ARRAY;
+    }
+
+    @Override public boolean isSoft() {
+        return false;
+    }
+
+    @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+        //TODO: implement this
+        throw new IncorrectOperationException();
+    }
+
+    @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+        SmaliMemberName memberName = getMemberName();
+        if (memberName == null) {
+            throw new IncorrectOperationException();
+        }
+        memberName.setName(newElementName);
+        return this;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFile.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFile.java
new file mode 100644
index 0000000..6ec3bd5
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliFile.java
@@ -0,0 +1,94 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.extapi.psi.PsiFileBase;
+import com.intellij.psi.FileViewProvider;
+import com.intellij.psi.PsiClassOwner;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliFileType;
+import org.jf.smalidea.SmaliLanguage;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliFile extends PsiFileBase implements PsiClassOwner {
+    public SmaliFile(FileViewProvider viewProvider) {
+        super(viewProvider, SmaliLanguage.INSTANCE);
+    }
+
+    @NotNull @Override public SmaliFileType getFileType() {
+        return SmaliFileType.INSTANCE;
+    }
+
+    @Nullable
+    public SmaliClass getPsiClass() {
+        StubElement<? extends PsiElement> stub = (StubElement<? extends PsiElement>)getStub();
+        if (stub != null) {
+            StubElement<SmaliClass> classElement = stub.findChildStubByType(SmaliElementTypes.CLASS);
+            if (classElement != null) {
+                return classElement.getPsi();
+            } else {
+                return null;
+            }
+        } else {
+            return findChildByClass(SmaliClass.class);
+        }
+    }
+
+    @NotNull @Override public SmaliClass[] getClasses() {
+        SmaliClass smaliClass = getPsiClass();
+        if (smaliClass == null) {
+            return new SmaliClass[] {};
+        } else {
+            return new SmaliClass[] { smaliClass };
+        }
+    }
+
+    @NotNull @Override public String getPackageName() {
+        SmaliClass smaliClass = getPsiClass();
+        if (smaliClass == null) {
+            return "";
+        }
+        return smaliClass.getPackageName();
+    }
+
+    @Override public void setPackageName(String packageName) throws IncorrectOperationException {
+        SmaliClass smaliClass = getPsiClass();
+        if (smaliClass == null) {
+            return;
+        }
+        smaliClass.setPackageName(packageName);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliImplementsList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliImplementsList.java
new file mode 100644
index 0000000..8992ab0
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliImplementsList.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.google.common.collect.Lists;
+import com.intellij.lang.ASTNode;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.stub.SmaliImplementsListStub;
+
+import java.util.List;
+
+public class SmaliImplementsList extends SmaliBaseReferenceList<SmaliImplementsListStub> {
+    public SmaliImplementsList(@NotNull SmaliImplementsListStub stub) {
+        super(stub, SmaliElementTypes.IMPLEMENTS_LIST);
+    }
+
+    public SmaliImplementsList(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @NotNull @Override public SmaliClassTypeElement[] getReferenceElements() {
+        if (!((SmaliClass)getParent()).isInterface()) {
+            return getImplementsElements();
+        }
+        return new SmaliClassTypeElement[0];
+    }
+
+    @NotNull private SmaliClassTypeElement[] getImplementsElements() {
+        SmaliClass smaliClass = (SmaliClass)getStubOrPsiParent();
+        assert smaliClass != null;
+
+        SmaliImplementsStatement[] implementsStatements = smaliClass.getImplementsStatements();
+        if (implementsStatements.length > 0) {
+            // all implemented interfaces go in the extends list for an interface
+            List<SmaliClassTypeElement> types = Lists.newArrayList();
+
+            for (SmaliImplementsStatement implementsStatement: implementsStatements) {
+                SmaliClassTypeElement classReference = implementsStatement.getClassReference();
+                if (classReference != null) {
+                    types.add(classReference);
+                }
+            }
+            return types.toArray(new SmaliClassTypeElement[types.size()]);
+        }
+        return new SmaliClassTypeElement[0];
+    }
+
+    @Override public Role getRole() {
+        return Role.IMPLEMENTS_LIST;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliImplementsStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliImplementsStatement.java
new file mode 100644
index 0000000..e50d8c6
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliImplementsStatement.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliImplementsStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliImplementsStatement();
+        }
+    };
+
+    public SmaliImplementsStatement() {
+        super(SmaliElementTypes.IMPLEMENTS_STATEMENT);
+    }
+
+    @Nullable
+    public SmaliClassTypeElement getClassReference() {
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliInstruction.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliInstruction.java
new file mode 100644
index 0000000..8cb2d77
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliInstruction.java
@@ -0,0 +1,231 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.google.common.base.Preconditions;
+import com.intellij.lang.ASTNode;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.dexlib2.Format;
+import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.analysis.AnalyzedInstruction;
+import org.jf.dexlib2.analysis.MethodAnalyzer;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+import java.util.Arrays;
+import java.util.List;
+
+public class SmaliInstruction extends SmaliCompositeElement {
+    private static final int NO_OFFSET = -1;
+
+    @Nullable private Opcode opcode;
+    private int offset = NO_OFFSET;
+
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliInstruction();
+        }
+    };
+
+    public SmaliInstruction() {
+        super(SmaliElementTypes.INSTRUCTION);
+    }
+
+    @NotNull public SmaliMethod getParentMethod() {
+        SmaliMethod smaliMethod = findAncestorByClass(SmaliMethod.class);
+        assert smaliMethod != null;
+        return smaliMethod;
+    }
+
+    @NotNull public Opcode getOpcode() {
+        if (opcode == null) {
+            ASTNode instructionNode = findChildByType(SmaliTokens.INSTRUCTION_TOKENS);
+            // this should be impossible, based on the parser definition
+            assert instructionNode != null;
+
+            // TODO: put a project level Opcodes instance with the appropriate api level somewhere
+            opcode = new Opcodes(15, false).getOpcodeByName(instructionNode.getText());
+            if (opcode == null) {
+                if (instructionNode.getText().equals(".packed-switch")) {
+                    return Opcode.PACKED_SWITCH_PAYLOAD;
+                }
+                if (instructionNode.getText().equals(".sparse-switch")) {
+                    return Opcode.SPARSE_SWITCH_PAYLOAD;
+                }
+                if (instructionNode.getText().equals(".array-data")) {
+                    return Opcode.ARRAY_PAYLOAD;
+                }
+                assert false;
+            }
+        }
+        return opcode;
+    }
+
+    public int getOffset() {
+        // TODO: don't calculate this recursively. ugh!
+        if (offset == NO_OFFSET) {
+            SmaliInstruction previousInstruction = findPrevSiblingByClass(SmaliInstruction.class);
+            if (previousInstruction == null) {
+                offset = 0;
+            } else {
+                offset = previousInstruction.getOffset() + previousInstruction.getInstructionSize();
+            }
+        }
+        return offset;
+    }
+
+    public int getRegister(int registerIndex) {
+        Preconditions.checkArgument(registerIndex >= 0);
+
+        List<ASTNode> registers = findChildrenByType(SmaliElementTypes.REGISTER_REFERENCE);
+        if (registerIndex >= registers.size()) {
+            return -1;
+        }
+
+        SmaliRegisterReference registerReference = (SmaliRegisterReference)registers.get(registerIndex);
+        return registerReference.getRegisterNumber();
+    }
+
+    @Nullable
+    public SmaliLabelReference getTarget() {
+        return findChildByClass(SmaliLabelReference.class);
+    }
+
+    public int getRegisterCount() {
+        return findChildrenByType(SmaliElementTypes.REGISTER_REFERENCE).size();
+    }
+
+    @Nullable
+    public SmaliLiteral getLiteral() {
+        return findChildByClass(SmaliLiteral.class);
+    }
+
+    @Nullable
+    public SmaliTypeElement getTypeReference() {
+        return findChildByClass(SmaliTypeElement.class);
+    }
+
+    @Nullable
+    public SmaliFieldReference getFieldReference() {
+        return findChildByClass(SmaliFieldReference.class);
+    }
+
+    @Nullable
+    public SmaliMethodReference getMethodReference() {
+        return findChildByClass(SmaliMethodReference.class);
+    }
+
+    @Nullable
+    public SmaliLiteral getPackedSwitchStartKey() {
+        return findChildByClass(SmaliLiteral.class);
+    }
+
+    @NotNull
+    public List<SmaliPackedSwitchElement> getPackedSwitchElements() {
+        return Arrays.asList(findChildrenByClass(SmaliPackedSwitchElement.class));
+    }
+
+    @NotNull
+    public List<SmaliSparseSwitchElement> getSparseSwitchElements() {
+        return Arrays.asList(findChildrenByClass(SmaliSparseSwitchElement.class));
+    }
+
+    @Nullable
+    public SmaliLiteral getArrayDataWidth() {
+        return findChildByClass(SmaliLiteral.class);
+    }
+
+    @NotNull
+    public List<SmaliArrayDataElement> getArrayDataElements() {
+        return Arrays.asList(findChildrenByClass(SmaliArrayDataElement.class));
+    }
+
+    public int getInstructionSize() {
+        Opcode opcode = getOpcode();
+        if (!opcode.format.isPayloadFormat) {
+            return opcode.format.size;
+        } else if (opcode.format == Format.ArrayPayload) {
+            int elementWidth = (int)getArrayDataWidth().getIntegralValue();
+            int elementCount = getArrayDataElements().size();
+
+            return 8 + (elementWidth * elementCount + 1);
+        } else if (opcode.format == Format.PackedSwitchPayload) {
+            return 8 + getPackedSwitchElements().size() * 4;
+        } else if (opcode.format == Format.SparseSwitchPayload) {
+            return 2 + getSparseSwitchElements().size() * 4;
+        }
+        assert false;
+        throw new RuntimeException();
+    }
+
+    private AnalyzedInstruction analyzedInstruction = null;
+
+    @Nullable
+    private AnalyzedInstruction getAnalyzedInstructionFromMethod() {
+        SmaliMethod method = getParentMethod();
+
+        MethodAnalyzer analyzer = method.getMethodAnalyzer();
+        if (analyzer == null) {
+            return null;
+        }
+
+        int thisOffset = this.getOffset() / 2;
+        int codeOffset = 0;
+
+        for (AnalyzedInstruction instruction: analyzer.getAnalyzedInstructions()) {
+            if (codeOffset == thisOffset) {
+                return instruction;
+            }
+            assert codeOffset < thisOffset;
+
+            codeOffset += instruction.getOriginalInstruction().getCodeUnits();
+        }
+        assert false;
+        return null;
+    }
+
+    @Nullable
+    public AnalyzedInstruction getAnalyzedInstruction() {
+        if (analyzedInstruction == null) {
+            analyzedInstruction = getAnalyzedInstructionFromMethod();
+        }
+        return analyzedInstruction;
+    }
+
+    @Override public void clearCaches() {
+        super.clearCaches();
+        analyzedInstruction = null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLabel.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLabel.java
new file mode 100644
index 0000000..b07220d
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLabel.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliLabel extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliLabel();
+        }
+    };
+
+    public SmaliLabel() {
+        super(SmaliElementTypes.LABEL);
+    }
+
+    @Override public String getName() {
+        return getText().substring(1);
+    }
+
+    @Nullable
+    public SmaliInstruction getInstruction() {
+        return findNextSiblingByClass(SmaliInstruction.class);
+    }
+
+    @Nullable
+    private SmaliInstruction getPreviousInstruction() {
+        return findPrevSiblingByClass(SmaliInstruction.class);
+    }
+
+    public int getOffset() {
+        SmaliInstruction instruction = getInstruction();
+        if (instruction == null) {
+            instruction = getPreviousInstruction();
+            if (instruction == null) {
+                return 0;
+            }
+            // TODO: handle variable size instructions
+            return instruction.getOffset() + instruction.getOpcode().format.size;
+        }
+        return instruction.getOffset();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLabelReference.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLabelReference.java
new file mode 100644
index 0000000..940f429
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLabelReference.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliLabelReference extends SmaliCompositeElement implements PsiReference {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliLabelReference();
+        }
+    };
+
+    public SmaliLabelReference() {
+        super(SmaliElementTypes.LABEL_REFERENCE);
+    }
+
+    @Override public String getName() {
+        return getText().substring(1);
+    }
+
+    @Override public PsiReference getReference() {
+        return this;
+    }
+
+    @Override public PsiElement getElement() {
+        return this;
+    }
+
+    @Override public TextRange getRangeInElement() {
+        return new TextRange(0, getTextLength());
+    }
+
+    @Nullable @Override public SmaliLabel resolve() {
+        SmaliMethod method = findAncestorByClass(SmaliMethod.class);
+        if (method == null) {
+            return null;
+        }
+        return method.getLabel(getText());
+    }
+
+    @NotNull @Override public String getCanonicalText() {
+        return getText();
+    }
+
+    @Override public boolean isReferenceTo(PsiElement element) {
+        return resolve() == element;
+    }
+
+    @NotNull @Override public Object[] getVariants() {
+        return ArrayUtil.EMPTY_OBJECT_ARRAY;
+    }
+
+    @Override public boolean isSoft() {
+        return false;
+    }
+
+    @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+        //TODO: implement this
+        throw new IncorrectOperationException();
+    }
+
+    @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+        //TODO: implement this
+        throw new IncorrectOperationException();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLineDebugStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLineDebugStatement.java
new file mode 100644
index 0000000..7ea3227
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLineDebugStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliLineDebugStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliLineDebugStatement();
+        }
+    };
+
+    public SmaliLineDebugStatement() {
+        super(SmaliElementTypes.LINE_DEBUG_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLiteral.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLiteral.java
new file mode 100644
index 0000000..0a9f538
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLiteral.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiAnnotationMemberValue;
+import com.intellij.psi.tree.IElementType;
+import org.jf.smali.LiteralTools;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliLiteral extends SmaliCompositeElement implements PsiAnnotationMemberValue {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliLiteral();
+        }
+    };
+
+    public SmaliLiteral() {
+        super(SmaliElementTypes.LITERAL);
+    }
+
+    public long getIntegralValue() {
+        ASTNode literalNode = getNode().getFirstChildNode();
+        IElementType literalType = literalNode.getElementType();
+
+        if (literalType == SmaliTokens.LONG_LITERAL) {
+            return LiteralTools.parseLong(literalNode.getText());
+        } else if (literalType == SmaliTokens.NEGATIVE_INTEGER_LITERAL ||
+                literalType == SmaliTokens.POSITIVE_INTEGER_LITERAL) {
+            return LiteralTools.parseInt(literalNode.getText());
+        } else if (literalType == SmaliTokens.SHORT_LITERAL) {
+            return LiteralTools.parseShort(literalNode.getText());
+        } else if (literalType == SmaliTokens.CHAR_LITERAL) {
+            // TODO: implement this
+            return -1;
+        } else if (literalType == SmaliTokens.BYTE_LITERAL) {
+            return LiteralTools.parseByte(literalNode.getText());
+        } else if (literalType == SmaliTokens.BOOL_LITERAL) {
+            return Boolean.parseBoolean(literalNode.getText())?1:0;
+        } else {
+            throw new RuntimeException("Not an integral literal");
+        }
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLocalDebugStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLocalDebugStatement.java
new file mode 100644
index 0000000..02d1748
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLocalDebugStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliLocalDebugStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliLocalDebugStatement();
+        }
+    };
+
+    public SmaliLocalDebugStatement() {
+        super(SmaliElementTypes.LOCAL_DEBUG_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLocalName.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLocalName.java
new file mode 100644
index 0000000..6ade23a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliLocalName.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.PsiIdentifier;
+import com.intellij.psi.tree.IElementType;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliLocalName extends SmaliCompositeElement implements PsiIdentifier {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliLocalName();
+        }
+    };
+
+    public SmaliLocalName() {
+        super(SmaliElementTypes.LOCAL_NAME);
+    }
+
+    @Override public IElementType getTokenType() {
+        return getNode().getElementType();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMemberName.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMemberName.java
new file mode 100644
index 0000000..f337736
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMemberName.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.PsiIdentifier;
+import com.intellij.psi.impl.source.codeStyle.CodeEditUtil;
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.leaf.SmaliSimpleName;
+
+public class SmaliMemberName extends SmaliCompositeElement implements PsiIdentifier {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliMemberName();
+        }
+    };
+
+    public SmaliMemberName() {
+        super(SmaliElementTypes.MEMBER_NAME);
+    }
+
+    @Override public IElementType getTokenType() {
+        return getElementType();
+    }
+
+    @Override
+    public String getName() {
+        return this.getText();
+    }
+
+    public void setName(@NotNull String newElementName) {
+        removeAllChildren();
+        SmaliSimpleName newNameElement = new SmaliSimpleName(newElementName);
+        CodeEditUtil.setNodeGenerated(newNameElement, true);
+
+        addChild(newNameElement);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java
new file mode 100644
index 0000000..085585b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethod.java
@@ -0,0 +1,371 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.google.common.base.Supplier;
+import com.google.common.base.Suppliers;
+import com.google.common.collect.Maps;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.editor.Document;
+import com.intellij.psi.*;
+import com.intellij.psi.PsiModifier.ModifierConstant;
+import com.intellij.psi.impl.PsiImplUtil;
+import com.intellij.psi.impl.PsiSuperMethodImplUtil;
+import com.intellij.psi.javadoc.PsiDocComment;
+import com.intellij.psi.util.MethodSignature;
+import com.intellij.psi.util.MethodSignatureBackedByPsiMethod;
+import com.intellij.psi.util.PsiTreeUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.dexlib2.analysis.AnalysisException;
+import org.jf.dexlib2.analysis.ClassPath;
+import org.jf.dexlib2.analysis.MethodAnalyzer;
+import org.jf.smalidea.dexlib.SmalideaMethod;
+import org.jf.smalidea.dexlib.analysis.SmalideaClassProvider;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
+import org.jf.smalidea.psi.stub.SmaliMethodStub;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public class SmaliMethod extends SmaliStubBasedPsiElement<SmaliMethodStub>
+        implements PsiMethod, SmaliModifierListOwner, PsiAnnotationMethod {
+    public SmaliMethod(@NotNull SmaliMethodStub stub) {
+        super(stub, SmaliElementTypes.METHOD);
+    }
+
+    public SmaliMethod(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @NotNull @Override public String getName() {
+        SmaliMethodStub stub = getStub();
+        String name = null;
+        if (stub != null) {
+            name = stub.getName();
+        } else {
+            SmaliMemberName nameIdentifier = getNameIdentifier();
+            if (nameIdentifier != null) {
+                name = nameIdentifier.getText();
+            }
+        }
+        if (name == null || name.isEmpty()) {
+            name = "<unnamed>";
+        }
+        return name;
+    }
+
+    @Override public boolean hasTypeParameters() {
+        // TODO: (generics) implement this
+        return false;
+    }
+
+    @NotNull
+    public SmaliMethodPrototype getMethodPrototype() {
+        return getRequiredStubOrPsiChild(SmaliElementTypes.METHOD_PROTOTYPE);
+    }
+
+    @Nullable @Override public PsiType getReturnType() {
+        if (isConstructor()) return null;
+        return getMethodPrototype().getReturnType();
+    }
+
+    @Nullable @Override public PsiTypeElement getReturnTypeElement() {
+        if (isConstructor()) return null;
+        return getMethodPrototype().getReturnTypeElement();
+    }
+
+    @NotNull @Override public SmaliMethodParamList getParameterList() {
+        return getMethodPrototype().getParameterList();
+    }
+
+    @NotNull @Override public SmaliThrowsList getThrowsList() {
+        return getRequiredStubOrPsiChild(SmaliElementTypes.THROWS_LIST);
+    }
+
+    @Nullable @Override public PsiCodeBlock getBody() {
+        // not applicable
+        return null;
+    }
+
+    @NotNull public List<SmaliInstruction> getInstructions() {
+        return findChildrenByType(SmaliElementTypes.INSTRUCTION);
+    }
+
+    @NotNull public List<SmaliCatchStatement> getCatchStatements() {
+        return Arrays.asList(findChildrenByClass(SmaliCatchStatement.class));
+    }
+
+    @Nullable public SourcePosition getSourcePositionForCodeOffset(int offset) {
+        for (SmaliInstruction instruction: getInstructions()) {
+            if (instruction.getOffset() >= offset) {
+                return SourcePosition.createFromElement(instruction);
+            }
+        }
+        return null;
+    }
+
+    public int getOffsetForLine(int line) {
+        PsiDocumentManager documentManager = PsiDocumentManager.getInstance(getProject());
+        final Document document = documentManager.getDocument(getContainingFile());
+        if (document == null) {
+            return -1;
+        }
+
+        for (final SmaliInstruction instruction: getInstructions()) {
+            int curLine = document.getLineNumber(instruction.getTextOffset());
+            if (curLine >= line) {
+                return instruction.getOffset();
+            }
+        }
+        return -1;
+    }
+
+    public int getRegisterCount() {
+        SmaliRegistersStatement registersStatement = findChildByClass(SmaliRegistersStatement.class);
+        if (registersStatement == null) {
+            return 0;
+        }
+        return registersStatement.getRegisterCount();
+    }
+
+    public int getParameterRegisterCount() {
+        int parameterRegisterCount = getMethodPrototype().getParameterList().getParameterRegisterCount();
+        if (!isStatic()) {
+            parameterRegisterCount++;
+        }
+        return parameterRegisterCount;
+    }
+
+    @NotNull public SmaliParameterStatement[] getParameterStatements() {
+        return findChildrenByClass(SmaliParameterStatement.class);
+    }
+
+    @Override public boolean isConstructor() {
+        // TODO: should this return true for the class initializer?
+        return hasModifierProperty("constructor") && !hasModifierProperty("static");
+    }
+
+    public boolean isStatic() {
+        return hasModifierProperty("static");
+    }
+
+    @Override public boolean isVarArgs() {
+        return hasModifierProperty("varargs");
+    }
+
+    @NotNull @Override public MethodSignature getSignature(@NotNull PsiSubstitutor substitutor) {
+        return MethodSignatureBackedByPsiMethod.create(this, substitutor);
+    }
+
+    @Nullable @Override public SmaliMemberName getNameIdentifier() {
+        return findChildByClass(SmaliMemberName.class);
+    }
+
+    @NotNull @Override public PsiMethod[] findSuperMethods() {
+        return PsiSuperMethodImplUtil.findSuperMethods(this);
+    }
+
+    @NotNull @Override public PsiMethod[] findSuperMethods(boolean checkAccess) {
+        return PsiSuperMethodImplUtil.findSuperMethods(this, checkAccess);
+    }
+
+    @NotNull @Override public PsiMethod[] findSuperMethods(PsiClass parentClass) {
+        return PsiSuperMethodImplUtil.findSuperMethods(this, parentClass);
+    }
+
+    @NotNull @Override
+    public List<MethodSignatureBackedByPsiMethod> findSuperMethodSignaturesIncludingStatic(boolean checkAccess) {
+        return PsiSuperMethodImplUtil.findSuperMethodSignaturesIncludingStatic(this, checkAccess);
+    }
+
+    @Nullable @Override public PsiMethod findDeepestSuperMethod() {
+        return PsiSuperMethodImplUtil.findDeepestSuperMethod(this);
+    }
+
+    @NotNull @Override public PsiMethod[] findDeepestSuperMethods() {
+        return PsiSuperMethodImplUtil.findDeepestSuperMethods(this);
+    }
+
+    @NotNull @Override public SmaliModifierList getModifierList() {
+        return getRequiredStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST);
+    }
+
+    @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
+        SmaliMemberName smaliMemberName = getNameIdentifier();
+        if (smaliMemberName == null) {
+            throw new IncorrectOperationException();
+        }
+        smaliMemberName.setName(name);
+        return this;
+    }
+
+    @NotNull @Override public HierarchicalMethodSignature getHierarchicalMethodSignature() {
+        return PsiSuperMethodImplUtil.getHierarchicalMethodSignature(this);
+    }
+
+    @Nullable @Override public PsiDocComment getDocComment() {
+        // not applicable
+        return null;
+    }
+
+    @Override public boolean isDeprecated() {
+        return PsiImplUtil.isDeprecatedByAnnotation(this);
+    }
+
+    @Nullable @Override public PsiTypeParameterList getTypeParameterList() {
+        // TODO: (generics) implement this
+        return null;
+    }
+
+    @NotNull @Override public PsiTypeParameter[] getTypeParameters() {
+        // TODO: (generics) implement this
+        return new PsiTypeParameter[0];
+    }
+
+    @Nullable @Override public SmaliClass getContainingClass() {
+        PsiElement parent = getStubOrPsiParent();
+        if (parent instanceof SmaliClass) {
+            return (SmaliClass) parent;
+        }
+        return null;
+    }
+
+    @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
+        return getModifierList().hasModifierProperty(name);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getAnnotations() {
+        return getStubOrPsiChildren(SmaliElementTypes.ANNOTATION, new SmaliAnnotation[0]);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
+        return getAnnotations();
+    }
+
+    @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        for (SmaliAnnotation annotation: getAnnotations()) {
+            if (qualifiedName.equals(annotation.getQualifiedName())) {
+                return annotation;
+            }
+        }
+        return null;
+    }
+
+    @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        // TODO: implement this
+        return null;
+    }
+
+    private final Supplier<Map<String, SmaliLabel>> labelMap = Suppliers.memoize(
+            new Supplier<Map<String, SmaliLabel>>() {
+                @Override public Map<String, SmaliLabel> get() {
+                    Map<String, SmaliLabel> labelMap = Maps.newHashMap();
+                    for (SmaliLabel label: findChildrenByClass(SmaliLabel.class)) {
+                        if (!labelMap.containsKey(label.getText())) {
+                            labelMap.put(label.getText(), label);
+                        }
+                    }
+                    return labelMap;
+                }
+            });
+
+    @Nullable public SmaliLabel getLabel(String name) {
+        return labelMap.get().get(name);
+    }
+
+    private MethodAnalyzer methodAnalyzer = null;
+
+    @Nullable
+    public MethodAnalyzer getMethodAnalyzer() {
+        if (methodAnalyzer == null) {
+            if (!PsiTreeUtil.hasErrorElements(this)) {
+                ClassPath classPath;
+                try {
+                    classPath = new ClassPath(
+                            new SmalideaClassProvider(getProject(), getContainingFile().getVirtualFile()));
+                } catch (IOException ex) {
+                    throw new RuntimeException(ex);
+                }
+
+                try {
+                    methodAnalyzer = new MethodAnalyzer(classPath, new SmalideaMethod(SmaliMethod.this), null, false);
+                } catch (AnalysisException ex) {
+                    methodAnalyzer = null;
+                }
+            }
+        }
+        return methodAnalyzer;
+    }
+
+    @Override public void subtreeChanged() {
+        super.subtreeChanged();
+        methodAnalyzer = null;
+    }
+
+    @Override public int getTextOffset() {
+        SmaliMemberName smaliMemberName = getNameIdentifier();
+        if (smaliMemberName != null) {
+            return smaliMemberName.getTextOffset();
+        }
+        return super.getTextOffset();
+    }
+
+    @Nullable @Override public PsiAnnotationMemberValue getDefaultValue() {
+        SmaliClass containingClass = getContainingClass();
+        if (containingClass == null || !containingClass.isAnnotationType()) {
+            return null;
+        }
+
+        for (SmaliAnnotation annotation: containingClass.getAnnotations()) {
+            String annotationType = annotation.getQualifiedName();
+            if (annotationType == null) {
+                continue;
+            }
+            if (annotationType.equals("dalvik.annotation.AnnotationDefault")) {
+                PsiAnnotationMemberValue value = annotation.findAttributeValue("value");
+                if (!(value instanceof SmaliAnnotation)) {
+                    return null;
+                }
+                SmaliAnnotation valueSubAnnotation = (SmaliAnnotation)value;
+                return valueSubAnnotation.findAttributeValue(getName());
+            }
+        }
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodParamList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodParamList.java
new file mode 100644
index 0000000..0b759f0
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodParamList.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiParameter;
+import com.intellij.psi.PsiParameterList;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.stub.SmaliMethodParamListStub;
+
+import java.util.Arrays;
+
+public class SmaliMethodParamList extends SmaliStubBasedPsiElement<SmaliMethodParamListStub>
+        implements PsiParameterList {
+    public SmaliMethodParamList(@NotNull SmaliMethodParamListStub stub) {
+        super(stub, SmaliElementTypes.METHOD_PARAM_LIST);
+    }
+
+    public SmaliMethodParamList(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @NotNull @Override public SmaliMethodParameter[] getParameters() {
+        return getStubOrPsiChildren(SmaliElementTypes.METHOD_PARAMETER, new SmaliMethodParameter[0]);
+    }
+
+    @Override public int getParameterIndex(PsiParameter parameter) {
+        if (!(parameter instanceof SmaliMethodParameter)) {
+            return -1;
+        }
+        return Arrays.asList(getParameters()).indexOf(parameter);
+    }
+
+    @Override public int getParametersCount() {
+        return getParameters().length;
+    }
+
+    /**
+     * Returns the number of registers needed for the parameters in this parameter list
+     *
+     * Note: this does *not* include the implicit "this" parameter, if applicable
+     */
+    public int getParameterRegisterCount() {
+        int count = 0;
+        for (SmaliMethodParameter param: getParameters()) {
+            count += param.getRegisterCount();
+        }
+        return count;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodParameter.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodParameter.java
new file mode 100644
index 0000000..93d376e
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodParameter.java
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.*;
+import com.intellij.psi.PsiModifier.ModifierConstant;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
+import org.jf.smalidea.psi.stub.SmaliMethodParameterStub;
+import org.jf.smalidea.util.NameUtils;
+
+public class SmaliMethodParameter extends SmaliStubBasedPsiElement<SmaliMethodParameterStub>
+        implements PsiParameter, SmaliModifierListOwner {
+    public SmaliMethodParameter(@NotNull SmaliMethodParameterStub stub) {
+        super(stub, SmaliElementTypes.METHOD_PARAMETER);
+    }
+
+    public SmaliMethodParameter(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @NotNull @Override public SmaliModifierList getModifierList() {
+        return getRequiredStubOrPsiChild(SmaliElementTypes.MODIFIER_LIST);
+    }
+
+    @NotNull @Override public PsiElement getDeclarationScope() {
+        return getParentMethod();
+    }
+
+    @Override public boolean isVarArgs() {
+        if (getType().getArrayDimensions() == 0 || !getParentMethod().isVarArgs()) {
+            return false;
+        }
+
+        SmaliMethodParamList paramList = getStubOrPsiParentOfType(SmaliMethodParamList.class);
+        if (paramList == null) {
+            return false;
+        }
+        SmaliMethodParameter[] parameters = paramList.getParameters();
+        // is this the last parameter?
+        return parameters[parameters.length-1] == this;
+    }
+
+    @NotNull @Override public SmaliTypeElement getTypeElement() {
+        SmaliTypeElement typeElement = findChildByClass(SmaliTypeElement.class);
+        assert typeElement != null;
+        return typeElement;
+    }
+
+    @NotNull @Override public PsiType getType() {
+        SmaliMethodParameterStub stub = getStub();
+        if (stub != null) {
+            return NameUtils.resolveSmaliToPsiType(this, stub.getSmaliTypeName());
+        }
+        return getTypeElement().getType();
+    }
+
+    @Nullable @Override public PsiExpression getInitializer() {
+        // not applicable
+        return null;
+    }
+
+    @Override public boolean hasInitializer() {
+        return false;
+    }
+
+    @Override public void normalizeDeclaration() throws IncorrectOperationException {
+        // not applicable
+    }
+
+    @Nullable @Override public Object computeConstantValue() {
+        // not applicable
+        return null;
+    }
+
+    @Nullable @Override public String getName() {
+        SmaliMethodParameterStub stub = getStub();
+        if (stub != null) {
+            return stub.getName();
+        }
+        SmaliLocalName name = getNameIdentifier();
+        if (name == null) {
+            return null;
+        }
+        // TODO: get the actual string value
+        return getNameIdentifier().getText();
+    }
+
+    @Nullable @Override public SmaliLocalName getNameIdentifier() {
+        SmaliParameterStatement parameterStatement = findParameterStatement();
+        if (parameterStatement == null) {
+            return null;
+        }
+
+        return parameterStatement.getNameIdentifier();
+    }
+
+    @Override public PsiElement setName(@NonNls @NotNull String name) throws IncorrectOperationException {
+        // TODO: implement this
+        throw new UnsupportedOperationException();
+    }
+
+    @Override public boolean hasModifierProperty(@ModifierConstant @NonNls @NotNull String name) {
+        // not applicable
+        return false;
+    }
+
+    /**
+     * Returns the number of registers required for this parameter. 1 for most types, but 2 for double/long.
+     */
+    public int getRegisterCount() {
+        PsiType type = getType();
+        if (type == PsiType.DOUBLE || type == PsiType.LONG) {
+            return 2;
+        }
+        return 1;
+    }
+
+    @NotNull public SmaliMethod getParentMethod() {
+        SmaliMethod smaliMethod = findStubOrPsiAncestorOfType(SmaliMethod.class);
+        assert smaliMethod != null;
+        return smaliMethod;
+    }
+
+    /**
+     * Gets the parameter register number of this parameters. This is the number of a pNN style register reference.
+     */
+    public int getParameterRegisterNumber() {
+        // TODO: it might be a good idea to cache this, or at least do it non-recursively
+        PsiElement prevSibling = getPrevSibling();
+        if (prevSibling == null) {
+            return getParentMethod().isStatic() ? 0 : 1;
+        }
+        assert prevSibling instanceof SmaliMethodParameter;
+        SmaliMethodParameter prevParam = (SmaliMethodParameter)prevSibling;
+        return prevParam.getParameterRegisterNumber() + prevParam.getRegisterCount();
+    }
+
+    /**
+     * Gets the register number of this parameters. This is the number of a rNN style register reference.
+     */
+    public int getRegisterNumber() {
+        SmaliMethod parentMethod = getParentMethod();
+        return getParameterRegisterNumber() + parentMethod.getRegisterCount() -
+                parentMethod.getParameterRegisterCount();
+    }
+
+    @Nullable
+    private SmaliParameterStatement findParameterStatement() {
+        SmaliMethod parentMethod = getParentMethod();
+
+        for (SmaliParameterStatement parameterStatement: parentMethod.getParameterStatements()) {
+            SmaliRegisterReference registerReference = parameterStatement.getParameterRegister();
+            if (registerReference != null && registerReference.getRegisterNumber() == getRegisterNumber()) {
+                return parameterStatement;
+            }
+        }
+        return null;
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getAnnotations() {
+        SmaliParameterStatement parameterStatement = findParameterStatement();
+        if (parameterStatement == null) {
+            return new SmaliAnnotation[0];
+        }
+        return parameterStatement.getAnnotations();
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
+        return getAnnotations();
+    }
+
+    @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        SmaliParameterStatement parameterStatement = findParameterStatement();
+        if (parameterStatement == null) {
+            return null;
+        }
+        return parameterStatement.findAnnotation(qualifiedName);
+    }
+
+    @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        SmaliParameterStatement parameterStatement = findParameterStatement();
+        if (parameterStatement == null) {
+            // TODO: add a parameter statement for this parameter if not found
+            throw new UnsupportedOperationException();
+        }
+        return parameterStatement.addAnnotation(qualifiedName);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodPrototype.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodPrototype.java
new file mode 100644
index 0000000..3b2aae3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodPrototype.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.stub.SmaliMethodPrototypeStub;
+import org.jf.smalidea.util.NameUtils;
+
+public class SmaliMethodPrototype extends SmaliStubBasedPsiElement<SmaliMethodPrototypeStub> {
+    public SmaliMethodPrototype(@NotNull SmaliMethodPrototypeStub stub) {
+        super(stub, SmaliElementTypes.METHOD_PROTOTYPE);
+    }
+
+    public SmaliMethodPrototype(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @Nullable
+    public PsiType getReturnType() {
+        SmaliMethodPrototypeStub stub = getStub();
+        if (stub != null) {
+            String returnSmaliTypeName = stub.getReturnSmaliTypeName();
+            if (returnSmaliTypeName == null) {
+                return null;
+            }
+            return NameUtils.resolveSmaliToPsiType(this, returnSmaliTypeName);
+        }
+
+        PsiTypeElement returnTypeElement = getReturnTypeElement();
+        if (returnTypeElement == null) {
+            return null;
+        }
+        return returnTypeElement.getType();
+    }
+
+    @Nullable public SmaliTypeElement getReturnTypeElement() {
+        return findChildByClass(SmaliTypeElement.class);
+    }
+
+    @NotNull
+    public SmaliMethodParamList getParameterList() {
+        return getRequiredStubOrPsiChild(SmaliElementTypes.METHOD_PARAM_LIST);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodReference.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodReference.java
new file mode 100644
index 0000000..f8048e9
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodReference.java
@@ -0,0 +1,192 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.google.common.collect.Lists;
+import com.intellij.openapi.util.TextRange;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.PsiType;
+import com.intellij.psi.impl.light.LightMethodBuilder;
+import com.intellij.util.ArrayUtil;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliLanguage;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+import javax.annotation.Nonnull;
+import java.util.ArrayList;
+import java.util.List;
+
+public class SmaliMethodReference extends SmaliCompositeElement implements PsiReference {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliMethodReference();
+        }
+    };
+
+    @Override public String getName() {
+        PsiElement memberName = getMemberName();
+        if (memberName == null) {
+            return null;
+        }
+        return memberName.getText();
+    }
+
+    public SmaliMethodReference() {
+        super(SmaliElementTypes.METHOD_REFERENCE);
+    }
+
+    @Override public PsiReference getReference() {
+        return this;
+    }
+
+    @Override public PsiElement getElement() {
+        return this;
+    }
+
+    @Override public TextRange getRangeInElement() {
+        return new TextRange(0, getTextLength());
+    }
+
+    @Nullable
+    public PsiClass getContainingClass() {
+        SmaliClassTypeElement containingClassReference = getContainingType();
+        if (containingClassReference == null) {
+            return null;
+        }
+        PsiClass containingClass = containingClassReference.resolve();
+        if (containingClass == null) {
+            return null;
+        }
+
+        return containingClass;
+    }
+
+    @Nullable
+    public SmaliClassTypeElement getContainingType() {
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+
+    @Nullable
+    public SmaliMemberName getMemberName() {
+        return findChildByClass(SmaliMemberName.class);
+    }
+
+    @Nonnull
+    public List<PsiType> getParameterTypes() {
+        SmaliMethodReferenceParamList paramList = findChildByClass(SmaliMethodReferenceParamList.class);
+        if (paramList == null) {
+            return Lists.newArrayList();
+        }
+
+        SmaliTypeElement[] parameterElements = paramList.getParameterTypes();
+
+        List<PsiType> types = new ArrayList<PsiType>(parameterElements.length);
+        for (SmaliTypeElement parameterElement: parameterElements) {
+            types.add(parameterElement.getType());
+        }
+        return types;
+    }
+
+    @Nullable
+    public SmaliTypeElement getReturnType() {
+        SmaliTypeElement[] types = findChildrenByClass(SmaliTypeElement.class);
+        if (types.length < 2) {
+            return null;
+        }
+        return types[1];
+    }
+
+    @Nullable @Override public PsiElement resolve() {
+        PsiClass containingClass = getContainingClass();
+        if (containingClass == null) {
+            return null;
+        }
+
+        SmaliMemberName memberName = getMemberName();
+        if (memberName == null) {
+            return null;
+        }
+
+        LightMethodBuilder pattern = new LightMethodBuilder(getManager(), SmaliLanguage.INSTANCE, memberName.getText());
+
+        for (PsiType type: getParameterTypes()) {
+            pattern.addParameter("", type);
+        }
+
+        SmaliTypeElement returnTypeElement = getReturnType();
+        if (returnTypeElement == null) {
+            return null;
+        }
+
+        pattern.setMethodReturnType(returnTypeElement.getType());
+
+        // TODO: what about static constructor?
+        pattern.setConstructor(memberName.getText().equals("<init>"));
+
+        return containingClass.findMethodBySignature(pattern, true);
+    }
+
+    @NotNull @Override public String getCanonicalText() {
+        return getText();
+    }
+
+    @Override public boolean isReferenceTo(PsiElement element) {
+        return resolve() == element;
+    }
+
+    @NotNull @Override public Object[] getVariants() {
+        return ArrayUtil.EMPTY_OBJECT_ARRAY;
+    }
+
+    @Override public boolean isSoft() {
+        return false;
+    }
+
+    @Override public PsiElement handleElementRename(String newElementName) throws IncorrectOperationException {
+        SmaliMemberName memberName = getMemberName();
+        if (memberName == null) {
+            throw new IncorrectOperationException();
+        }
+        memberName.setName(newElementName);
+        return this;
+    }
+
+    @Override public PsiElement bindToElement(@NotNull PsiElement element) throws IncorrectOperationException {
+        //TODO: implement this
+        throw new IncorrectOperationException();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodReferenceParamList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodReferenceParamList.java
new file mode 100644
index 0000000..f9dccf0
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliMethodReferenceParamList.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliMethodReferenceParamList extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliMethodReferenceParamList();
+        }
+    };
+
+    public SmaliMethodReferenceParamList() {
+        super(SmaliElementTypes.METHOD_REFERENCE_PARAM_LIST);
+    }
+
+    @NotNull public SmaliTypeElement[] getParameterTypes() {
+        return findChildrenByClass(SmaliTypeElement.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliModifierList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliModifierList.java
new file mode 100644
index 0000000..c23763f
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliModifierList.java
@@ -0,0 +1,166 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.command.WriteCommandAction;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiModifier.ModifierConstant;
+import com.intellij.psi.PsiModifierList;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.psi.StubBasedPsiElement;
+import com.intellij.psi.impl.source.tree.Factory;
+import com.intellij.psi.impl.source.tree.TreeElement;
+import com.intellij.util.IncorrectOperationException;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.iface.SmaliModifierListOwner;
+import org.jf.smalidea.psi.stub.SmaliModifierListStub;
+import org.jf.smalidea.psi.stub.element.SmaliModifierListElementType;
+
+import javax.annotation.Nonnull;
+
+public class SmaliModifierList extends SmaliStubBasedPsiElement<SmaliModifierListStub>
+        implements StubBasedPsiElement<SmaliModifierListStub>, PsiModifierList {
+    public SmaliModifierList(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    public SmaliModifierList(@NotNull SmaliModifierListStub stub) {
+        super(stub, SmaliModifierListElementType.INSTANCE);
+    }
+
+    public int getAccessFlags() {
+        SmaliModifierListStub stub = getStub();
+        if (stub != null) {
+            return stub.getAccessFlags();
+        }
+
+        int flags = 0;
+
+        for (PsiElement accessSpec: findChildrenByType(SmaliTokens.ACCESS_SPEC)) {
+            AccessFlags flag = AccessFlags.getAccessFlag(accessSpec.getText());
+            if (flag != null) {
+                flags |= flag.getValue();
+            }
+        }
+
+        return flags;
+    }
+
+    @Override public boolean hasModifierProperty(@ModifierConstant @NotNull @NonNls String name) {
+        return hasExplicitModifier(name);
+    }
+
+    @Override public boolean hasExplicitModifier(@ModifierConstant @NotNull @NonNls String name) {
+        SmaliModifierListStub stub = getStub();
+        if (stub != null) {
+            AccessFlags flag = AccessFlags.getAccessFlag(name);
+            if (flag == null) {
+                return false;
+            }
+            return (stub.getAccessFlags() & flag.getValue()) != 0;
+        }
+
+        for (PsiElement accessSpec: findChildrenByType(SmaliTokens.ACCESS_SPEC)) {
+            if (accessSpec.getText().equals(name)) {
+                return true;
+            }
+        }
+
+        return false;
+    }
+
+    @Override
+    public void setModifierProperty(@ModifierConstant @NotNull @NonNls String name, boolean addModifier)
+            throws IncorrectOperationException {
+        if (addModifier) {
+
+            final TreeElement leaf = Factory.createSingleLeafElement(SmaliTokens.ACCESS_SPEC, name, null, getManager());
+
+            new WriteCommandAction.Simple(getProject(), getContainingFile()) {
+                @Override protected void run() throws Throwable {
+                    addInternal(leaf, leaf, null, null);
+                }
+            }.execute();
+        } else {
+            final PsiElement accessSpec = getAccessFlagElement(name);
+            if (accessSpec != null) {
+                new WriteCommandAction.Simple(getProject(), getContainingFile()) {
+                    @Override protected void run() throws Throwable {
+                        accessSpec.delete();
+                    }
+                }.execute();
+            }
+        }
+    }
+
+    @Override
+    public void checkSetModifierProperty(@ModifierConstant @NotNull @NonNls String name, boolean addModifier)
+            throws IncorrectOperationException {
+    }
+
+    @Nonnull
+    private SmaliModifierListOwner getParentForAnnotations() {
+        SmaliModifierListOwner parent = (SmaliModifierListOwner)getStubOrPsiParentOfType(PsiModifierListOwner.class);
+        assert parent != null;
+        return parent;
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getAnnotations() {
+        return getParentForAnnotations().getAnnotations();
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
+        return getParentForAnnotations().getApplicableAnnotations();
+    }
+
+    @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        return getParentForAnnotations().findAnnotation(qualifiedName);
+    }
+
+    @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        return getParentForAnnotations().addAnnotation(qualifiedName);
+    }
+
+    @Nullable public PsiElement getAccessFlagElement(@NotNull String accessFlag) {
+        for (PsiElement accessSpec: findChildrenByType(SmaliTokens.ACCESS_SPEC)) {
+            if (accessSpec.getText().equals(accessFlag)) {
+                return accessSpec;
+            }
+        }
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPackedSwitchElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPackedSwitchElement.java
new file mode 100644
index 0000000..b0e0415
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPackedSwitchElement.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliPackedSwitchElement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliPackedSwitchElement();
+        }
+    };
+
+    public SmaliPackedSwitchElement() {
+        super(SmaliElementTypes.PACKED_SWITCH_ELEMENT);
+    }
+
+    @Nullable
+    public SmaliLabelReference getTarget() {
+        return findChildByClass(SmaliLabelReference.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliParameterStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliParameterStatement.java
new file mode 100644
index 0000000..5fa8c4c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliParameterStatement.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.PsiAnnotationOwner;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliParameterStatement extends SmaliCompositeElement implements PsiAnnotationOwner {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliParameterStatement();
+        }
+    };
+
+    public SmaliParameterStatement() {
+        super(SmaliElementTypes.PARAMETER_STATEMENT);
+    }
+
+    @Nullable
+    public SmaliLocalName getNameIdentifier() {
+        return findChildByClass(SmaliLocalName.class);
+    }
+
+    @Nullable
+    public SmaliRegisterReference getParameterRegister() {
+        return findChildByClass(SmaliRegisterReference.class);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getAnnotations() {
+        return findChildrenByClass(SmaliAnnotation.class);
+    }
+
+    @NotNull @Override public SmaliAnnotation[] getApplicableAnnotations() {
+        return getAnnotations();
+    }
+
+    @Nullable @Override public SmaliAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        for (SmaliAnnotation annotation: getAnnotations()) {
+            if (qualifiedName.equals(annotation.getQualifiedName())) {
+                return annotation;
+            }
+        }
+        return null;
+    }
+
+    @NotNull @Override public SmaliAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        // TODO: implement this
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPrimitiveTypeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPrimitiveTypeElement.java
new file mode 100644
index 0000000..4bdf65a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPrimitiveTypeElement.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.PsiType;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliPrimitiveTypeElement extends SmaliTypeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliPrimitiveTypeElement();
+        }
+    };
+
+    public SmaliPrimitiveTypeElement() {
+        super(SmaliElementTypes.PRIMITIVE_TYPE);
+    }
+
+    @NotNull @Override public PsiType getType() {
+        switch (getText().charAt(0)) {
+            case 'Z':
+                return PsiType.BOOLEAN;
+            case 'B':
+                return PsiType.BYTE;
+            case 'S':
+                return PsiType.SHORT;
+            case 'C':
+                return PsiType.CHAR;
+            case 'I':
+                return PsiType.INT;
+            case 'J':
+                return PsiType.LONG;
+            case 'F':
+                return PsiType.FLOAT;
+            case 'D':
+                return PsiType.DOUBLE;
+            default:
+                throw new RuntimeException("Unexpected primitive type");
+        }
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPrologueDebugStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPrologueDebugStatement.java
new file mode 100644
index 0000000..c4f73f0
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliPrologueDebugStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliPrologueDebugStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliPrologueDebugStatement();
+        }
+    };
+
+    public SmaliPrologueDebugStatement() {
+        super(SmaliElementTypes.PROLOGUE_DEBUG_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRegisterReference.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRegisterReference.java
new file mode 100644
index 0000000..3c2fe7b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRegisterReference.java
@@ -0,0 +1,70 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliRegisterReference extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliRegisterReference();
+        }
+    };
+
+    public SmaliRegisterReference() {
+        super(SmaliElementTypes.REGISTER_REFERENCE);
+    }
+
+    @NotNull
+    public SmaliMethod getParentMethod() {
+        SmaliMethod parentMethod = findAncestorByClass(SmaliMethod.class);
+        assert parentMethod != null;
+        return parentMethod;
+    }
+
+    public int getRegisterNumber() {
+        int registerNumber = Integer.parseInt(getText().substring(1));
+
+        if (isParameterRegister()) {
+            SmaliMethod method = getParentMethod();
+            registerNumber += method.getRegisterCount() - method.getParameterRegisterCount();
+        }
+
+        return registerNumber;
+    }
+
+    public boolean isParameterRegister() {
+        return getText().charAt(0) == 'p';
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRegistersStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRegistersStatement.java
new file mode 100644
index 0000000..93080a7
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRegistersStatement.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.SmaliTokens;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliRegistersStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliRegistersStatement();
+        }
+    };
+
+    public SmaliRegistersStatement() {
+        super(SmaliElementTypes.REGISTERS_STATEMENT);
+    }
+
+    @NotNull
+    private SmaliMethod getParentMethod() {
+        return findAncestorByClass(SmaliMethod.class);
+    }
+
+    /**
+     * Get the total number of registers for the method
+     */
+    public int getRegisterCount() {
+        SmaliLiteral literal = findChildByClass(SmaliLiteral.class);
+        assert literal != null;
+
+        long registerCount = literal.getIntegralValue();
+        // TODO: check for register count that's too large
+        if (isLocals()) {
+            SmaliMethod parentMethod = getParentMethod();
+            return (int)registerCount + parentMethod.getParameterRegisterCount();
+        }
+        return (int)registerCount;
+    }
+
+    private boolean isLocals() {
+        return findChildByType(SmaliTokens.LOCALS_DIRECTIVE) != null;
+    }
+
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRestartLocalDebugStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRestartLocalDebugStatement.java
new file mode 100644
index 0000000..e273607
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliRestartLocalDebugStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliRestartLocalDebugStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliRestartLocalDebugStatement();
+        }
+    };
+
+    public SmaliRestartLocalDebugStatement() {
+        super(SmaliElementTypes.RESTART_LOCAL_DEBUG_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSourceDebugStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSourceDebugStatement.java
new file mode 100644
index 0000000..758e175
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSourceDebugStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliSourceDebugStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliSourceDebugStatement();
+        }
+    };
+
+    public SmaliSourceDebugStatement() {
+        super(SmaliElementTypes.SOURCE_DEBUG_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSourceStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSourceStatement.java
new file mode 100644
index 0000000..4f696cf
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSourceStatement.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliSourceStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliSourceStatement();
+        }
+    };
+
+    public SmaliSourceStatement() {
+        super(SmaliElementTypes.SOURCE_STATEMENT);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSparseSwitchElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSparseSwitchElement.java
new file mode 100644
index 0000000..28fee43
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSparseSwitchElement.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliSparseSwitchElement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliSparseSwitchElement();
+        }
+    };
+
+    public SmaliSparseSwitchElement() {
+        super(SmaliElementTypes.SPARSE_SWITCH_ELEMENT);
+    }
+
+    @Nullable
+    public SmaliLiteral getKey() {
+        return findChildByClass(SmaliLiteral.class);
+    }
+
+    @Nullable
+    public SmaliLabelReference getTarget() {
+        return findChildByClass(SmaliLabelReference.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliStubBasedPsiElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliStubBasedPsiElement.java
new file mode 100644
index 0000000..5df05e2
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliStubBasedPsiElement.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.extapi.psi.StubBasedPsiElementBase;
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.StubBasedPsiElement;
+import com.intellij.psi.stubs.IStubElementType;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class SmaliStubBasedPsiElement<T extends StubElement> extends StubBasedPsiElementBase<T>
+        implements StubBasedPsiElement<T> {
+    protected SmaliStubBasedPsiElement(@NotNull T stub, @NotNull IStubElementType nodeType) {
+        super(stub, nodeType);
+    }
+
+    protected SmaliStubBasedPsiElement(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    @SuppressWarnings("unchecked")
+    @Nullable
+    protected <E extends PsiElement> E findStubOrPsiAncestorOfType(@NotNull Class<E> aClass) {
+        T stub = getStub();
+        if (stub != null)  {
+            StubElement parent = stub.getParentStub();
+            while (parent != null) {
+                PsiElement parentPsi = parent.getPsi();
+                if (aClass.isInstance(parentPsi)) {
+                    return (E)parentPsi;
+                }
+                parent = parent.getParentStub();
+            }
+            return null;
+        }
+
+        PsiElement parent = getParent();
+        while (parent != null) {
+            if (aClass.isInstance(parent)) {
+                return (E)parent;
+            }
+            parent = parent.getParent();
+        }
+        return null;
+    }
+
+    public String toString() {
+        return this.getClass().getSimpleName() + "(" + this.getElementType().toString() + ")";
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSuperStatement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSuperStatement.java
new file mode 100644
index 0000000..c0948dc
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliSuperStatement.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliSuperStatement extends SmaliCompositeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliSuperStatement();
+        }
+    };
+
+    public SmaliSuperStatement() {
+        super(SmaliElementTypes.SUPER_STATEMENT);
+    }
+
+    @Nullable
+    public SmaliClassTypeElement getClassReference() {
+        return findChildByClass(SmaliClassTypeElement.class);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliThrowsList.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliThrowsList.java
new file mode 100644
index 0000000..9cff5c3
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliThrowsList.java
@@ -0,0 +1,56 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiReferenceList;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.stub.SmaliThrowsListStub;
+
+public class SmaliThrowsList extends SmaliBaseReferenceList<SmaliThrowsListStub> implements PsiReferenceList {
+    public SmaliThrowsList(@NotNull ASTNode node) {
+        super(node);
+    }
+
+    public SmaliThrowsList(@NotNull SmaliThrowsListStub stub) {
+        super(stub, SmaliElementTypes.THROWS_LIST);
+    }
+
+    @NotNull @Override public SmaliClassTypeElement[] getReferenceElements() {
+        return SmaliClassTypeElement.EMPTY_ARRAY;
+    }
+
+    @Override public Role getRole() {
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliTypeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliTypeElement.java
new file mode 100644
index 0000000..634cf1a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliTypeElement.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.PsiAnnotation;
+import com.intellij.psi.PsiJavaCodeReferenceElement;
+import com.intellij.psi.PsiTypeElement;
+import com.intellij.psi.tree.IElementType;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+public abstract class SmaliTypeElement extends SmaliCompositeElement implements PsiTypeElement {
+    protected SmaliTypeElement(IElementType type) {
+        super(type);
+    }
+
+    @Nullable @Override public PsiJavaCodeReferenceElement getInnermostComponentReferenceElement() {
+        return null;
+    }
+
+    @NotNull
+    public String getSmaliName() {
+        return getText();
+    }
+
+    // Annotations on types are for JSR 308. Not applicable to smali.
+
+    @NotNull @Override public PsiAnnotation[] getAnnotations() {
+        return new PsiAnnotation[0];
+    }
+
+    @NotNull @Override public PsiAnnotation[] getApplicableAnnotations() {
+        return new PsiAnnotation[0];
+    }
+
+    @Nullable @Override public PsiAnnotation findAnnotation(@NotNull @NonNls String qualifiedName) {
+        return null;
+    }
+
+    @NotNull @Override public PsiAnnotation addAnnotation(@NotNull @NonNls String qualifiedName) {
+        throw new UnsupportedOperationException();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliVoidTypeElement.java b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliVoidTypeElement.java
new file mode 100644
index 0000000..2b540c6
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/impl/SmaliVoidTypeElement.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.impl;
+
+import com.intellij.psi.PsiType;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliCompositeElementFactory;
+import org.jf.smalidea.psi.SmaliElementTypes;
+
+public class SmaliVoidTypeElement extends SmaliTypeElement {
+    public static final SmaliCompositeElementFactory FACTORY = new SmaliCompositeElementFactory() {
+        @Override public SmaliCompositeElement createElement() {
+            return new SmaliVoidTypeElement();
+        }
+    };
+
+    public SmaliVoidTypeElement() {
+        super(SmaliElementTypes.VOID_TYPE);
+    }
+
+    @NotNull @Override public PsiType getType() {
+        return PsiType.VOID;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/index/SmaliClassFinder.java b/smalidea/src/main/java/org/jf/smalidea/psi/index/SmaliClassFinder.java
new file mode 100644
index 0000000..0ebb1ee
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/index/SmaliClassFinder.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.index;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiElementFinder;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliClass;
+
+import java.util.Collection;
+
+public class SmaliClassFinder extends PsiElementFinder {
+    @Override
+    public PsiClass findClass(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
+        Collection<SmaliClass> classes = SmaliClassNameIndex.INSTANCE.get(qualifiedName, scope.getProject(), scope);
+        if (classes != null && classes.size() == 1) {
+            return classes.iterator().next();
+        }
+        return null;
+    }
+
+    @NotNull
+    @Override
+    public PsiClass[] findClasses(@NotNull String qualifiedName, @NotNull GlobalSearchScope scope) {
+        PsiClass cls = findClass(qualifiedName, scope);
+        if (cls != null) {
+            return new PsiClass[] {cls};
+        }
+        return PsiClass.EMPTY_ARRAY;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/index/SmaliClassNameIndex.java b/smalidea/src/main/java/org/jf/smalidea/psi/index/SmaliClassNameIndex.java
new file mode 100644
index 0000000..0429597
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/index/SmaliClassNameIndex.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.index;
+
+import com.intellij.psi.stubs.StringStubIndexExtension;
+import com.intellij.psi.stubs.StubIndexKey;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliClass;
+
+public class SmaliClassNameIndex extends StringStubIndexExtension<SmaliClass> {
+    public static final StubIndexKey<String, SmaliClass> KEY =
+            StubIndexKey.createIndexKey("smali.class.name");
+
+    public static final SmaliClassNameIndex INSTANCE = new SmaliClassNameIndex();
+
+    private SmaliClassNameIndex() {
+    }
+
+    @NotNull @Override public StubIndexKey<String, SmaliClass> getKey() {
+        return KEY;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/leaf/SmaliClassDescriptor.java b/smalidea/src/main/java/org/jf/smalidea/psi/leaf/SmaliClassDescriptor.java
new file mode 100644
index 0000000..36c59f2
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/leaf/SmaliClassDescriptor.java
@@ -0,0 +1,17 @@
+package org.jf.smalidea.psi.leaf;
+
+import com.intellij.psi.PsiIdentifier;
+import com.intellij.psi.impl.source.tree.LeafPsiElement;
+import com.intellij.psi.tree.IElementType;
+import org.jf.smalidea.SmaliTokens;
+
+public class SmaliClassDescriptor extends LeafPsiElement implements PsiIdentifier {
+    public SmaliClassDescriptor(CharSequence text) {
+        super(SmaliTokens.CLASS_DESCRIPTOR, text);
+    }
+
+    @Override
+    public IElementType getTokenType() {
+        return SmaliTokens.CLASS_DESCRIPTOR;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/leaf/SmaliSimpleName.java b/smalidea/src/main/java/org/jf/smalidea/psi/leaf/SmaliSimpleName.java
new file mode 100644
index 0000000..84b2e50
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/leaf/SmaliSimpleName.java
@@ -0,0 +1,10 @@
+package org.jf.smalidea.psi.leaf;
+
+import com.intellij.psi.impl.source.tree.LeafPsiElement;
+import org.jf.smalidea.SmaliTokens;
+
+public class SmaliSimpleName extends LeafPsiElement {
+    public SmaliSimpleName(CharSequence text) {
+        super(SmaliTokens.SIMPLE_NAME, text);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliAnnotationStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliAnnotationStub.java
new file mode 100644
index 0000000..d20882c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliAnnotationStub.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliAnnotation;
+
+public class SmaliAnnotationStub extends StubBase<SmaliAnnotation> {
+    @Nullable
+    private final String annotationSmaliTypeName;
+
+    public SmaliAnnotationStub(StubElement parent, @Nullable String annotationSmaliTypeName) {
+        super(parent, SmaliElementTypes.ANNOTATION);
+        this.annotationSmaliTypeName = annotationSmaliTypeName;
+    }
+
+    @Nullable
+    public String getAnnotationSmaliTypeName() {
+        return annotationSmaliTypeName;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliBaseReferenceListStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliBaseReferenceListStub.java
new file mode 100644
index 0000000..8521c4b
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliBaseReferenceListStub.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.PsiManager;
+import com.intellij.psi.stubs.IStubElementType;
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.impl.LightSmaliClassTypeElement;
+import org.jf.smalidea.psi.impl.SmaliBaseReferenceList;
+import org.jf.smalidea.psi.impl.SmaliClassType;
+
+public abstract class SmaliBaseReferenceListStub<T extends SmaliBaseReferenceList> extends StubBase<T> {
+    @NotNull private final String[] smaliTypeNames;
+    @Nullable private SmaliClassType[] classTypes = null;
+
+    protected SmaliBaseReferenceListStub(
+            @NotNull StubElement parent, @NotNull IStubElementType elementType, @NotNull String[] smaliTypeNames) {
+        super(parent, elementType);
+        this.smaliTypeNames = smaliTypeNames;
+    }
+
+    @NotNull public String[] getSmaliTypeNames() {
+        return smaliTypeNames;
+    }
+
+    @NotNull
+    public SmaliClassType[] getReferencedTypes() {
+        if (classTypes == null) {
+            classTypes = new SmaliClassType[smaliTypeNames.length];
+            for (int i = 0; i< smaliTypeNames.length; i++) {
+                classTypes[i] = new SmaliClassType(
+                        new LightSmaliClassTypeElement(PsiManager.getInstance(getProject()), smaliTypeNames[i]));
+            }
+        }
+        return classTypes;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliClassStatementStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliClassStatementStub.java
new file mode 100644
index 0000000..41faccf
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliClassStatementStub.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliClassStatement;
+
+public class SmaliClassStatementStub extends StubBase<SmaliClassStatement>  {
+    @Nullable private final String qualifiedName;
+
+    public SmaliClassStatementStub(StubElement parent, @Nullable String qualifiedName) {
+        super(parent, SmaliElementTypes.CLASS_STATEMENT);
+        this.qualifiedName = qualifiedName;
+    }
+
+    @Nullable public String getQualifiedName() {
+        return qualifiedName;
+    }
+
+    @Nullable public String getName() {
+        if (qualifiedName == null) {
+            return null;
+        }
+        int lastDot = qualifiedName.lastIndexOf('.');
+        if (lastDot < 0) {
+            return qualifiedName;
+        }
+        return qualifiedName.substring(lastDot+1);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliClassStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliClassStub.java
new file mode 100644
index 0000000..b1656a5
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliClassStub.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliClass;
+
+public class SmaliClassStub extends StubBase<SmaliClass> {
+    public SmaliClassStub(StubElement parent) {
+        super(parent, SmaliElementTypes.CLASS);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliExtendsListStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliExtendsListStub.java
new file mode 100644
index 0000000..9678a29
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliExtendsListStub.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliExtendsList;
+
+public class SmaliExtendsListStub extends SmaliBaseReferenceListStub<SmaliExtendsList> {
+    public SmaliExtendsListStub(@NotNull StubElement parent, @NotNull String[] smaliTypeNames) {
+        super(parent, SmaliElementTypes.EXTENDS_LIST, smaliTypeNames);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliFieldStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliFieldStub.java
new file mode 100644
index 0000000..15e0fbf
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliFieldStub.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliField;
+
+public class SmaliFieldStub extends StubBase<SmaliField> {
+    @Nullable private final String name;
+    @NotNull private final String smaliTypeName;
+
+    public SmaliFieldStub(StubElement parent, @Nullable String name, @NotNull String smaliTypeName) {
+        super(parent,  SmaliElementTypes.FIELD);
+        this.name = name;
+        this.smaliTypeName = smaliTypeName;
+    }
+
+    @Nullable public String getName() {
+        return name;
+    }
+
+    @NotNull public String getSmaliTypeName() {
+        return smaliTypeName;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliFileStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliFileStub.java
new file mode 100644
index 0000000..be05976
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliFileStub.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.PsiFileStubImpl;
+import org.jf.smalidea.psi.impl.SmaliFile;
+
+public class SmaliFileStub extends PsiFileStubImpl<SmaliFile> {
+    public SmaliFileStub(SmaliFile file) {
+        super(file);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliImplementsListStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliImplementsListStub.java
new file mode 100644
index 0000000..2b762c9
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliImplementsListStub.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliImplementsList;
+
+public class SmaliImplementsListStub extends SmaliBaseReferenceListStub<SmaliImplementsList> {
+    public SmaliImplementsListStub(@NotNull StubElement parent, @NotNull String[] smaliTypeNames) {
+        super(parent, SmaliElementTypes.IMPLEMENTS_LIST, smaliTypeNames);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodParamListStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodParamListStub.java
new file mode 100644
index 0000000..f487866
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodParamListStub.java
@@ -0,0 +1,44 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliMethodPrototype;
+
+public class SmaliMethodParamListStub extends StubBase<SmaliMethodPrototype> {
+    public SmaliMethodParamListStub(@NotNull StubElement parent) {
+        super(parent, SmaliElementTypes.METHOD_PARAM_LIST);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodParameterStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodParameterStub.java
new file mode 100644
index 0000000..e6f1cf2
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodParameterStub.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliMethodParameter;
+
+public class SmaliMethodParameterStub extends StubBase<SmaliMethodParameter> {
+    @NotNull private final String smaliTypeName;
+    @Nullable private final String name;
+
+    public SmaliMethodParameterStub(@NotNull StubElement parent, @NotNull String smaliTypeName, @Nullable String name) {
+        super(parent, SmaliElementTypes.METHOD_PARAMETER);
+        this.smaliTypeName = smaliTypeName;
+        this.name = name;
+    }
+
+    @NotNull public String getSmaliTypeName() {
+        return smaliTypeName;
+    }
+
+    @Nullable public String getName() {
+        return name;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodPrototypeStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodPrototypeStub.java
new file mode 100644
index 0000000..ac0b1e9
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodPrototypeStub.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliMethodPrototype;
+
+public class SmaliMethodPrototypeStub extends StubBase<SmaliMethodPrototype> {
+    @Nullable private final String returnSmaliTypeName;
+
+    public SmaliMethodPrototypeStub(@NotNull StubElement parent, @Nullable String returnSmaliTypeName) {
+        super(parent, SmaliElementTypes.METHOD_PROTOTYPE);
+        this.returnSmaliTypeName = returnSmaliTypeName;
+    }
+
+    @Nullable public String getReturnSmaliTypeName() {
+        return returnSmaliTypeName;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodStub.java
new file mode 100644
index 0000000..1412980
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliMethodStub.java
@@ -0,0 +1,53 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+import javax.annotation.Nullable;
+
+public class SmaliMethodStub extends StubBase<SmaliMethod> {
+    @Nullable private final String name;
+
+    public SmaliMethodStub(@NotNull StubElement parent, @Nullable String name) {
+        super(parent, SmaliElementTypes.METHOD);
+        this.name = name;
+    }
+
+    @Nullable public String getName() {
+        return name;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliModifierListStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliModifierListStub.java
new file mode 100644
index 0000000..e0a5f0e
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliModifierListStub.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubBase;
+import com.intellij.psi.stubs.StubElement;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+import org.jf.smalidea.psi.stub.element.SmaliModifierListElementType;
+
+public class SmaliModifierListStub extends StubBase<SmaliMethod> {
+    private final int accessFlags;
+
+    public SmaliModifierListStub(StubElement parent, int accessFlags) {
+        super(parent, SmaliModifierListElementType.INSTANCE);
+        this.accessFlags = accessFlags;
+    }
+
+    public int getAccessFlags() {
+        return accessFlags;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliThrowsListStub.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliThrowsListStub.java
new file mode 100644
index 0000000..94d239e
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/SmaliThrowsListStub.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub;
+
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliThrowsList;
+
+public class SmaliThrowsListStub extends SmaliBaseReferenceListStub<SmaliThrowsList> {
+    public SmaliThrowsListStub(@NotNull StubElement parent) {
+        super(parent, SmaliElementTypes.THROWS_LIST, new String[0]);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliAnnotationElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliAnnotationElementType.java
new file mode 100644
index 0000000..7085cc0
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliAnnotationElementType.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliAnnotation;
+import org.jf.smalidea.psi.stub.SmaliAnnotationStub;
+
+import java.io.IOException;
+
+public class SmaliAnnotationElementType extends SmaliStubElementType<SmaliAnnotationStub, SmaliAnnotation> {
+    public static final SmaliAnnotationElementType INSTANCE = new SmaliAnnotationElementType();
+
+    private SmaliAnnotationElementType() {
+        super("ANNOTATION");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.annotation";
+    }
+
+    @Override public SmaliAnnotation createPsi(@NotNull SmaliAnnotationStub stub) {
+        return new SmaliAnnotation(stub);
+    }
+
+    @Override public SmaliAnnotation createPsi(@NotNull ASTNode node) {
+        return new SmaliAnnotation(node);
+    }
+
+    @Override public SmaliAnnotationStub createStub(@NotNull SmaliAnnotation psi, StubElement parentStub) {
+        return new SmaliAnnotationStub(parentStub, psi.getSmaliName());
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliAnnotationStub stub, @NotNull StubOutputStream dataStream) throws IOException {
+        dataStream.writeName(stub.getAnnotationSmaliTypeName());
+    }
+
+    @NotNull @Override
+    public SmaliAnnotationStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
+        return new SmaliAnnotationStub(parentStub, deserializeNullableString(dataStream));
+    }
+
+    @Override public void indexStub(@NotNull SmaliAnnotationStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliBaseReferenceListElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliBaseReferenceListElementType.java
new file mode 100644
index 0000000..d73a33a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliBaseReferenceListElementType.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliBaseReferenceList;
+import org.jf.smalidea.psi.stub.SmaliBaseReferenceListStub;
+
+import java.io.IOException;
+
+public abstract class SmaliBaseReferenceListElementType<StubT extends SmaliBaseReferenceListStub,
+        PsiT extends SmaliBaseReferenceList> extends SmaliStubElementType<StubT, PsiT> {
+
+    protected SmaliBaseReferenceListElementType(@NotNull @NonNls String debugName) {
+        super(debugName);
+    }
+
+    @Override
+    public void serialize(@NotNull StubT stub, @NotNull StubOutputStream dataStream)
+            throws IOException {
+        String[] references = stub.getSmaliTypeNames();
+        dataStream.writeVarInt(references.length);
+        for (String reference: references) {
+            dataStream.writeName(reference);
+        }
+    }
+
+    @NotNull @Override
+    public StubT deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
+        String[] smaliTypeNames = new String[dataStream.readVarInt()];
+        for (int i=0; i<smaliTypeNames.length; i++) {
+            smaliTypeNames[i] = dataStream.readName().getString();
+        }
+
+        return createStub(parentStub, smaliTypeNames);
+    }
+
+    protected abstract StubT createStub(StubElement parentStub, String[] smaliTypeNames);
+
+    @Override public void indexStub(@NotNull StubT stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliClassElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliClassElementType.java
new file mode 100644
index 0000000..dcd7a2c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliClassElementType.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.impl.java.stubs.index.JavaStubIndexKeys;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.SmaliElementTypes;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.index.SmaliClassNameIndex;
+import org.jf.smalidea.psi.stub.SmaliClassStatementStub;
+import org.jf.smalidea.psi.stub.SmaliClassStub;
+
+import java.io.IOException;
+
+public class SmaliClassElementType extends SmaliStubElementType<SmaliClassStub, SmaliClass> {
+    public static final SmaliClassElementType INSTANCE = new SmaliClassElementType();
+
+    private SmaliClassElementType() {
+        super("CLASS");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.class";
+    }
+
+    @Override public SmaliClass createPsi(@NotNull SmaliClassStub stub) {
+        return new SmaliClass(stub);
+    }
+
+    @Override public SmaliClass createPsi(@NotNull ASTNode node) {
+        return new SmaliClass(node);
+    }
+
+    @Override public SmaliClassStub createStub(@NotNull SmaliClass psi, StubElement parentStub) {
+        return new SmaliClassStub(parentStub);
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliClassStub stub, @NotNull StubOutputStream dataStream) throws IOException {
+
+    }
+
+    @NotNull @Override
+    public SmaliClassStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
+        return new SmaliClassStub(parentStub);
+    }
+
+    @Override public void indexStub(@NotNull SmaliClassStub stub, @NotNull IndexSink sink) {
+        SmaliClassStatementStub smaliClassStatementStub =
+                (SmaliClassStatementStub)stub.findChildStubByType(SmaliElementTypes.CLASS_STATEMENT);
+        if (smaliClassStatementStub != null) {
+            String qualifiedName = smaliClassStatementStub.getQualifiedName();
+            if (qualifiedName != null) {
+                sink.occurrence(SmaliClassNameIndex.KEY, qualifiedName);
+            }
+
+            final String shortName = smaliClassStatementStub.getName();
+            if (shortName != null) {
+                sink.occurrence(JavaStubIndexKeys.CLASS_SHORT_NAMES, shortName);
+            }
+        }
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliClassStatementElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliClassStatementElementType.java
new file mode 100644
index 0000000..f3029c0
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliClassStatementElementType.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliClassStatement;
+import org.jf.smalidea.psi.stub.SmaliClassStatementStub;
+
+import java.io.IOException;
+
+public class SmaliClassStatementElementType extends SmaliStubElementType<SmaliClassStatementStub, SmaliClassStatement> {
+    public static final SmaliClassStatementElementType INSTANCE = new SmaliClassStatementElementType();
+
+    public SmaliClassStatementElementType() {
+        super("CLASS_STATEMENT");
+    }
+
+    @Override public SmaliClassStatement createPsi(@NotNull ASTNode node) {
+        return new SmaliClassStatement(node);
+    }
+
+    @Override public SmaliClassStatement createPsi(@NotNull SmaliClassStatementStub stub) {
+        return new SmaliClassStatement(stub);
+    }
+
+    @Override public SmaliClassStatementStub createStub(@NotNull SmaliClassStatement psi, StubElement parentStub) {
+        return new SmaliClassStatementStub(parentStub, psi.getQualifiedName());
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.class_statement";
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliClassStatementStub stub, @NotNull StubOutputStream dataStream) throws IOException {
+        dataStream.writeName(stub.getQualifiedName());
+    }
+
+    @NotNull @Override
+    public SmaliClassStatementStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
+        return new SmaliClassStatementStub(parentStub, deserializeNullableString(dataStream));
+    }
+
+    @Override public void indexStub(@NotNull SmaliClassStatementStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliExtendsListElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliExtendsListElementType.java
new file mode 100644
index 0000000..3acfd8f
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliExtendsListElementType.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliExtendsList;
+import org.jf.smalidea.psi.stub.SmaliExtendsListStub;
+
+public class SmaliExtendsListElementType extends SmaliBaseReferenceListElementType<SmaliExtendsListStub, SmaliExtendsList> {
+    public static final SmaliExtendsListElementType  INSTANCE = new SmaliExtendsListElementType ();
+
+    private SmaliExtendsListElementType() {
+        super("EXTENDS_LIST");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.extends_list";
+    }
+
+    @Override public SmaliExtendsList createPsi(@NotNull SmaliExtendsListStub stub) {
+        return new SmaliExtendsList(stub);
+    }
+
+    @Override public SmaliExtendsList createPsi(@NotNull ASTNode node) {
+        return new SmaliExtendsList(node);
+    }
+
+    @Override protected SmaliExtendsListStub createStub(StubElement parentStub, String[] smaliTypeNames) {
+        return new SmaliExtendsListStub(parentStub, smaliTypeNames);
+    }
+
+    @Override public SmaliExtendsListStub createStub(@NotNull SmaliExtendsList psi, StubElement parentStub) {
+        return new SmaliExtendsListStub(parentStub, psi.getSmaliNames());
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliFieldElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliFieldElementType.java
new file mode 100644
index 0000000..235e995
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliFieldElementType.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.openapi.project.IndexNotReadyException;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliField;
+import org.jf.smalidea.psi.impl.SmaliTypeElement;
+import org.jf.smalidea.psi.stub.SmaliFieldStub;
+
+import java.io.IOException;
+
+public class SmaliFieldElementType extends SmaliStubElementType<SmaliFieldStub, SmaliField> {
+    public static final SmaliFieldElementType INSTANCE = new SmaliFieldElementType();
+
+    private SmaliFieldElementType() {
+        super("FIELD");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.field";
+    }
+
+    @Override public SmaliField createPsi(@NotNull SmaliFieldStub stub) {
+        return new SmaliField(stub);
+    }
+
+    @Override public SmaliField createPsi(@NotNull ASTNode node) {
+        return new SmaliField(node);
+    }
+
+    @Override public SmaliFieldStub createStub(@NotNull SmaliField psi, StubElement parentStub) {
+        try {
+            String fieldSmaliTypeName;
+            SmaliTypeElement typeElement = psi.getTypeElement();
+            if (typeElement != null) {
+                fieldSmaliTypeName = typeElement.getSmaliName();
+            } else {
+                fieldSmaliTypeName = "Ljava/lang/Object;";
+            }
+
+            return new SmaliFieldStub(parentStub, psi.getName(), fieldSmaliTypeName);
+        } catch (IndexNotReadyException ex) {
+            System.out.println(psi.getName());
+            throw ex;
+        }
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliFieldStub stub, @NotNull StubOutputStream dataStream) throws IOException {
+        dataStream.writeName(stub.getName());
+        dataStream.writeName(stub.getSmaliTypeName());
+    }
+
+    @NotNull @Override
+    public SmaliFieldStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
+        return new SmaliFieldStub(parentStub, deserializeNullableString(dataStream),
+                dataStream.readName().getString());
+    }
+
+    @Override public void indexStub(@NotNull SmaliFieldStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliFileElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliFileElementType.java
new file mode 100644
index 0000000..3e1eaf5
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliFileElementType.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.StubBuilder;
+import com.intellij.psi.stubs.DefaultStubBuilder;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.tree.IStubFileElementType;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.SmaliLanguage;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.stub.SmaliFileStub;
+
+public class SmaliFileElementType extends IStubFileElementType<SmaliFileStub> {
+    public static final SmaliFileElementType INSTANCE = new SmaliFileElementType();
+
+    private SmaliFileElementType() {
+        super("smali.FILE", SmaliLanguage.INSTANCE);
+    }
+
+    @Override public StubBuilder getBuilder() {
+        return new DefaultStubBuilder() {
+            @Override
+            protected StubElement createStubForFile(@NotNull PsiFile file) {
+                if (file instanceof SmaliFile) {
+                    return new SmaliFileStub((SmaliFile)file);
+                }
+                throw new RuntimeException("Unexpected file type");
+            }
+        };
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliImplementsListElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliImplementsListElementType.java
new file mode 100644
index 0000000..3b17bc1
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliImplementsListElementType.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliImplementsList;
+import org.jf.smalidea.psi.stub.SmaliImplementsListStub;
+
+public class SmaliImplementsListElementType
+        extends SmaliBaseReferenceListElementType<SmaliImplementsListStub, SmaliImplementsList> {
+    public static final SmaliImplementsListElementType INSTANCE = new SmaliImplementsListElementType();
+
+    private SmaliImplementsListElementType() {
+        super("IMPLEMENTS_LIST");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.implements_list";
+    }
+
+    @Override public SmaliImplementsList createPsi(@NotNull SmaliImplementsListStub stub) {
+        return new SmaliImplementsList(stub);
+    }
+
+    @Override public SmaliImplementsList createPsi(@NotNull ASTNode node) {
+        return new SmaliImplementsList(node);
+    }
+
+    @Override protected SmaliImplementsListStub createStub(StubElement parentStub, String[] smaliTypeNames) {
+        return new SmaliImplementsListStub(parentStub, smaliTypeNames);
+    }
+
+    @Override public SmaliImplementsListStub createStub(@NotNull SmaliImplementsList psi, StubElement parentStub) {
+        return new SmaliImplementsListStub(parentStub, psi.getSmaliNames());
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodElementType.java
new file mode 100644
index 0000000..1d83f1a
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodElementType.java
@@ -0,0 +1,80 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+import org.jf.smalidea.psi.stub.SmaliMethodStub;
+
+import java.io.IOException;
+
+public class SmaliMethodElementType extends SmaliStubElementType<SmaliMethodStub, SmaliMethod> {
+    public static final SmaliMethodElementType INSTANCE = new SmaliMethodElementType();
+
+    private SmaliMethodElementType() {
+        super("METHOD");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.method";
+    }
+
+    @Override public SmaliMethod createPsi(@NotNull SmaliMethodStub stub) {
+        return new SmaliMethod(stub);
+    }
+
+    @Override public SmaliMethod createPsi(@NotNull ASTNode node) {
+        return new SmaliMethod(node);
+    }
+
+    @Override public SmaliMethodStub createStub(@NotNull SmaliMethod psi, StubElement parentStub) {
+        return new SmaliMethodStub(parentStub, psi.getName());
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliMethodStub stub, @NotNull StubOutputStream dataStream) throws IOException {
+        dataStream.writeName(stub.getName());
+    }
+
+    @NotNull @Override
+    public SmaliMethodStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub) throws IOException {
+        return new SmaliMethodStub(parentStub, dataStream.readName().getString());
+    }
+
+    @Override public void indexStub(@NotNull SmaliMethodStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodParamListElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodParamListElementType.java
new file mode 100644
index 0000000..197e682
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodParamListElementType.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliMethodParamList;
+import org.jf.smalidea.psi.stub.SmaliMethodParamListStub;
+
+import java.io.IOException;
+
+public class SmaliMethodParamListElementType
+        extends SmaliStubElementType<SmaliMethodParamListStub, SmaliMethodParamList> {
+    public static final SmaliMethodParamListElementType INSTANCE = new SmaliMethodParamListElementType();
+
+    private SmaliMethodParamListElementType() {
+        super("METHOD_PARAM_LIST");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.method_param_list";
+    }
+
+    @Override public SmaliMethodParamList createPsi(@NotNull ASTNode node) {
+        return new SmaliMethodParamList(node);
+    }
+
+    @Override public SmaliMethodParamList createPsi(@NotNull SmaliMethodParamListStub stub) {
+        return new SmaliMethodParamList(stub);
+    }
+
+    @Override public SmaliMethodParamListStub createStub(@NotNull SmaliMethodParamList psi, StubElement parentStub) {
+        return new SmaliMethodParamListStub(parentStub);
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliMethodParamListStub stub, @NotNull StubOutputStream dataStream)
+                          throws IOException {
+    }
+
+    @NotNull @Override
+    public SmaliMethodParamListStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub)
+            throws IOException {
+        return new SmaliMethodParamListStub(parentStub);
+    }
+
+    @Override public void indexStub(@NotNull SmaliMethodParamListStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodParameterElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodParameterElementType.java
new file mode 100644
index 0000000..ca5a010
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodParameterElementType.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliMethodParameter;
+import org.jf.smalidea.psi.stub.SmaliMethodParameterStub;
+
+import java.io.IOException;
+
+public class SmaliMethodParameterElementType
+        extends SmaliStubElementType<SmaliMethodParameterStub, SmaliMethodParameter> {
+    public static final SmaliMethodParameterElementType INSTANCE = new SmaliMethodParameterElementType();
+
+    private SmaliMethodParameterElementType() {
+        super("METHOD_PARAMETER");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.method_parameter";
+    }
+
+    @Override public SmaliMethodParameter createPsi(@NotNull ASTNode node) {
+        return new SmaliMethodParameter(node);
+    }
+
+    @Override public SmaliMethodParameter createPsi(@NotNull SmaliMethodParameterStub stub) {
+        return new SmaliMethodParameter(stub);
+    }
+
+    @Override public SmaliMethodParameterStub createStub(@NotNull SmaliMethodParameter psi, StubElement parentStub) {
+        return new SmaliMethodParameterStub(parentStub, psi.getTypeElement().getSmaliName(), psi.getName());
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliMethodParameterStub stub, @NotNull StubOutputStream dataStream)
+                          throws IOException {
+        dataStream.writeName(stub.getSmaliTypeName());
+        dataStream.writeName(stub.getName());
+    }
+
+    @NotNull @Override
+    public SmaliMethodParameterStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub)
+            throws IOException {
+        return new SmaliMethodParameterStub(parentStub, dataStream.readName().getString(),
+                deserializeNullableString(dataStream));
+    }
+
+    @Override public void indexStub(@NotNull SmaliMethodParameterStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodPrototypeElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodPrototypeElementType.java
new file mode 100644
index 0000000..290b78c
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliMethodPrototypeElementType.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliMethodPrototype;
+import org.jf.smalidea.psi.impl.SmaliTypeElement;
+import org.jf.smalidea.psi.stub.SmaliMethodPrototypeStub;
+
+import java.io.IOException;
+
+public class SmaliMethodPrototypeElementType
+        extends SmaliStubElementType<SmaliMethodPrototypeStub, SmaliMethodPrototype> {
+    public static final SmaliMethodPrototypeElementType INSTANCE = new SmaliMethodPrototypeElementType();
+
+    private SmaliMethodPrototypeElementType() {
+        super("METHOD_PROTOTYPE");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.method_prototype";
+    }
+
+    @Override public SmaliMethodPrototype createPsi(@NotNull ASTNode node) {
+        return new SmaliMethodPrototype(node);
+    }
+
+    @Override public SmaliMethodPrototype createPsi(@NotNull SmaliMethodPrototypeStub stub) {
+        return new SmaliMethodPrototype(stub);
+    }
+
+    @Override public SmaliMethodPrototypeStub createStub(@NotNull SmaliMethodPrototype psi, StubElement parentStub) {
+        SmaliTypeElement returnType = psi.getReturnTypeElement();
+        String returnSmaliTypeName = null;
+        if (returnType != null) {
+            returnSmaliTypeName = returnType.getSmaliName();
+        }
+
+        return new SmaliMethodPrototypeStub(parentStub, returnSmaliTypeName);
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliMethodPrototypeStub stub, @NotNull StubOutputStream dataStream)
+            throws IOException {
+        dataStream.writeName(stub.getReturnSmaliTypeName());
+    }
+
+    @NotNull @Override
+    public SmaliMethodPrototypeStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub)
+            throws IOException {
+        return new SmaliMethodPrototypeStub(parentStub, deserializeNullableString(dataStream));
+    }
+
+    @Override public void indexStub(@NotNull SmaliMethodPrototypeStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliModifierListElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliModifierListElementType.java
new file mode 100644
index 0000000..12299b4
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliModifierListElementType.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.IndexSink;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.psi.stubs.StubOutputStream;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliModifierList;
+import org.jf.smalidea.psi.stub.SmaliModifierListStub;
+
+import java.io.IOException;
+
+public class SmaliModifierListElementType extends SmaliStubElementType<SmaliModifierListStub, SmaliModifierList> {
+    public static final SmaliModifierListElementType INSTANCE = new SmaliModifierListElementType();
+
+    private SmaliModifierListElementType() {
+        super("MODIFIER_LIST");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.modifier_list";
+    }
+
+    @Override public SmaliModifierList createPsi(@NotNull SmaliModifierListStub stub) {
+        return new SmaliModifierList(stub);
+    }
+
+    @Override public SmaliModifierList createPsi(@NotNull ASTNode node) {
+        return new SmaliModifierList(node);
+    }
+
+    @Override public SmaliModifierListStub createStub(@NotNull SmaliModifierList psi, StubElement parentStub) {
+        return new SmaliModifierListStub(parentStub, psi.getAccessFlags());
+    }
+
+    @Override
+    public void serialize(@NotNull SmaliModifierListStub stub, @NotNull StubOutputStream dataStream)
+            throws IOException {
+        dataStream.writeVarInt(stub.getAccessFlags());
+    }
+
+    @NotNull @Override
+    public SmaliModifierListStub deserialize(@NotNull StubInputStream dataStream, StubElement parentStub)
+            throws IOException {
+        return new SmaliModifierListStub(parentStub, dataStream.readVarInt());
+    }
+
+    @Override public void indexStub(@NotNull SmaliModifierListStub stub, @NotNull IndexSink sink) {
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliStubElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliStubElementType.java
new file mode 100644
index 0000000..51544b4
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliStubElementType.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.stubs.IStubElementType;
+import com.intellij.psi.stubs.StubElement;
+import com.intellij.psi.stubs.StubInputStream;
+import com.intellij.util.io.StringRef;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smalidea.SmaliLanguage;
+
+import java.io.IOException;
+
+public abstract class SmaliStubElementType<StubT extends StubElement, PsiT extends PsiElement>
+        extends IStubElementType<StubT, PsiT> {
+    protected SmaliStubElementType(@NotNull @NonNls String debugName) {
+        super(debugName, SmaliLanguage.INSTANCE);
+    }
+
+    public abstract PsiT createPsi(@NotNull ASTNode node);
+
+    @Nullable
+    protected String deserializeNullableString(@NotNull StubInputStream dataStream) throws IOException {
+        StringRef stringRef = dataStream.readName();
+        if (stringRef == null) {
+            return null;
+        }
+        return stringRef.getString();
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliThrowsListElementType.java b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliThrowsListElementType.java
new file mode 100644
index 0000000..b53af34
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/psi/stub/element/SmaliThrowsListElementType.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.psi.stub.element;
+
+import com.intellij.lang.ASTNode;
+import com.intellij.psi.stubs.StubElement;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliThrowsList;
+import org.jf.smalidea.psi.stub.SmaliThrowsListStub;
+
+public class SmaliThrowsListElementType
+        extends SmaliBaseReferenceListElementType<SmaliThrowsListStub, SmaliThrowsList> {
+    public static final SmaliThrowsListElementType INSTANCE = new SmaliThrowsListElementType();
+
+    private SmaliThrowsListElementType() {
+        super("THROWS_LIST");
+    }
+
+    @NotNull @Override public String getExternalId() {
+        return "smali.throws_list";
+    }
+
+    @Override public SmaliThrowsList createPsi(@NotNull SmaliThrowsListStub stub) {
+        return new SmaliThrowsList(stub);
+    }
+
+    @Override public SmaliThrowsList createPsi(@NotNull ASTNode node) {
+        return new SmaliThrowsList(node);
+    }
+
+    @Override protected SmaliThrowsListStub createStub(StubElement parentStub, String[] types) {
+        return new SmaliThrowsListStub(parentStub);
+    }
+
+    @Override public SmaliThrowsListStub createStub(@NotNull SmaliThrowsList psi, StubElement parentStub) {
+        return new SmaliThrowsListStub(parentStub);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/util/InstructionUtils.java b/smalidea/src/main/java/org/jf/smalidea/util/InstructionUtils.java
new file mode 100644
index 0000000..e302b01
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/util/InstructionUtils.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.util;
+
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+import org.jf.dexlib2.Opcode;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+import org.jf.smalidea.psi.impl.SmaliLabel;
+import org.jf.smalidea.psi.impl.SmaliLabelReference;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+
+public class InstructionUtils {
+
+    @Nullable
+    public static SmaliInstruction findFirstInstructionWithTarget(
+            @NotNull SmaliMethod method, @NotNull Opcode opcode, int targetOffset) {
+        for (SmaliInstruction instruction: method.getInstructions()) {
+            if (instruction.getOpcode() == opcode) {
+                SmaliLabelReference labelReference = instruction.getTarget();
+                if (labelReference == null) {
+                    continue;
+                }
+
+                SmaliLabel label = labelReference.resolve();
+                if (label == null) {
+                    continue;
+                }
+
+                if (label.getOffset() == targetOffset) {
+                    return instruction;
+                }
+            }
+        }
+        return null;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java b/smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java
new file mode 100644
index 0000000..305c7f8
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/util/NameUtils.java
@@ -0,0 +1,334 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.util;
+
+import com.google.common.collect.ImmutableMap;
+import com.intellij.openapi.project.Project;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.ResolveScopeManager;
+import com.intellij.psi.search.GlobalSearchScope;
+import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
+
+import java.util.Map;
+
+public class NameUtils {
+    private static final Map<String, String> javaToSmaliPrimitiveTypes = ImmutableMap.<String, String>builder()
+            .put("boolean", "Z")
+            .put("byte", "B")
+            .put("char", "C")
+            .put("short", "S")
+            .put("int", "I")
+            .put("long", "J")
+            .put("float", "F")
+            .put("double", "D")
+            .build();
+
+    @NotNull
+    public static String javaToSmaliType(@NotNull PsiType psiType) {
+        if (psiType instanceof PsiClassType) {
+            PsiClass psiClass = ((PsiClassType)psiType).resolve();
+            if (psiClass != null) {
+                return javaToSmaliType(psiClass);
+            }
+        }
+        return javaToSmaliType(psiType.getCanonicalText());
+    }
+
+    @NotNull
+    public static String javaToSmaliType(@NotNull PsiClass psiClass) {
+        String qualifiedName = psiClass.getQualifiedName();
+        if (qualifiedName == null) {
+            throw new IllegalArgumentException("This method does not support anonymous classes");
+        }
+        PsiClass parent = psiClass.getContainingClass();
+        if (parent != null) {
+            int offset = qualifiedName.lastIndexOf('.');
+            String parentName = qualifiedName.substring(0, offset);
+            assert parentName.equals(parent.getQualifiedName());
+            String className = qualifiedName.substring(offset+1, qualifiedName.length());
+            assert className.equals(psiClass.getName());
+            return javaToSmaliType(parentName + '$' + className);
+        } else {
+            return javaToSmaliType(psiClass.getQualifiedName());
+        }
+    }
+
+    @NotNull
+    public static String javaToSmaliType(@NotNull String javaType) {
+        if (javaType.charAt(javaType.length()-1) == ']') {
+            int dimensions = 0;
+            int firstArrayChar = -1;
+            for (int i=0; i<javaType.length(); i++) {
+                if (javaType.charAt(i) == '[') {
+                    if (firstArrayChar == -1) {
+                        firstArrayChar = i;
+                    }
+                    dimensions++;
+                }
+            }
+            if (dimensions > 0) {
+                StringBuilder sb = new StringBuilder(firstArrayChar + 2 + dimensions);
+                for (int i=0; i<dimensions; i++) {
+                    sb.append('[');
+                }
+                convertSimpleJavaToSmaliType(javaType.substring(0, firstArrayChar), sb);
+                return sb.toString();
+            }
+        }
+
+        return simpleJavaToSmaliType(javaType);
+    }
+
+    private static void convertSimpleJavaToSmaliType(@NotNull String javaType, @NotNull StringBuilder dest) {
+        String smaliType = javaToSmaliPrimitiveTypes.get(javaType);
+        if (smaliType != null) {
+            dest.append(smaliType);
+        } else {
+            dest.append('L');
+            for (int i=0; i<javaType.length(); i++) {
+                char c = javaType.charAt(i);
+                if (c == '.') {
+                    dest.append('/');
+                } else {
+                    dest.append(c);
+                }
+            }
+            dest.append(';');
+        }
+    }
+
+    public static PsiClass resolveSmaliType(@NotNull Project project, @NotNull GlobalSearchScope scope,
+                                            @NotNull String smaliType) {
+        JavaPsiFacade facade = JavaPsiFacade.getInstance(project);
+
+        String javaType = NameUtils.smaliToJavaType(smaliType);
+
+        PsiClass psiClass = facade.findClass(javaType, scope);
+        if (psiClass != null) {
+            return psiClass;
+        }
+
+        int offset = javaType.lastIndexOf('.');
+        if (offset < 0) {
+            offset = 0;
+        }
+        // find the first $ after the last .
+        offset = javaType.indexOf('$', offset+1);
+        if (offset < 0) {
+            return null;
+        }
+
+        while (offset > 0 && offset < javaType.length()-1) {
+            String left = javaType.substring(0, offset);
+            psiClass = facade.findClass(left, scope);
+            if (psiClass != null) {
+                psiClass = findInnerClass(psiClass, javaType.substring(offset+1, javaType.length()), facade, scope);
+                if (psiClass != null) {
+                    return psiClass;
+                }
+            }
+            offset = javaType.indexOf('$', offset+1);
+        }
+        return null;
+    }
+
+    @Nullable
+    public static PsiClass resolveSmaliType(@NotNull PsiElement element, @NotNull String smaliType) {
+        GlobalSearchScope scope = ResolveScopeManager.getElementResolveScope(element);
+        return resolveSmaliType(element.getProject(), scope, smaliType);
+    }
+
+    @Nullable
+    public static PsiClass findInnerClass(@NotNull PsiClass outerClass, String innerText, JavaPsiFacade facade,
+                                          GlobalSearchScope scope) {
+        int offset = innerText.indexOf('$');
+        if (offset < 0) {
+            offset = innerText.length();
+        }
+
+        while (offset > 0 && offset <= innerText.length()) {
+            String left = innerText.substring(0, offset);
+            String nextInner = outerClass.getQualifiedName() + "." + left;
+            PsiClass psiClass = facade.findClass(nextInner, scope);
+            if (psiClass != null) {
+                if (offset < innerText.length()) {
+                    psiClass = findInnerClass(psiClass, innerText.substring(offset+1, innerText.length()), facade,
+                            scope);
+                    if (psiClass != null) {
+                        return psiClass;
+                    }
+                } else {
+                    return psiClass;
+                }
+            }
+            if (offset >= innerText.length()) {
+                break;
+            }
+            offset = innerText.indexOf('$', offset+1);
+            if (offset < 0) {
+                offset = innerText.length();
+            }
+        }
+        return null;
+    }
+
+    private static String simpleJavaToSmaliType(@NotNull String simpleJavaType) {
+        StringBuilder sb = new StringBuilder(simpleJavaType.length() + 2);
+        convertSimpleJavaToSmaliType(simpleJavaType, sb);
+        sb.trimToSize();
+        return sb.toString();
+    }
+
+    @NotNull
+    public static String smaliToJavaType(@NotNull String smaliType) {
+        if (smaliType.charAt(0) == '[') {
+            return convertSmaliArrayToJava(smaliType);
+        } else {
+            StringBuilder sb = new StringBuilder(smaliType.length());
+            convertAndAppendNonArraySmaliTypeToJava(smaliType, sb);
+            return sb.toString();
+        }
+    }
+
+    @NotNull
+    public static String resolveSmaliToJavaType(@NotNull Project project, @NotNull GlobalSearchScope scope,
+                                                @NotNull String smaliType) {
+        // First, try to resolve the type and get its qualified name, so that we can make sure
+        // to use the correct name for inner classes
+        PsiClass resolvedType = resolveSmaliType(project, scope, smaliType);
+        if (resolvedType != null) {
+            String qualifiedName = resolvedType.getQualifiedName();
+            if (qualifiedName != null) {
+                return qualifiedName;
+            }
+        }
+
+        // if we can't find it, just do a textual conversion of the name
+        return smaliToJavaType(smaliType);
+    }
+
+    @NotNull
+    public static String resolveSmaliToJavaType(@NotNull PsiElement element, @NotNull String smaliType) {
+        return resolveSmaliToJavaType(element.getProject(), element.getResolveScope(), smaliType);
+    }
+
+    @NotNull
+    public static PsiType resolveSmaliToPsiType(@NotNull PsiElement element, @NotNull String smaliType) {
+        PsiClass resolvedType = resolveSmaliType(element, smaliType);
+        if (resolvedType != null) {
+            PsiElementFactory factory = JavaPsiFacade.getInstance(element.getProject()).getElementFactory();
+            return factory.createType(resolvedType);
+        }
+
+        String javaType = NameUtils.smaliToJavaType(smaliType);
+        PsiElementFactory factory = JavaPsiFacade.getInstance(element.getProject()).getElementFactory();
+        return factory.createTypeFromText(javaType, element);
+    }
+
+    @NotNull
+    private static String convertSmaliArrayToJava(@NotNull String smaliType) {
+        int dimensions=0;
+        while (smaliType.charAt(dimensions) == '[') {
+            dimensions++;
+        }
+
+        StringBuilder sb = new StringBuilder(smaliType.length() + dimensions);
+        convertAndAppendNonArraySmaliTypeToJava(smaliType.substring(dimensions), sb);
+        for (int i=0; i<dimensions; i++) {
+            sb.append("[]");
+        }
+        return sb.toString();
+    }
+
+    private static void convertAndAppendNonArraySmaliTypeToJava(@NotNull String smaliType, @NotNull StringBuilder dest) {
+        switch (smaliType.charAt(0)) {
+            case 'Z':
+                dest.append("boolean");
+                return;
+            case 'B':
+                dest.append("byte");
+                return;
+            case 'C':
+                dest.append("char");
+                return;
+            case 'S':
+                dest.append("short");
+                return;
+            case 'I':
+                dest.append("int");
+                return;
+            case 'J':
+                dest.append("long");
+                return;
+            case 'F':
+                dest.append("float");
+                return;
+            case 'D':
+                dest.append("double");
+            case 'L':
+                for (int i=1; i<smaliType.length()-1; i++) {
+                    char c = smaliType.charAt(i);
+                    if (c == '/') {
+                        dest.append('.');
+                    } else {
+                        dest.append(c);
+                    }
+                }
+                return;
+            case 'V':
+                dest.append("void");
+                return;
+            case 'U':
+                if (smaliType.equals("Ujava/lang/Object;")) {
+                    dest.append("java.lang.Object");
+                    return;
+                }
+                // fall through
+            default:
+                throw new RuntimeException("Invalid smali type: " + smaliType);
+        }
+    }
+
+    @Nullable
+    public static String shortNameFromQualifiedName(@Nullable String qualifiedName) {
+        if (qualifiedName == null) {
+            return null;
+        }
+
+        int index = qualifiedName.lastIndexOf('.');
+        if (index == -1) {
+            return qualifiedName;
+        }
+        return qualifiedName.substring(index+1);
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/util/PsiUtil.java b/smalidea/src/main/java/org/jf/smalidea/util/PsiUtil.java
new file mode 100644
index 0000000..72c5a13
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/util/PsiUtil.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.util;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.util.PsiMatcherExpression;
+
+public class PsiUtil {
+    public static PsiElement searchBackward(PsiElement element, PsiMatcherExpression matcher,
+                                            PsiMatcherExpression until) {
+        while (!matcher.match(element)) {
+            if (until.match(element)) {
+                return null;
+            }
+            PsiElement prev = element.getPrevSibling();
+            if (prev == null) {
+                prev = element.getParent();
+                if (prev == null) {
+                    return null;
+                }
+            }
+            element = prev;
+        }
+        return element;
+    }
+
+    public static PsiElement searchForward(PsiElement element, PsiMatcherExpression matcher,
+                                            PsiMatcherExpression until) {
+        while (!matcher.match(element)) {
+            if (until.match(element)) {
+                return null;
+            }
+            PsiElement next = element.getNextSibling();
+            if (next == null) {
+                next = element.getParent();
+                if (next == null) {
+                    return null;
+                }
+            }
+            element = next;
+        }
+        return element;
+    }
+}
diff --git a/smalidea/src/main/java/org/jf/smalidea/util/StringUtils.java b/smalidea/src/main/java/org/jf/smalidea/util/StringUtils.java
new file mode 100644
index 0000000..e5cc1cc
--- /dev/null
+++ b/smalidea/src/main/java/org/jf/smalidea/util/StringUtils.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.util;
+
+import org.antlr.runtime.CommonToken;
+import org.jetbrains.annotations.Nullable;
+import org.jf.smali.smaliFlexLexer;
+import org.jf.smali.smaliParser;
+
+import java.io.StringReader;
+
+public class StringUtils {
+
+    @Nullable
+    public static String parseQuotedString(String str) {
+        if (str.charAt(0) != '"') {
+            return null;
+        }
+
+        smaliFlexLexer lexer = new smaliFlexLexer(new StringReader(str));
+        lexer.setSuppressErrors(true);
+
+        CommonToken token = (CommonToken)lexer.nextToken();
+        if (token.getType() != smaliParser.STRING_LITERAL) {
+            return null;
+        }
+
+        if (token.getStopIndex() != str.length()-1) {
+            return null;
+        }
+
+        String text = token.getText();
+        return text.substring(1, text.length()-1);
+    }
+}
diff --git a/smalidea/src/main/resources/META-INF/plugin.xml b/smalidea/src/main/resources/META-INF/plugin.xml
new file mode 100644
index 0000000..6e24054
--- /dev/null
+++ b/smalidea/src/main/resources/META-INF/plugin.xml
@@ -0,0 +1,47 @@
+<idea-plugin version="2">
+  <id>org.jf.smalidea</id>
+  <name>Smalidea</name>
+  <version>0.02</version>
+  <vendor email="jesusfreke@jesusfreke.com" url="http://smali.org">JesusFreke</vendor>
+
+  <description><![CDATA[
+      A smali language plugin for IDEA
+    ]]></description>
+
+  <change-notes><![CDATA[
+    ]]>
+  </change-notes>
+
+  <idea-version since-build="131"/>
+
+  <extensions defaultExtensionNs="com.intellij">
+      <fileTypeFactory implementation="org.jf.smalidea.SmaliFileTypeFactory"/>
+      <syntaxHighlighter key="smali" implementationClass="org.jf.smalidea.SmaliHighlighter"/>
+      <colorSettingsPage implementation="org.jf.smalidea.SmaliColorsPage"/>
+      <lang.parserDefinition language="smali" implementationClass="org.jf.smalidea.SmaliParserDefinition"/>
+      <lang.ast.factory language="smali" implementationClass="org.jf.smalidea.SmaliASTFactory"/>
+      <java.elementFinder implementation="org.jf.smalidea.psi.index.SmaliClassFinder"/>
+      <stubIndex implementation="org.jf.smalidea.psi.index.SmaliClassNameIndex"/>
+      <debugger.positionManagerFactory implementation="org.jf.smalidea.debugging.SmaliPositionManagerFactory"/>
+      <xdebugger.debuggerSupport id="SmaliDebuggerSupport" order="first,before XDebuggerSupport"
+                                 implementation="org.jf.smalidea.debugging.SmaliDebuggerSupport"/>
+      <debugger.codeFragmentFactory implementation="org.jf.smalidea.debugging.SmaliCodeFragmentFactory"/>
+      <stubElementTypeHolder class="org.jf.smalidea.psi.SmaliElementTypes" />
+      <lang.findUsagesProvider language="smali"
+                               implementationClass="org.jf.smalidea.findUsages.SmaliFindUsagesProvider"/>
+      <referencesSearch implementation="org.jf.smalidea.findUsages.SmaliClassReferenceSearcher"/>
+      <usageTargetProvider implementation="org.jf.smalidea.findUsages.SmaliUsageTargetProvider" />
+      <usageTypeProvider implementation="org.jf.smalidea.findUsages.SmaliUsageTypeProvider"/>
+      <errorHandler implementation="org.jf.smalidea.errorReporting.ErrorReporter"/>
+  </extensions>
+
+  <application-components>
+  </application-components>
+
+  <project-components>
+  </project-components>
+
+  <actions>
+  </actions>
+
+</idea-plugin>
\ No newline at end of file
diff --git a/smalidea/src/main/resources/icons/smali.png b/smalidea/src/main/resources/icons/smali.png
new file mode 100644
index 0000000..e60efc5
--- /dev/null
+++ b/smalidea/src/main/resources/icons/smali.png
Binary files differ
diff --git a/smalidea/src/test/java/org/jf/smalidea/AnnotationElementNameReferenceTest.java b/smalidea/src/test/java/org/jf/smalidea/AnnotationElementNameReferenceTest.java
new file mode 100644
index 0000000..b11193a
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/AnnotationElementNameReferenceTest.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.*;
+import com.intellij.testFramework.ResolveTestCase;
+import org.junit.Assert;
+
+public class AnnotationElementNameReferenceTest extends ResolveTestCase {
+    public void testSmaliReferenceFromSmali() throws Exception {
+        createFile("AnnotationWithValues.smali", "" +
+                ".class public abstract interface annotation LAnnotationWithValues;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract intValue()I\n" +
+                ".end method");
+
+        PsiReference reference = configureByFileText("" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".annotation runtime LAnnotationWithValues;\n" +
+                "  int<ref>Value = 123\n" +
+                ".end annotation", "blah.smali");
+
+        PsiElement resolved = reference.resolve();
+        Assert.assertNotNull(resolved);
+        Assert.assertTrue(resolved instanceof PsiAnnotationMethod);
+        Assert.assertEquals("intValue", ((PsiAnnotationMethod)resolved).getName());
+        Assert.assertEquals("AnnotationWithValues",
+                ((PsiAnnotationMethod)resolved).getContainingClass().getQualifiedName());
+    }
+
+    public void testJavaReferenceFromSmali() throws Exception {
+        createFile("AnnotationWithValues.java", "" +
+                "public @interface AnnotationWithValues {\n" +
+                "    int intValue();\n" +
+                "}");
+
+        PsiReference reference = configureByFileText("" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".annotation runtime LAnnotationWithValues;\n" +
+                "  int<ref>Value = 123\n" +
+                ".end annotation", "blah.smali");
+
+        PsiElement resolved = reference.resolve();
+        Assert.assertNotNull(resolved);
+        Assert.assertTrue(resolved instanceof PsiAnnotationMethod);
+        Assert.assertEquals("intValue", ((PsiAnnotationMethod)resolved).getName());
+        Assert.assertEquals("AnnotationWithValues",
+                ((PsiAnnotationMethod)resolved).getContainingClass().getQualifiedName());
+    }
+
+    public void testSmaliReferenceFromJava() throws Exception {
+        createFile("AnnotationWithValues.smali", "" +
+                ".class public abstract interface annotation LAnnotationWithValues;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract intValue()I\n" +
+                ".end method");
+
+        PsiReference reference = configureByFileText("" +
+                "@AnnotationWithValues(int<ref>Value=123)\n" +
+                "public class blah {}", "blah.java");
+
+        PsiElement resolved = reference.resolve();
+        Assert.assertNotNull(resolved);
+        Assert.assertTrue(resolved instanceof PsiAnnotationMethod);
+        Assert.assertEquals("intValue", ((PsiAnnotationMethod)resolved).getName());
+        Assert.assertEquals("AnnotationWithValues",
+                ((PsiAnnotationMethod)resolved).getContainingClass().getQualifiedName());
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/ClassMoveTest.java b/smalidea/src/test/java/org/jf/smalidea/ClassMoveTest.java
new file mode 100644
index 0000000..89d4545
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/ClassMoveTest.java
@@ -0,0 +1,55 @@
+package org.jf.smalidea;
+
+import com.intellij.openapi.roots.JavaProjectRootsUtil;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.refactoring.MultiFileTestCase;
+import com.intellij.refactoring.PackageWrapper;
+import com.intellij.refactoring.move.moveClassesOrPackages.AutocreatingSingleSourceRootMoveDestination;
+import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesProcessor;
+import org.jetbrains.annotations.NotNull;
+
+import java.util.List;
+
+public class ClassMoveTest extends MultiFileTestCase {
+    @Override
+    protected String getTestDataPath() {
+        return "testData";
+    }
+
+    @NotNull
+    @Override
+    protected String getTestRoot() {
+        return "/classMove/";
+    }
+
+    public void testBasicFromNoPackage() {
+        doTest("blah", "my");
+    }
+
+    public void testBasicToNoPackage() {
+        doTest("my.blah", "");
+    }
+
+    private void doTest(@NotNull final String oldQualifiedName, @NotNull final String newPackage) {
+        doTest(new PerformAction() {
+            @Override
+            public void performAction(VirtualFile rootDir, VirtualFile rootAfter) throws Exception {
+                doMove(oldQualifiedName, newPackage);
+            }
+        });
+    }
+
+    private void doMove(String oldQualifiedName, final String newPackage) throws Exception {
+        final PsiClass testClass = myJavaFacade.findClass(oldQualifiedName, GlobalSearchScope.allScope(getProject()));
+
+        final List<VirtualFile> contentSourceRoots =
+                JavaProjectRootsUtil.getSuitableDestinationSourceRoots(getProject());
+
+        new MoveClassesOrPackagesProcessor(getProject(), new PsiClass[] {testClass},
+                new AutocreatingSingleSourceRootMoveDestination(new PackageWrapper(getPsiManager(), newPackage),
+                        contentSourceRoots.get(0)), false, false, null).run();
+    }
+
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/ClassReferenceTest.java b/smalidea/src/test/java/org/jf/smalidea/ClassReferenceTest.java
new file mode 100644
index 0000000..05f3b1b
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/ClassReferenceTest.java
@@ -0,0 +1,112 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.psi.JavaResolveResult;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiReference;
+import com.intellij.testFramework.ResolveTestCase;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliClassTypeElement;
+import org.junit.Assert;
+
+public class ClassReferenceTest extends ResolveTestCase {
+    /**
+     * Test a reference to a java class from a smali class
+     */
+    public void testJavaReferenceFromSmali() throws Exception {
+        SmaliClassTypeElement typeElement = (SmaliClassTypeElement)configureByFileText(
+                ".class public Lblah; .super L<ref>java/lang/Object;", "blah.smali");
+
+        Assert.assertNotNull(typeElement);
+        Assert.assertEquals("Object", typeElement.getName());
+
+        PsiClass psiClass = typeElement.resolve();
+        Assert.assertNotNull(psiClass);
+        Assert.assertEquals("java.lang.Object", psiClass.getQualifiedName());
+
+        JavaResolveResult resolveResult = typeElement.advancedResolve(false);
+        Assert.assertNotNull(resolveResult.getElement());
+        Assert.assertEquals("java.lang.Object", ((PsiClass)resolveResult.getElement()).getQualifiedName());
+
+        JavaResolveResult[] resolveResults = typeElement.multiResolve(false);
+        Assert.assertEquals(1, resolveResults.length);
+        Assert.assertNotNull(resolveResults[0].getElement());
+        Assert.assertEquals("java.lang.Object", ((PsiClass)resolveResults[0].getElement()).getQualifiedName());
+    }
+
+    /**
+     * Test a reference to a smali class from a smali class
+     */
+    public void testSmaliReferenceFromSmali() throws Exception {
+        createFile("blarg.smali", ".class public Lblarg; .super Ljava/lang/Object;");
+
+        SmaliClassTypeElement typeElement = (SmaliClassTypeElement)configureByFileText(
+                ".class public Lblah; .super L<ref>blarg;", "blah.smali");
+
+        Assert.assertEquals("blarg", typeElement.getName());
+
+        SmaliClass smaliClass = (SmaliClass)typeElement.resolve();
+        Assert.assertNotNull(smaliClass);
+        Assert.assertEquals("blarg", smaliClass.getQualifiedName());
+
+        JavaResolveResult resolveResult = typeElement.advancedResolve(false);
+        Assert.assertNotNull(resolveResult.getElement());
+        Assert.assertEquals("blarg", ((PsiClass)resolveResult.getElement()).getQualifiedName());
+
+        JavaResolveResult[] resolveResults = typeElement.multiResolve(false);
+        Assert.assertEquals(1, resolveResults.length);
+        Assert.assertNotNull(resolveResults[0].getElement());
+        Assert.assertEquals("blarg", ((PsiClass)resolveResults[0].getElement()).getQualifiedName());
+    }
+
+    /**
+     * Test a reference to a smali class from a java class
+     */
+    public void testSmaliReferenceFromJava() throws Exception {
+        createFile("blarg.smali", ".class public Lblarg; .super Ljava/lang/Object;");
+
+        PsiReference reference = configureByFileText(
+                "public class blah extends bla<ref>rg { }", "blah.java");
+
+        SmaliClass smaliClass = (SmaliClass)reference.resolve();
+        Assert.assertNotNull(smaliClass);
+        Assert.assertEquals("blarg", smaliClass.getQualifiedName());
+    }
+
+    @Override
+    protected Sdk getTestProjectJdk() {
+        return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/ClassRenameTest.java b/smalidea/src/test/java/org/jf/smalidea/ClassRenameTest.java
new file mode 100644
index 0000000..d1c7365
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/ClassRenameTest.java
@@ -0,0 +1,51 @@
+package org.jf.smalidea;
+
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.refactoring.MultiFileTestCase;
+import com.intellij.refactoring.rename.RenameProcessor;
+import org.jetbrains.annotations.NotNull;
+
+public class ClassRenameTest extends MultiFileTestCase {
+    @Override
+    protected String getTestDataPath() {
+        return "testData";
+    }
+
+    @NotNull
+    @Override
+    protected String getTestRoot() {
+        return "/classRename/";
+    }
+
+    public void testBasicNoPackage() {
+        doTest("blah", "blah2");
+    }
+
+    public void testBasicWithPackage() {
+        doTest("my.blah", "blah2");
+    }
+
+    private void doTest(@NotNull final String oldQualifiedName, @NotNull final String newName) {
+        doTest(new PerformAction() {
+            @Override
+            public void performAction(VirtualFile rootDir, VirtualFile rootAfter) throws Exception {
+                doRename(oldQualifiedName, newName);
+            }
+        });
+    }
+
+    private void doRename(String oldQualifiedName, String newName) throws Exception {
+        PsiClass testClass = myJavaFacade.findClass(oldQualifiedName, GlobalSearchScope.allScope(getProject()));
+
+        RenameProcessor processor = new RenameProcessor(getProject(), testClass, newName, false, false);
+        processor.run();
+
+        PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
+        FileDocumentManager.getInstance().saveAllDocuments();
+    }
+
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/FieldReferenceTest.java b/smalidea/src/test/java/org/jf/smalidea/FieldReferenceTest.java
new file mode 100644
index 0000000..78360d8
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/FieldReferenceTest.java
@@ -0,0 +1,127 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiReference;
+import com.intellij.testFramework.ResolveTestCase;
+import org.jf.smalidea.psi.impl.SmaliFieldReference;
+import org.junit.Assert;
+
+public class FieldReferenceTest extends ResolveTestCase {
+    /**
+     * Test a reference to a java field from a smali class
+     */
+    public void testJavaReferenceFromSmali() throws Exception {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method public blah()V\n" +
+                        "    .locals 1\n" +
+                        "    sget-object v0, Ljava/lang/System;->o<ref>ut:Ljava/io/PrintStream;\n" +
+                        "    return-void\n" +
+                        ".end method";
+
+        SmaliFieldReference fieldReference = (SmaliFieldReference)configureByFileText(text, "blah.smali");
+
+        Assert.assertNotNull(fieldReference);
+        Assert.assertEquals("out", fieldReference.getName());
+        Assert.assertNotNull(fieldReference.getFieldType());
+        Assert.assertEquals("java.io.PrintStream", fieldReference.getFieldType().getType().getCanonicalText());
+
+        PsiField resolvedField = fieldReference.resolve();
+        Assert.assertNotNull(resolvedField);
+        Assert.assertEquals("out", resolvedField.getName());
+        Assert.assertNotNull(resolvedField.getContainingClass());
+        Assert.assertEquals("java.lang.System", resolvedField.getContainingClass().getQualifiedName());
+        Assert.assertEquals("java.io.PrintStream", resolvedField.getType().getCanonicalText());
+    }
+
+    /**
+     * Test a reference to a smali field from a smali class
+     */
+    public void testSmaliReferenceFromSmali() throws Exception {
+        createFile("blarg.smali", ".class public Lblarg; .super Ljava/lang/Object;" +
+                ".field public static blort:I");
+
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method public blah()V\n" +
+                        "    .locals 1\n" +
+                        "    sget v0, Lblarg;->bl<ref>ort:I\n" +
+                        "    return-void\n" +
+                        ".end method";
+
+        SmaliFieldReference fieldReference = (SmaliFieldReference)configureByFileText(text, "blah.smali");
+
+        Assert.assertNotNull(fieldReference);
+        Assert.assertEquals("blort", fieldReference.getName());
+        Assert.assertNotNull(fieldReference.getFieldType());
+        Assert.assertEquals("int", fieldReference.getFieldType().getType().getCanonicalText());
+
+        PsiField resolvedField = fieldReference.resolve();
+        Assert.assertNotNull(resolvedField);
+        Assert.assertEquals("blort", resolvedField.getName());
+        Assert.assertNotNull(resolvedField.getContainingClass());
+        Assert.assertEquals("blarg", resolvedField.getContainingClass().getQualifiedName());
+        Assert.assertEquals("int", resolvedField.getType().getCanonicalText());
+    }
+
+    /**
+     * Test a reference to a smali field from a java class
+     */
+    public void testSmaliReferenceFromJava() throws Exception {
+        createFile("blarg.smali", ".class public Lblarg; .super Ljava/lang/Object;" +
+                ".field public static blort:I");
+
+        String text = "public class blah { public static void something() {" +
+                        "blarg.bl<ref>ort = 10;" +
+                        "}}";
+
+        PsiReference fieldReference = configureByFileText(text, "blah.java");
+
+        Assert.assertNotNull(fieldReference);
+
+        PsiField resolvedField = (PsiField)fieldReference.resolve();
+        Assert.assertNotNull(resolvedField);
+        Assert.assertEquals("blort", resolvedField.getName());
+        Assert.assertNotNull(resolvedField.getContainingClass());
+        Assert.assertEquals("blarg", resolvedField.getContainingClass().getQualifiedName());
+        Assert.assertEquals("int", resolvedField.getType().getCanonicalText());
+    }
+
+    @Override
+    protected Sdk getTestProjectJdk() {
+        return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/FieldRenameTest.java b/smalidea/src/test/java/org/jf/smalidea/FieldRenameTest.java
new file mode 100644
index 0000000..4edab84
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/FieldRenameTest.java
@@ -0,0 +1,50 @@
+package org.jf.smalidea;
+
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiField;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.refactoring.MultiFileTestCase;
+import com.intellij.refactoring.rename.RenameProcessor;
+import org.jetbrains.annotations.NotNull;
+
+public class FieldRenameTest extends MultiFileTestCase {
+    @Override
+    protected String getTestDataPath() {
+        return "testData";
+    }
+
+    @NotNull
+    @Override
+    protected String getTestRoot() {
+        return "/fieldRename/";
+    }
+
+    public void testFieldRename() {
+        doTest("blah", "blah", "blort");
+    }
+
+    private void doTest(@NotNull final String containingClass, @NotNull final String oldFieldName,
+                        @NotNull final String newFieldName) {
+        doTest(new PerformAction() {
+            @Override
+            public void performAction(VirtualFile rootDir, VirtualFile rootAfter) throws Exception {
+                doRename(containingClass, oldFieldName, newFieldName);
+            }
+        });
+    }
+
+    private void doRename(String containingClass, String oldFieldName, String newFieldName) throws Exception {
+        PsiClass testClass = myJavaFacade.findClass(containingClass, GlobalSearchScope.allScope(getProject()));
+
+        PsiField field = testClass.findFieldByName(oldFieldName, false);
+
+        RenameProcessor processor = new RenameProcessor(getProject(), field, newFieldName, false, false);
+        processor.run();
+
+        PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
+        FileDocumentManager.getInstance().saveAllDocuments();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/LightCodeInsightParsingTestCase.java b/smalidea/src/test/java/org/jf/smalidea/LightCodeInsightParsingTestCase.java
new file mode 100644
index 0000000..290021a
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/LightCodeInsightParsingTestCase.java
@@ -0,0 +1,209 @@
+/*
+ * Copyright 2000-2014 JetBrains s.r.o.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.lang.Language;
+import com.intellij.openapi.fileEditor.impl.LoadTextUtil;
+import com.intellij.openapi.util.io.FileUtil;
+import com.intellij.openapi.vfs.CharsetToolkit;
+import com.intellij.psi.*;
+import com.intellij.psi.impl.DebugUtil;
+import com.intellij.psi.impl.PsiFileFactoryImpl;
+import com.intellij.psi.impl.source.PsiFileImpl;
+import com.intellij.psi.stubs.SerializationManagerImpl;
+import com.intellij.psi.stubs.SerializerNotFoundException;
+import com.intellij.psi.stubs.StubTree;
+import com.intellij.testFramework.LightVirtualFile;
+import com.intellij.testFramework.TestDataFile;
+import com.intellij.testFramework.UsefulTestCase;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jetbrains.annotations.NonNls;
+import org.jetbrains.annotations.NotNull;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.util.Set;
+
+/**
+ * A test case for parsing tests.
+ *
+ * This was originally based on com.intellij.testFramework.ParsingTestCase, but was modified
+ * to use the LightCodeInsightFixtureTestCase base class, which provides more functionality
+ */
+public abstract class LightCodeInsightParsingTestCase extends LightCodeInsightFixtureTestCase {
+    protected final String myFilePrefix = "";
+    protected final String myFileExt;
+    @NonNls protected final String myFullDataPath;
+    protected final Language myLanguage;
+
+    protected PsiFile myFile;
+
+    public LightCodeInsightParsingTestCase(@NonNls @NotNull String dataPath, @NotNull String fileExt,
+                                           @NotNull Language language) {
+        myLanguage = language;
+        myFullDataPath = getTestDataPath() + "/" + dataPath;
+        myFileExt = fileExt;
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+        myFile = null;
+    }
+
+    protected boolean includeRanges() {
+        return false;
+    }
+
+    protected boolean skipSpaces() {
+        return false;
+    }
+
+    protected boolean checkAllPsiRoots() {
+        return true;
+    }
+
+    protected void doTest(boolean checkResult) {
+        String name = getTestName(false);
+        try {
+            String text = loadFile(name + "." + myFileExt);
+            PsiFile f = createPsiFile(name, text);
+
+            if (f instanceof PsiFileImpl) {
+                // Also want to test stub serialization/deserialization
+                StubTree stubTree = ((PsiFileImpl)f).calcStubTree();
+
+                ByteArrayOutputStream baos = new ByteArrayOutputStream();
+                SerializationManagerImpl.getInstanceEx().serialize(stubTree.getRoot(), baos);
+                ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
+                SerializationManagerImpl.getInstanceEx().deserialize(bais);
+            }
+
+            ensureParsed(f);
+            assertEquals("light virtual file text mismatch", text,
+                    ((LightVirtualFile)f.getVirtualFile()).getContent().toString());
+            assertEquals("virtual file text mismatch", text, LoadTextUtil.loadText(f.getVirtualFile()));
+            assertEquals("doc text mismatch", text, f.getViewProvider().getDocument().getText());
+            assertEquals("psi text mismatch", text, f.getText());
+            if (checkResult){
+                checkResult(name, f);
+            }
+            else{
+                toParseTreeText(f, skipSpaces(), includeRanges());
+            }
+        }
+        catch (IOException e) {
+            throw new RuntimeException(e);
+        } catch (SerializerNotFoundException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    protected void doTest(String suffix) throws IOException {
+        String name = getTestName(false);
+        String text = loadFile(name + "." + myFileExt);
+        myFile = createPsiFile(name, text);
+        ensureParsed(myFile);
+        assertEquals(text, myFile.getText());
+        checkResult(name + suffix, myFile);
+    }
+
+    protected void doCodeTest(String code) throws IOException {
+        String name = getTestName(false);
+        myFile = createPsiFile("a", code);
+        ensureParsed(myFile);
+        assertEquals(code, myFile.getText());
+        checkResult(myFilePrefix + name, myFile);
+    }
+
+    protected PsiFile createPsiFile(String name, String text) {
+        return createFile(name + "." + myFileExt, text);
+    }
+
+    protected PsiFile createFile(@NonNls String name, String text) {
+        LightVirtualFile virtualFile = new LightVirtualFile(name, myLanguage, text);
+        virtualFile.setCharset(CharsetToolkit.UTF8_CHARSET);
+        return createFile(virtualFile);
+    }
+
+    protected PsiFile createFile(LightVirtualFile virtualFile) {
+        return ((PsiFileFactoryImpl)PsiFileFactory.getInstance(getProject())).trySetupPsiForFile(
+                virtualFile, myLanguage, true, false);
+    }
+
+    protected void checkResult(@NonNls @TestDataFile String targetDataName, final PsiFile file) throws IOException {
+        doCheckResult(myFullDataPath, file, checkAllPsiRoots(), targetDataName, skipSpaces(), includeRanges());
+    }
+
+    public static void doCheckResult(String myFullDataPath,
+                                     PsiFile file,
+                                     boolean checkAllPsiRoots,
+                                     String targetDataName,
+                                     boolean skipSpaces,
+                                     boolean printRanges) throws IOException {
+        FileViewProvider provider = file.getViewProvider();
+        Set<Language> languages = provider.getLanguages();
+
+        if (!checkAllPsiRoots || languages.size() == 1) {
+            doCheckResult(myFullDataPath, targetDataName + ".txt", toParseTreeText(file, skipSpaces, printRanges).trim());
+            return;
+        }
+
+        for (Language language : languages) {
+            PsiFile root = provider.getPsi(language);
+            String expectedName = targetDataName + "." + language.getID() + ".txt";
+            doCheckResult(myFullDataPath, expectedName, toParseTreeText(root, skipSpaces, printRanges).trim());
+        }
+    }
+
+    protected void checkResult(@TestDataFile @NonNls String targetDataName, final String text) throws IOException {
+        doCheckResult(myFullDataPath, targetDataName, text);
+    }
+
+    public static void doCheckResult(String fullPath, String targetDataName, String text) throws IOException {
+        String expectedFileName = fullPath + File.separatorChar + targetDataName;
+        UsefulTestCase.assertSameLinesWithFile(expectedFileName, text);
+    }
+
+    protected static String toParseTreeText(final PsiElement file,  boolean skipSpaces, boolean printRanges) {
+        return DebugUtil.psiToString(file, skipSpaces, printRanges);
+    }
+
+    protected String loadFile(@NonNls @TestDataFile String name) throws IOException {
+        return doLoadFile(myFullDataPath, name);
+    }
+
+    private static String doLoadFile(String myFullDataPath, String name) throws IOException {
+        return FileUtil.loadFile(new File(myFullDataPath, name), CharsetToolkit.UTF8, true).trim();
+    }
+
+    public static void ensureParsed(PsiFile file) {
+        file.accept(new PsiElementVisitor() {
+            @Override
+            public void visitElement(PsiElement element) {
+                element.acceptChildren(this);
+            }
+        });
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/MethodReferenceTest.java b/smalidea/src/test/java/org/jf/smalidea/MethodReferenceTest.java
new file mode 100644
index 0000000..1de6598
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/MethodReferenceTest.java
@@ -0,0 +1,143 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.PsiReference;
+import com.intellij.testFramework.ResolveTestCase;
+import org.jf.smalidea.psi.impl.SmaliMethodReference;
+import org.junit.Assert;
+
+public class MethodReferenceTest extends ResolveTestCase {
+    /**
+     * Test a reference to a java method from a smali class
+     */
+    public void testJavaReferenceFromSmali() throws Exception {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method public blah()V\n" +
+                        "    .locals 1\n" +
+
+                        "    invoke-static {}, Ljava/lang/System;->nano<ref>Time()J\n" +
+                        "    return-void\n" +
+                        ".end method";
+
+        SmaliMethodReference methodReference = (SmaliMethodReference)configureByFileText(text, "blah.smali");
+
+        Assert.assertNotNull(methodReference);
+        Assert.assertEquals("nanoTime", methodReference.getName());
+
+        PsiMethod resolvedMethod = (PsiMethod)methodReference.resolve();
+        Assert.assertNotNull(resolvedMethod);
+        Assert.assertEquals("nanoTime", resolvedMethod.getName());
+        Assert.assertNotNull(resolvedMethod.getContainingClass());
+        Assert.assertEquals("java.lang.System", resolvedMethod.getContainingClass().getQualifiedName());
+        Assert.assertEquals(0, resolvedMethod.getParameterList().getParametersCount());
+        Assert.assertNotNull(resolvedMethod.getReturnType());
+        Assert.assertEquals("long", resolvedMethod.getReturnType().getCanonicalText());
+    }
+
+    /**
+     * Test a reference to a smali method from a smali class
+     */
+    public void testSmaliReferenceFromSmali() throws Exception {
+        createFile("blarg.smali", ".class public Lblarg; .super Ljava/lang/Object;" +
+                ".method public static blort(ILjava/lang/String;)V\n" +
+                "    .locals 0\n" +
+                "    return-void\n" +
+                ".end method\n");
+
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method public blah2()V\n" +
+                        "    .locals 0\n" +
+                        "    invoke-static {}, Lblarg;->bl<ref>ort(ILjava/lang/String;)V\n" +
+                        "    return-void\n" +
+                        ".end method";
+
+        SmaliMethodReference methodReference = (SmaliMethodReference)configureByFileText(text, "blah.smali");
+
+        Assert.assertNotNull(methodReference);
+        Assert.assertEquals("blort", methodReference.getName());
+
+        PsiMethod resolvedMethod = (PsiMethod)methodReference.resolve();
+        Assert.assertNotNull(resolvedMethod);
+        Assert.assertEquals("blort", resolvedMethod.getName());
+        Assert.assertNotNull(resolvedMethod.getContainingClass());
+        Assert.assertEquals("blarg", resolvedMethod.getContainingClass().getQualifiedName());
+        Assert.assertEquals(2, resolvedMethod.getParameterList().getParametersCount());
+        Assert.assertEquals("int", resolvedMethod.getParameterList().getParameters()[0].getType().getCanonicalText());
+        Assert.assertEquals("java.lang.String",
+                resolvedMethod.getParameterList().getParameters()[1].getType().getCanonicalText());
+        Assert.assertNotNull(resolvedMethod.getReturnType());
+        Assert.assertEquals("void", resolvedMethod.getReturnType().getCanonicalText());
+    }
+
+    /**
+     * Test a reference to a smali method from a java class
+     */
+    public void testSmaliReferenceFromJava() throws Exception {
+        createFile("blarg.smali", ".class public Lblarg; .super Ljava/lang/Object;" +
+                ".method public static blort(ILjava/lang/String;)V\n" +
+                "    .locals 0\n" +
+                "    return-void\n" +
+                ".end method\n");
+
+
+        String text = "public class blah { public static void something() {" +
+                        "blarg.bl<ref>ort(10, \"bob\");" +
+                        "}}";
+
+        PsiReference methodReference = configureByFileText(text, "blah.java");
+
+        Assert.assertNotNull(methodReference);
+
+        PsiMethod resolvedMethod = (PsiMethod)methodReference.resolve();
+        Assert.assertNotNull(resolvedMethod);
+        Assert.assertEquals("blort", resolvedMethod.getName());
+        Assert.assertNotNull(resolvedMethod.getContainingClass());
+        Assert.assertEquals("blarg", resolvedMethod.getContainingClass().getQualifiedName());
+        Assert.assertEquals(2, resolvedMethod.getParameterList().getParametersCount());
+        Assert.assertEquals("int", resolvedMethod.getParameterList().getParameters()[0].getType().getCanonicalText());
+        Assert.assertEquals("java.lang.String",
+                resolvedMethod.getParameterList().getParameters()[1].getType().getCanonicalText());
+        Assert.assertNotNull(resolvedMethod.getReturnType());
+        Assert.assertEquals("void", resolvedMethod.getReturnType().getCanonicalText());
+    }
+
+    @Override
+    protected Sdk getTestProjectJdk() {
+        return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/MethodRenameTest.java b/smalidea/src/test/java/org/jf/smalidea/MethodRenameTest.java
new file mode 100644
index 0000000..29cf4d3
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/MethodRenameTest.java
@@ -0,0 +1,50 @@
+package org.jf.smalidea;
+
+import com.intellij.openapi.fileEditor.FileDocumentManager;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiMethod;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.refactoring.MultiFileTestCase;
+import com.intellij.refactoring.rename.RenameProcessor;
+import org.jetbrains.annotations.NotNull;
+
+public class MethodRenameTest extends MultiFileTestCase {
+    @Override
+    protected String getTestDataPath() {
+        return "testData";
+    }
+
+    @NotNull
+    @Override
+    protected String getTestRoot() {
+        return "/methodRename/";
+    }
+
+    public void testMethodRename() {
+        doTest("blah", "blah", "blort");
+    }
+
+    private void doTest(@NotNull final String containingClass, @NotNull final String oldMethodName,
+                        @NotNull final String newMethodName) {
+        doTest(new PerformAction() {
+            @Override
+            public void performAction(VirtualFile rootDir, VirtualFile rootAfter) throws Exception {
+                doRename(containingClass, oldMethodName, newMethodName);
+            }
+        });
+    }
+
+    private void doRename(String containingClass, String oldMethodName, String newMethodName) throws Exception {
+        PsiClass testClass = myJavaFacade.findClass(containingClass, GlobalSearchScope.allScope(getProject()));
+
+        PsiMethod method = testClass.findMethodsByName(oldMethodName, false)[0];
+
+        RenameProcessor processor = new RenameProcessor(getProject(), method, newMethodName, false, false);
+        processor.run();
+
+        PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
+        FileDocumentManager.getInstance().saveAllDocuments();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/ParserTest.java b/smalidea/src/test/java/org/jf/smalidea/ParserTest.java
new file mode 100644
index 0000000..0504bfe
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/ParserTest.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+public class ParserTest extends LightCodeInsightParsingTestCase {
+    public ParserTest() {
+        super("", "smalidea", SmaliLanguage.INSTANCE);
+    }
+
+    @Override
+    protected String getTestDataPath() {
+        return "testData";
+    }
+
+    public void testEmpty() throws Exception { doTest(true); }
+    public void testFieldAnnotations() throws Exception { doTest(true); }
+    public void testInvalidAnnotation() throws Exception { doTest(true); }
+    public void testInvalidClassDirective() throws Exception { doTest(true); }
+    public void testInvalidClassDirective2() throws Exception { doTest(true); }
+    public void testInvalidClassDirective3() throws Exception { doTest(true); }
+    public void testInvalidEnumLiteral() throws Exception { doTest(true); }
+    public void testInvalidField() throws Exception { doTest(true); }
+    public void testInvalidField2() throws Exception { doTest(true); }
+    public void testInvalidField3() throws Exception { doTest(true); }
+    public void testInvalidField4() throws Exception { doTest(true); }
+    public void testInvalidInstruction() throws Exception { doTest(true); }
+    public void testInvalidLocal() throws Exception { doTest(true);}
+    public void testParamListInvalidParameter() throws Exception { doTest(true); }
+    public void testSuperClassInvalidSyntax() throws Exception { doTest(true); }
+    public void testSuperClassInvalidSyntax2() throws Exception { doTest(true); }
+    public void testInvalidMethodReference() throws Exception { doTest(true); }
+    public void testInvalidParameter() throws Exception { doTest(true); }
+    public void testInvalidMethod() throws Exception { doTest(true); }
+    public void testInvalidMethod2() throws Exception { doTest(true); }
+    public void testInvalidMethod3() throws Exception { doTest(true); }
+    public void testInvalidMethod4() throws Exception { doTest(true); }
+    public void testMissingDotDot() throws Exception { doTest(true); }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliAnnotationTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliAnnotationTest.java
new file mode 100644
index 0000000..3cd863f
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliAnnotationTest.java
@@ -0,0 +1,273 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.*;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliLiteral;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+import org.junit.Assert;
+
+public class SmaliAnnotationTest extends LightCodeInsightFixtureTestCase {
+    // TODO: test default values
+
+    public void testClassAnnotation() {
+        myFixture.addFileToProject("my/TestAnnotation.smali",
+                ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract testBooleanValue()Z\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringValue()Ljava/lang/String;\n" +
+                ".end method");
+
+        myFixture.addFileToProject("my/TestAnnotation2.smali",
+                ".class public interface abstract annotation Lmy/TestAnnotation2;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                "\n" +
+                ".annotation runtime Lmy/TestAnnotation;\n" +
+                "    testBooleanValue = true\n" +
+                "    testStringValue = \"blah\"\n" +
+                "    testStringArrayValue = {\n" +
+                "        \"blah1\",\n" +
+                "        \"blah2\"\n" +
+                "    }\n" +
+                ".end annotation\n" +
+                "\n" +
+                ".annotation runtime Lmy/TestAnnotation2;\n" +
+                ".end annotation");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        doTest(smaliClass);
+    }
+
+    public void testFieldAnnotation() {
+        myFixture.addFileToProject("my/TestAnnotation.smali",
+                ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract testBooleanValue()Z\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringValue()Ljava/lang/String;\n" +
+                ".end method");
+
+        myFixture.addFileToProject("my/TestAnnotation2.smali",
+                ".class public interface abstract annotation Lmy/TestAnnotation2;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                "\n" +
+                ".field public myField:I\n" +
+                "    .annotation runtime Lmy/TestAnnotation;\n" +
+                "        testBooleanValue = true\n" +
+                "        testStringValue = \"blah\"\n" +
+                "        testStringArrayValue = {\n" +
+                "            \"blah1\",\n" +
+                "            \"blah2\"\n" +
+                "        }\n" +
+                "    .end annotation\n" +
+                "    .annotation runtime Lmy/TestAnnotation2;\n" +
+                "    .end annotation\n" +
+                ".end field");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        PsiField field = smaliClass.findFieldByName("myField", false);
+        doTest((PsiAnnotationOwner)field);
+    }
+
+    public void testMethodAnnotation() {
+        myFixture.addFileToProject("my/TestAnnotation.smali",
+                ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract testBooleanValue()Z\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringValue()Ljava/lang/String;\n" +
+                ".end method");
+
+        myFixture.addFileToProject("my/TestAnnotation2.smali",
+                ".class public interface abstract annotation Lmy/TestAnnotation2;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                "\n" +
+                ".method public myMethod()V\n" +
+                "    .annotation runtime Lmy/TestAnnotation;\n" +
+                "        testBooleanValue = true\n" +
+                "        testStringValue = \"blah\"\n" +
+                "        testStringArrayValue = {\n" +
+                "            \"blah1\",\n" +
+                "            \"blah2\"\n" +
+                "        }\n" +
+                "    .end annotation\n" +
+                "    .annotation runtime Lmy/TestAnnotation2;\n" +
+                "    .end annotation\n" +
+                ".end method");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        SmaliMethod method = smaliClass.getMethods()[0];
+        doTest(method);
+    }
+
+    public void doTest(PsiAnnotationOwner annotationOwner) {
+        Assert.assertEquals(2, annotationOwner.getAnnotations().length);
+
+        Assert.assertEquals("my.TestAnnotation", annotationOwner.getAnnotations()[0].getQualifiedName());
+        PsiJavaCodeReferenceElement annotationNameRef = annotationOwner.getAnnotations()[0].getNameReferenceElement();
+        Assert.assertNotNull(annotationNameRef);
+        SmaliClass smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
+        Assert.assertNotNull(smaliAnnotationClass);
+        Assert.assertEquals("my.TestAnnotation", smaliAnnotationClass.getQualifiedName());
+
+        Assert.assertEquals("my.TestAnnotation2", annotationOwner.getAnnotations()[1].getQualifiedName());
+        annotationNameRef = annotationOwner.getAnnotations()[1].getNameReferenceElement();
+        Assert.assertNotNull(annotationNameRef);
+        smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
+        Assert.assertNotNull(smaliAnnotationClass);
+        Assert.assertEquals("my.TestAnnotation2", smaliAnnotationClass.getQualifiedName());
+
+        PsiAnnotation smaliAnnotation = annotationOwner.findAnnotation("my.TestAnnotation");
+        Assert.assertNotNull(smaliAnnotation);
+        Assert.assertEquals("my.TestAnnotation", smaliAnnotation.getQualifiedName());
+        PsiAnnotationOwner owner = smaliAnnotation.getOwner();
+        Assert.assertNotNull(owner);
+        Assert.assertSame(annotationOwner, owner);
+        annotationNameRef = smaliAnnotation.getNameReferenceElement();
+        Assert.assertNotNull(annotationNameRef);
+        smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
+        Assert.assertNotNull(smaliAnnotationClass);
+        Assert.assertEquals("my.TestAnnotation", smaliAnnotationClass.getQualifiedName());
+
+        PsiAnnotationParameterList parameterList = smaliAnnotation.getParameterList();
+        Assert.assertNotNull(parameterList);
+        Assert.assertEquals(3, parameterList.getAttributes().length);
+        Assert.assertEquals("testBooleanValue", parameterList.getAttributes()[0].getName());
+        PsiAnnotationMemberValue value = parameterList.getAttributes()[0].getValue();
+        Assert.assertNotNull(value);
+        // TODO: test the values rather than the text
+        Assert.assertEquals("true", value.getText());
+        Assert.assertEquals("testStringValue", parameterList.getAttributes()[1].getName());
+        value = parameterList.getAttributes()[1].getValue();
+        Assert.assertNotNull(value);
+        Assert.assertEquals("\"blah\"", value.getText());
+        Assert.assertEquals("testStringArrayValue", parameterList.getAttributes()[2].getName());
+        value = parameterList.getAttributes()[2].getValue();
+        Assert.assertNotNull(value);
+        // TODO: test the individual values, once the array literal stuff is implemented
+
+        value = smaliAnnotation.findAttributeValue("testBooleanValue");
+        Assert.assertNotNull(value);
+        Assert.assertEquals("true", value.getText());
+
+        value = smaliAnnotation.findAttributeValue("testStringValue");
+        Assert.assertNotNull(value);
+        Assert.assertEquals("\"blah\"", value.getText());
+
+        value = smaliAnnotation.findAttributeValue("testStringArrayValue");
+        Assert.assertNotNull(value);
+
+        // TODO: test findAttributeValue vs findDeclaredAttributeValue for default values
+
+        smaliAnnotation = annotationOwner.findAnnotation("my.TestAnnotation2");
+        Assert.assertNotNull(smaliAnnotation);
+        Assert.assertEquals("my.TestAnnotation2", smaliAnnotation.getQualifiedName());
+        owner = smaliAnnotation.getOwner();
+        Assert.assertNotNull(owner);
+        Assert.assertSame(annotationOwner, owner);
+        annotationNameRef = smaliAnnotation.getNameReferenceElement();
+        Assert.assertNotNull(annotationNameRef);
+        smaliAnnotationClass = (SmaliClass)annotationNameRef.resolve();
+        Assert.assertNotNull(smaliAnnotationClass);
+        Assert.assertEquals("my.TestAnnotation2", smaliAnnotationClass.getQualifiedName());
+
+        parameterList = smaliAnnotation.getParameterList();
+        Assert.assertNotNull(parameterList);
+        Assert.assertEquals(0, parameterList.getAttributes().length);
+    }
+
+    public void testDefaultValue() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("AnnotationWithDefaultValue.smali", "" +
+                ".class public abstract interface annotation LAnnotationWithValues;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract intValue()I\n" +
+                ".end method\n" +
+                "\n" +
+                ".annotation system Ldalvik/annotation/AnnotationDefault;\n" +
+                "    value = .subannotation LAnnotationWithValues;\n" +
+                "                intValue = 4\n" +
+                "            .end subannotation\n" +
+                ".end annotation\n" +
+                "\n");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertNotNull(smaliClass);
+        SmaliMethod method = smaliClass.getMethods()[0];
+        Assert.assertEquals("intValue", method.getName());
+
+        PsiAnnotationMemberValue defaultValue = method.getDefaultValue();
+        Assert.assertTrue(defaultValue instanceof SmaliLiteral);
+        Assert.assertEquals(4, ((SmaliLiteral)defaultValue).getIntegralValue());
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliClassModifierListTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliClassModifierListTest.java
new file mode 100644
index 0000000..a841d0f
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliClassModifierListTest.java
@@ -0,0 +1,194 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.application.ApplicationManager;
+import com.intellij.psi.PsiModifierListOwner;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.smalidea.psi.impl.SmaliAnnotation;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliModifierList;
+import org.junit.Assert;
+
+public class SmaliClassModifierListTest extends LightCodeInsightFixtureTestCase {
+    public void testAllClassAccessFlags() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public final interface abstract synthetic enum annotation Lmy/pkg/blah; " +
+                ".super Ljava/lang/Object;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliModifierList modifierList = smaliClass.getModifierList();
+
+        Assert.assertEquals(AccessFlags.PUBLIC.getValue() |
+                        AccessFlags.FINAL.getValue() |
+                        AccessFlags.INTERFACE.getValue() |
+                        AccessFlags.ABSTRACT.getValue() |
+                        AccessFlags.SYNTHETIC.getValue() |
+                        AccessFlags.ENUM.getValue() |
+                        AccessFlags.ANNOTATION.getValue(),
+                modifierList.getAccessFlags());
+
+        Assert.assertTrue(modifierList.hasModifierProperty("public"));
+        Assert.assertTrue(modifierList.hasModifierProperty("final"));
+        Assert.assertTrue(modifierList.hasModifierProperty("interface"));
+        Assert.assertTrue(modifierList.hasModifierProperty("abstract"));
+        Assert.assertTrue(modifierList.hasModifierProperty("synthetic"));
+        Assert.assertTrue(modifierList.hasModifierProperty("enum"));
+        Assert.assertTrue(modifierList.hasModifierProperty("annotation"));
+
+        Assert.assertTrue(modifierList.hasExplicitModifier("public"));
+        Assert.assertTrue(modifierList.hasExplicitModifier("final"));
+        Assert.assertTrue(modifierList.hasExplicitModifier("interface"));
+        Assert.assertTrue(modifierList.hasExplicitModifier("abstract"));
+        Assert.assertTrue(modifierList.hasExplicitModifier("synthetic"));
+        Assert.assertTrue(modifierList.hasExplicitModifier("enum"));
+        Assert.assertTrue(modifierList.hasExplicitModifier("annotation"));
+    }
+
+    public void testNoClassAccessFlags() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class Lmy/pkg/blah; " +
+                ".super Ljava/lang/Object;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliModifierList modifierList = smaliClass.getModifierList();
+
+        Assert.assertEquals(0, modifierList.getAccessFlags());
+
+        Assert.assertFalse(modifierList.hasModifierProperty("public"));
+        Assert.assertFalse(modifierList.hasModifierProperty("final"));
+        Assert.assertFalse(modifierList.hasModifierProperty("interface"));
+        Assert.assertFalse(modifierList.hasModifierProperty("abstract"));
+        Assert.assertFalse(modifierList.hasModifierProperty("synthetic"));
+        Assert.assertFalse(modifierList.hasModifierProperty("enum"));
+        Assert.assertFalse(modifierList.hasModifierProperty("annotation"));
+
+        Assert.assertFalse(modifierList.hasExplicitModifier("public"));
+        Assert.assertFalse(modifierList.hasExplicitModifier("final"));
+        Assert.assertFalse(modifierList.hasExplicitModifier("interface"));
+        Assert.assertFalse(modifierList.hasExplicitModifier("abstract"));
+        Assert.assertFalse(modifierList.hasExplicitModifier("synthetic"));
+        Assert.assertFalse(modifierList.hasExplicitModifier("enum"));
+        Assert.assertFalse(modifierList.hasExplicitModifier("annotation"));
+    }
+
+    public void testAddClassAccessFlag() {
+        final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah;\n" +
+                ".super Ljava/lang/Object;");
+        myFixture.configureFromExistingVirtualFile(file.getVirtualFile());
+
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+            @Override public void run() {
+                file.getPsiClass().getModifierList().setModifierProperty("final", true);
+            }
+        });
+
+        myFixture.checkResult(
+                ".class public final Lmy/pkg/blah;\n" +
+                ".super Ljava/lang/Object;");
+    }
+
+    public void testRemoveClassAccessFlag() {
+        final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public final Lmy/pkg/blah;\n" +
+                ".super Ljava/lang/Object;");
+        myFixture.configureFromExistingVirtualFile(file.getVirtualFile());
+
+        ApplicationManager.getApplication().runWriteAction(new Runnable() {
+            @Override public void run() {
+                file.getPsiClass().getModifierList().setModifierProperty("final", false);
+            }
+        });
+
+        myFixture.checkResult(
+                ".class public Lmy/pkg/blah;\n" +
+                ".super Ljava/lang/Object;");
+    }
+
+    public void testBasicAnnotation() {
+        final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public final Lmy/pkg/blah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".annotation Lmy/pkg/anno; .end annotation");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliModifierList modifierList = smaliClass.getModifierList();
+
+        SmaliAnnotation[] annotations = modifierList.getAnnotations();
+        Assert.assertEquals(1, annotations.length);
+
+        Assert.assertEquals("my.pkg.anno", annotations[0].getQualifiedName());
+
+        SmaliAnnotation[] applicableAnnotations = modifierList.getApplicableAnnotations();
+        Assert.assertEquals(1, applicableAnnotations.length);
+        Assert.assertEquals(annotations[0], applicableAnnotations[0]);
+    }
+
+    public void testNoAnnotation() {
+        final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public final Lmy/pkg/blah;\n" +
+                ".super Ljava/lang/Object;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliModifierList modifierList = smaliClass.getModifierList();
+
+        // Ensures that the parent of the modifier list is a PsiModifierListOwner
+        // e.g. for code like JavaSuppressionUtil.getInspectionIdsSuppressedInAnnotation,
+        // which assumes the parent is a PsiModifierListOwner
+        Assert.assertTrue(modifierList.getParent() instanceof PsiModifierListOwner);
+
+        Assert.assertEquals(0, modifierList.getAnnotations().length);
+        Assert.assertEquals(0, modifierList.getApplicableAnnotations().length);
+    }
+
+    public void testFindAnnotation() {
+        final SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public final Lmy/pkg/blah;\n" +
+                ".annotation Lanno; .end annotation\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".annotation Lmy/pkg/anno; .end annotation\n" +
+                ".annotation Lmy/pkg/anno2; .end annotation\n" +
+                ".annotation Lmy/pkg/anno3; .end annotation\n");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliModifierList modifierList = smaliClass.getModifierList();
+
+        SmaliAnnotation smaliAnnotation = modifierList.findAnnotation("my.pkg.anno2");
+        Assert.assertNotNull(smaliAnnotation);
+        Assert.assertEquals("my.pkg.anno2", smaliAnnotation.getQualifiedName());
+    }
+
+    // TODO: test modifierList.addAnnotation once implemented
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliClassTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliClassTest.java
new file mode 100644
index 0000000..ba18b66
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliClassTest.java
@@ -0,0 +1,168 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.openapi.module.Module;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.roots.ContentEntry;
+import com.intellij.openapi.roots.LanguageLevelModuleExtension;
+import com.intellij.openapi.roots.ModifiableRootModel;
+import com.intellij.pom.java.LanguageLevel;
+import com.intellij.psi.JavaPsiFacade;
+import com.intellij.psi.PsiClass;
+import com.intellij.psi.PsiClassType;
+import com.intellij.psi.PsiElementFactory;
+import com.intellij.testFramework.LightProjectDescriptor;
+import com.intellij.testFramework.fixtures.DefaultLightProjectDescriptor;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.junit.Assert;
+
+public class SmaliClassTest extends LightCodeInsightFixtureTestCase {
+    public void testName() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+        Assert.assertEquals("my.pkg", smaliClass.getPackageName());
+        Assert.assertEquals("blah", smaliClass.getName());
+    }
+
+    public void testEmptyPackageName() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lblah; .super Ljava/lang/Object;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("blah", smaliClass.getQualifiedName());
+        Assert.assertEquals("", smaliClass.getPackageName());
+    }
+
+    public void testGetSuperclass() {
+        myFixture.addFileToProject("base.smali",
+                ".class public interface Lbase; .super Ljava/lang/Object;");
+
+        myFixture.addFileToProject("iface.smali",
+                ".class public interface Liface; .super Ljava/lang/Object;");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("blah.smali",
+                ".class public Lblah; .super Lbase; .implements Liface;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("blah", smaliClass.getQualifiedName());
+        PsiClass superClass = smaliClass.getSuperClass();
+        Assert.assertNotNull(superClass);
+        Assert.assertEquals("base", smaliClass.getSuperClass().getQualifiedName());
+
+        Assert.assertEquals(2, smaliClass.getSupers().length);
+        Assert.assertEquals("base", smaliClass.getSupers()[0].getQualifiedName());
+        Assert.assertEquals("iface", smaliClass.getSupers()[1].getQualifiedName());
+
+        Assert.assertEquals(2, smaliClass.getSuperTypes().length);
+        Assert.assertEquals("base", smaliClass.getSuperTypes()[0].getCanonicalText());
+        Assert.assertEquals("iface", smaliClass.getSuperTypes()[1].getCanonicalText());
+
+        Assert.assertEquals(1, smaliClass.getInterfaces().length);
+        Assert.assertEquals("iface", smaliClass.getInterfaces()[0].getQualifiedName());
+    }
+
+    public void testGetSuperclassForInterface() {
+        myFixture.addFileToProject("iface.smali",
+                ".class public interface Liface; .super Ljava/lang/Object;");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("blah.smali",
+                ".class public interface Lblah; .super Ljava/lang/Object; .implements Liface;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("blah", smaliClass.getQualifiedName());
+        PsiClass superClass = smaliClass.getSuperClass();
+        Assert.assertNotNull(superClass);
+        Assert.assertEquals("java.lang.Object", smaliClass.getSuperClass().getQualifiedName());
+
+        Assert.assertEquals(2, smaliClass.getSupers().length);
+        Assert.assertEquals("java.lang.Object", smaliClass.getSupers()[0].getQualifiedName());
+        Assert.assertEquals("iface", smaliClass.getSupers()[1].getQualifiedName());
+
+        Assert.assertEquals(1, smaliClass.getSuperTypes().length);
+        Assert.assertEquals("iface", smaliClass.getSuperTypes()[0].getCanonicalText());
+
+        Assert.assertEquals(1, smaliClass.getInterfaces().length);
+        Assert.assertEquals("iface", smaliClass.getInterfaces()[0].getQualifiedName());
+    }
+
+    public void testIsInheritor() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("blah.smali",
+                ".class public Lblah; .super Ljava/lang/Exception;");
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("blah", smaliClass.getQualifiedName());
+
+        PsiElementFactory factory = JavaPsiFacade.getInstance(getProject()).getElementFactory();
+        PsiClassType throwableType = factory.createTypeByFQClassName("java.lang.Throwable", file.getResolveScope());
+        PsiClass throwableClass = throwableType.resolve();
+        Assert.assertNotNull(throwableClass);
+
+        PsiClassType exceptionType = factory.createTypeByFQClassName("java.lang.Exception", file.getResolveScope());
+        PsiClass exceptionClass = exceptionType.resolve();
+        Assert.assertNotNull(exceptionClass);
+
+        PsiClassType objectType = factory.createTypeByFQClassName("java.lang.Object", file.getResolveScope());
+        PsiClass objectClass = objectType.resolve();
+        Assert.assertNotNull(objectClass);
+
+        Assert.assertTrue(smaliClass.isInheritor(exceptionClass, true));
+        Assert.assertTrue(smaliClass.isInheritor(throwableClass, true));
+        Assert.assertTrue(smaliClass.isInheritor(objectClass, true));
+
+        Assert.assertTrue(smaliClass.isInheritorDeep(exceptionClass, null));
+        Assert.assertTrue(smaliClass.isInheritorDeep(throwableClass, null));
+        Assert.assertTrue(smaliClass.isInheritorDeep(objectClass, null));
+
+        Assert.assertTrue(smaliClass.isInheritor(exceptionClass, false));
+        Assert.assertFalse(smaliClass.isInheritor(throwableClass, false));
+        Assert.assertFalse(smaliClass.isInheritor(objectClass, false));
+    }
+
+    @NotNull @Override protected LightProjectDescriptor getProjectDescriptor() {
+        return new DefaultLightProjectDescriptor() {
+            public Sdk getSdk() {
+                return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+            }
+
+            public void configureModule(Module module, ModifiableRootModel model, ContentEntry contentEntry) {
+                model.getModuleExtension(LanguageLevelModuleExtension.class).setLanguageLevel(LanguageLevel.JDK_1_6);
+            }
+        };
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliClassTypeElementTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliClassTypeElementTest.java
new file mode 100644
index 0000000..434c959
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliClassTypeElementTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliClassType;
+import org.jf.smalidea.psi.impl.SmaliClassTypeElement;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.junit.Assert;
+
+public class SmaliClassTypeElementTest extends LightCodeInsightFixtureTestCase {
+    public void testGetType() {
+        myFixture.addFileToProject("my/blarg.smali",
+                ".class public Lmy/blarg; " +
+                ".super Ljava/lang/Object;");
+
+        String text = ".class public Lmy/pkg/blah; " +
+                      ".super Lmy/bl<ref>arg;";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                text.replace("<ref>", ""));
+
+        SmaliClassTypeElement typeElement =
+                (SmaliClassTypeElement)file.findReferenceAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(typeElement);
+        SmaliClassType type = typeElement.getType();
+
+        Assert.assertEquals("blarg", typeElement.getName());
+        Assert.assertEquals("my.blarg", typeElement.getCanonicalText());
+        Assert.assertEquals("blarg", type.getClassName());
+        Assert.assertEquals("my.blarg", type.getCanonicalText());
+
+        SmaliClass resolvedClass = (SmaliClass)typeElement.resolve();
+        Assert.assertNotNull(resolvedClass);
+        Assert.assertEquals("my.blarg", resolvedClass.getQualifiedName());
+
+        resolvedClass = (SmaliClass)type.resolve();
+        Assert.assertNotNull(resolvedClass);
+        Assert.assertEquals("my.blarg", resolvedClass.getQualifiedName());
+    }
+
+    public void testSimpleInnerClass() {
+        myFixture.addFileToProject("Outer.java", "" +
+                "public class Outer {" +
+                "   public static class Inner {" +
+                "   }" +
+                "}");
+
+        String text = ".class public Lsmali; " +
+                ".super LOuter$In<ref>ner;";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("smali.smali", text.replace("<ref>", ""));
+
+        SmaliClassTypeElement typeElement =
+                (SmaliClassTypeElement)file.findReferenceAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(typeElement);
+        SmaliClassType type = typeElement.getType();
+
+        Assert.assertEquals("Outer.Inner", typeElement.getQualifiedName());
+        Assert.assertEquals("Outer.Inner", type.getCanonicalText());
+    }
+
+    public void testInnerClassWithPackage() {
+        myFixture.addFileToProject("my/Outer.java", "" +
+                "package my;" +
+                "public class Outer {" +
+                "   public static class Inner {" +
+                "   }" +
+                "}");
+
+        String text = ".class public Lsmali; " +
+                ".super Lmy/Outer$In<ref>ner;";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("smali.smali", text.replace("<ref>", ""));
+
+        SmaliClassTypeElement typeElement =
+                (SmaliClassTypeElement)file.findReferenceAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(typeElement);
+        SmaliClassType type = typeElement.getType();
+
+        Assert.assertEquals("my.Outer.Inner", typeElement.getQualifiedName());
+        Assert.assertEquals("my.Outer.Inner", type.getCanonicalText());
+    }
+
+    public void testComplexInnerClass() {
+        myFixture.addFileToProject("my/Outer$blah.java", "" +
+                "package my;" +
+                "public class Outer$blah {" +
+                "   public static class Inner {" +
+                "   }" +
+                "   public static class Inner$blah {" +
+                "   }" +
+                "}");
+
+        String text = ".class public Lsmali; " +
+                ".super Lmy/Outer$blah$In<ref>ner$blah;";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("smali.smali", text.replace("<ref>", ""));
+
+        SmaliClassTypeElement typeElement =
+                (SmaliClassTypeElement)file.findReferenceAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(typeElement);
+        SmaliClassType type = typeElement.getType();
+
+        Assert.assertEquals("my.Outer$blah.Inner$blah", typeElement.getQualifiedName());
+        Assert.assertEquals("my.Outer$blah.Inner$blah", type.getCanonicalText());
+
+        text = ".class public Lsmali2; " +
+                ".super Lmy/Outer$blah$In<ref>ner;";
+
+        file = (SmaliFile)myFixture.addFileToProject("smali2.smali", text.replace("<ref>", ""));
+
+        typeElement = (SmaliClassTypeElement)file.findReferenceAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(typeElement);
+        type = typeElement.getType();
+
+        Assert.assertEquals("my.Outer$blah.Inner", typeElement.getQualifiedName());
+        Assert.assertEquals("my.Outer$blah.Inner", type.getCanonicalText());
+    }
+
+    public void testInnerClassTrailingDollar() {
+        myFixture.addFileToProject("my/Outer$blah.java", "" +
+                "package my;" +
+                "public class Outer$ {" +
+                "   public static class Inner$ {" +
+                "   }" +
+                "}");
+
+        String text = ".class public Lsmali; " +
+                ".super Lmy/Outer$$In<ref>ner$;";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("smali.smali", text.replace("<ref>", ""));
+
+        SmaliClassTypeElement typeElement =
+                (SmaliClassTypeElement)file.findReferenceAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(typeElement);
+        SmaliClassType type = typeElement.getType();
+
+        Assert.assertEquals("my.Outer$.Inner$", typeElement.getQualifiedName());
+        Assert.assertEquals("my.Outer$.Inner$", type.getCanonicalText());
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java
new file mode 100644
index 0000000..c61d3e4
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliCodeFragmentFactoryTest.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.google.common.collect.Sets;
+import com.intellij.codeInsight.CodeInsightTestCase;
+import com.intellij.codeInsight.completion.CodeCompletionHandlerBase;
+import com.intellij.codeInsight.completion.CompletionType;
+import com.intellij.codeInsight.daemon.DaemonCodeAnalyzer;
+import com.intellij.codeInsight.lookup.LookupElement;
+import com.intellij.codeInsight.lookup.LookupManager;
+import com.intellij.debugger.NoDataException;
+import com.intellij.debugger.engine.evaluation.CodeFragmentKind;
+import com.intellij.debugger.engine.evaluation.TextWithImportsImpl;
+import com.intellij.openapi.editor.Editor;
+import com.intellij.openapi.editor.impl.EditorImpl;
+import com.intellij.openapi.fileEditor.FileEditorManager;
+import com.intellij.openapi.fileEditor.OpenFileDescriptor;
+import com.intellij.openapi.projectRoots.Sdk;
+import com.intellij.openapi.projectRoots.impl.JavaAwareProjectJdkTableImpl;
+import com.intellij.openapi.vfs.VirtualFile;
+import com.intellij.psi.JavaCodeFragment;
+import com.intellij.psi.PsiDocumentManager;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl;
+import org.jetbrains.annotations.NotNull;
+import org.jf.smalidea.debugging.SmaliCodeFragmentFactory;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.junit.Assert;
+
+import java.util.HashSet;
+import java.util.List;
+
+public class SmaliCodeFragmentFactoryTest extends CodeInsightTestCase {
+    private static final String completionTestClass =
+            ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                    ".method public getRandomParentType(I)I\n" +
+                    "    .registers 4\n" +
+                    "    .param p1, \"edge\"    # I\n" +
+                    "\n" +
+                    "    .prologue\n" +
+                    "    const/4 v1, 0x2\n" + // 0
+                    "\n" +
+                    "    .line 179\n" +
+                    "    if-nez p1, :cond_5\n" +
+                    "\n" +
+                    "    move v0, v1\n" + // 2
+                    "\n" +
+                    "    .line 185\n" +
+                    "    :goto_4\n" +
+                    "    return v0\n" +
+                    "\n" +
+                    "    .line 182\n" +
+                    "    :cond_5\n" +
+                    "    if-ne p1, v1, :cond_f\n" +
+                    "\n" +
+                    "    .line 183\n" +
+                    "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                    "\n" +
+                    "    const/4 v1, 0x3\n" + // 6
+                    "\n" +
+                    "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                    "\n" +
+                    "    move-result v0\n" +
+                    "\n" +
+                    "    goto :goto_4\n" +
+                    "\n" +
+                    "    .line 185\n" +
+                    "    :cond_f\n" +
+                    "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                    "\n" +
+                    "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                    "\n" +
+                    "    move-result v0\n" +
+                    "\n" +
+                    "    goto :goto_4\n" +
+                    ".end method";
+
+    public void testCompletion() throws NoDataException {
+        SmaliFile smaliFile = (SmaliFile)configureByText(SmaliFileType.INSTANCE, completionTestClass);
+
+        PsiElement context = smaliFile.getPsiClass().getMethods()[0].getInstructions().get(0);
+        assertCompletionContains("v", context, new String[] {"v2", "v3"}, new String[] {"v0", "v1", "p0", "p1"});
+        assertCompletionContains("p", context, new String[] {"p0", "p1"}, new String[] {"v0", "v1", "v2", "v3"});
+
+        context = smaliFile.getPsiClass().getMethods()[0].getInstructions().get(2);
+        assertCompletionContains("v", context, new String[] {"v1", "v2", "v3"}, new String[] {"v0", "p0", "p1"});
+        assertCompletionContains("p", context, new String[] {"p0", "p1"}, new String[] {"v0", "v1", "v2", "v3"});
+
+        context = smaliFile.getPsiClass().getMethods()[0].getInstructions().get(6);
+        assertCompletionContains("v", context, new String[] {"v0", "v1", "v2", "v3"}, new String[] {"p0", "p1"});
+        assertCompletionContains("p", context, new String[] {"p0", "p1"}, new String[] {});
+    }
+
+    private static final String registerTypeTestText = "" +
+            ".class public LRegisterTypeTest;\n" +
+            ".super Ljava/lang/Object;\n" +
+            "\n" +
+            "# virtual methods\n" +
+            ".method public blah()V\n" +
+            "    .registers 6\n" +
+            "\n" +
+            "    .prologue\n" +
+            "    const/16 v3, 0xa\n" +
+            "\n" +
+            "    .line 7\n" +
+            "    new-instance v0, Ljava/util/Random;\n" +
+            "\n" +
+            "    invoke-direct {v0}, Ljava/util/Random;-><init>()V\n" +
+            "\n" +
+            "    .line 9\n" +
+            "    invoke-virtual {v0, v3}, Ljava/util/Random;->nextInt(I)I\n" +
+            "\n" +
+            "    move-result v1\n" +
+            "\n" +
+            "    const/4 v2, 0x5\n" +
+            "\n" +
+            "    if-le v1, v2, :cond_26\n" +
+            "\n" +
+            "    .line 10\n" +
+            "    new-instance v1, Ljava/security/SecureRandom;\n" +
+            "\n" +
+            "    invoke-direct {v1}, Ljava/security/SecureRandom;-><init>()V\n" +
+            "\n" +
+            "    .line 14\n" +
+            "    :goto_13\n" +
+            "    sget-o<ref>bject v2, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" +
+            "\n" +
+            "    invoke-virtual {v1, v3}, Ljava/util/Random;->nextInt(I)I\n" +
+            "\n" +
+            "    move-result v1\n" +
+            "\n" +
+            "    invoke-virtual {v2, v1}, Ljava/io/PrintStream;->println(I)V\n" +
+            "\n" +
+            "    .line 15\n" +
+            "    sget-object v1, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" +
+            "\n" +
+            "    invoke-virtual {v0}, Ljava/lang/Object;->toString()Ljava/lang/String;\n" +
+            "\n" +
+            "    move-result-object v0\n" +
+            "\n" +
+            "    invoke-virtual {v1, v0}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" +
+            "\n" +
+            "    .line 16\n" +
+            "    return-void\n" +
+            "\n" +
+            "    .line 12\n" +
+            "    :cond_26\n" +
+            "    invoke-virtual {p0}, LRegisterTypeTest;->getSerializable()Ljava/io/Serializable;\n" +
+            "\n" +
+            "    move-result-object v1\n" +
+            "\n" +
+            "    move-object v4, v1\n" +
+            "\n" +
+            "    move-object v1, v0\n" +
+            "\n" +
+            "    move-object v0, v4\n" +
+            "\n" +
+            "    goto :goto_13\n" +
+            ".end method\n" +
+            "\n" +
+            ".method public getSerializable()Ljava/io/Serializable;\n" +
+            "    .registers 2\n" +
+            "\n" +
+            "    .prologue\n" +
+            "    .line 19\n" +
+            "    new-instance v0, Ljava/util/Random;\n" +
+            "\n" +
+            "    invoke-direct {v0}, Ljava/util/Random;-><init>()V\n" +
+            "\n" +
+            "    return-object v0\n" +
+            ".end method\n";
+
+    public void testRegisterType() throws NoDataException {
+        SmaliFile smaliFile = (SmaliFile)configureByText(SmaliFileType.INSTANCE,
+                registerTypeTestText.replace("<ref>", ""));
+
+        int refOffset = registerTypeTestText.indexOf("<ref>");
+
+        PsiElement context = smaliFile.findElementAt(refOffset);
+        assertVariableType(context.getParent(), "v1", "java.util.Random");
+        assertVariableType(context.getParent(), "v0", "java.io.Serializable");
+    }
+
+    public void testUnknownClass() {
+        String modifiedText = registerTypeTestText.replace("Random", "Rnd");
+        SmaliFile smaliFile = (SmaliFile)configureByText(SmaliFileType.INSTANCE,
+                modifiedText.replace("<ref>", ""));
+
+        int refOffset = modifiedText.indexOf("<ref>");
+
+        PsiElement context = smaliFile.findElementAt(refOffset);
+        assertVariableType(context.getParent(), "v1", "java.lang.Object");
+        assertVariableType(context.getParent(), "v0", "java.lang.Object");
+    }
+
+    private void assertCompletionContains(String completionText, PsiElement context, String[] expectedItems,
+                                          String[] disallowedItems) {
+        SmaliCodeFragmentFactory codeFragmentFactory = new SmaliCodeFragmentFactory();
+        JavaCodeFragment fragment = codeFragmentFactory.createCodeFragment(
+                new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, completionText),
+                context, getProject());
+
+        Editor editor = createEditor(fragment.getVirtualFile());
+        editor.getCaretModel().moveToOffset(completionText.length());
+
+        new CodeCompletionHandlerBase(CompletionType.BASIC).invokeCompletion(getProject(), editor);
+        List<LookupElement> elements = LookupManager.getInstance(getProject()).getActiveLookup().getItems();
+
+        HashSet expectedSet = Sets.newHashSet(expectedItems);
+        HashSet disallowedSet = Sets.newHashSet(disallowedItems);
+
+        for (LookupElement element: elements) {
+            expectedSet.remove(element.toString());
+            Assert.assertFalse(disallowedSet.contains(element.toString()));
+        }
+
+        Assert.assertTrue(expectedSet.size() == 0);
+    }
+
+    private void assertVariableType(PsiElement context, String variableName, String expectedType) {
+        SmaliCodeFragmentFactory codeFragmentFactory = new SmaliCodeFragmentFactory();
+        JavaCodeFragment fragment = codeFragmentFactory.createCodeFragment(
+                new TextWithImportsImpl(CodeFragmentKind.EXPRESSION, variableName),
+                context, getProject());
+
+        Editor editor = createEditor(fragment.getVirtualFile());
+        editor.getCaretModel().moveToOffset(1);
+
+        PsiElement element = fragment.findElementAt(0);
+        Assert.assertTrue(element.getParent() instanceof PsiReferenceExpressionImpl);
+        PsiReferenceExpressionImpl reference = (PsiReferenceExpressionImpl)element.getParent();
+        Assert.assertEquals(expectedType, reference.getType().getCanonicalText());
+    }
+
+    protected Editor createEditor(@NotNull VirtualFile file) {
+        PsiDocumentManager.getInstance(getProject()).commitAllDocuments();
+        Editor editor = FileEditorManager.getInstance(getProject()).openTextEditor(
+                new OpenFileDescriptor(getProject(), file, 0), false);
+        DaemonCodeAnalyzer.getInstance(getProject()).restart();
+
+        ((EditorImpl)editor).setCaretActive();
+        return editor;
+    }
+
+    @Override
+    protected Sdk getTestProjectJdk() {
+        return JavaAwareProjectJdkTableImpl.getInstanceEx().getInternalJdk();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliFieldTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliFieldTest.java
new file mode 100644
index 0000000..05e46bc
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliFieldTest.java
@@ -0,0 +1,250 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.PsiField;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.psi.PsiTypeElement;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliField;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliModifierList;
+import org.junit.Assert;
+
+public class SmaliFieldTest extends LightCodeInsightFixtureTestCase {
+    public void testBasicField() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".field public myField:I");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        SmaliField[] fields = smaliClass.getFields();
+        Assert.assertEquals(1, fields.length);
+        Assert.assertEquals("myField", fields[0].getName());
+        Assert.assertTrue(fields[0].getType() instanceof PsiPrimitiveType);
+        Assert.assertEquals("int", fields[0].getType().getCanonicalText());
+        PsiTypeElement typeElement = fields[0].getTypeElement();
+        Assert.assertNotNull("I", typeElement);
+        Assert.assertEquals("I", typeElement.getText());
+
+        SmaliModifierList modifierList = fields[0].getModifierList();
+        Assert.assertNotNull(modifierList);
+        Assert.assertEquals(AccessFlags.PUBLIC.getValue(), modifierList.getAccessFlags());
+        Assert.assertTrue(modifierList.hasExplicitModifier("public"));
+        Assert.assertTrue(modifierList.hasModifierProperty("public"));
+        Assert.assertTrue(fields[0].hasModifierProperty("public"));
+
+        PsiField[] psifields = smaliClass.getAllFields();
+        Assert.assertEquals(1, psifields.length);
+        Assert.assertEquals("myField", psifields[0].getName());
+
+        PsiField field = smaliClass.findFieldByName("myField", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField", field.getName());
+
+        field = smaliClass.findFieldByName("nonExistantField", true);
+        Assert.assertNull(field);
+        field = smaliClass.findFieldByName("nonExistantField", false);
+        Assert.assertNull(field);
+    }
+
+    public void testSmaliSuperField() {
+        myFixture.addFileToProject("my/pkg/base.smali",
+                ".class public Lmy/pkg/base; .super Ljava/lang/Object;\n" +
+                ".field public baseField:I");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Lmy/pkg/base;\n" +
+                ".field public myField:I");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        PsiField[] fields = smaliClass.getFields();
+        Assert.assertEquals(1, fields.length);
+        Assert.assertEquals("myField", fields[0].getName());
+
+        fields = smaliClass.getAllFields();
+        Assert.assertEquals(2, fields.length);
+
+        Assert.assertTrue(fields[0].getName().equals("myField") || fields[1].getName().equals("myField"));
+        Assert.assertTrue(fields[0].getName().equals("baseField") || fields[1].getName().equals("baseField"));
+
+        PsiField field = smaliClass.findFieldByName("myField", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField", field.getName());
+
+        field = smaliClass.findFieldByName("myField", false);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField", field.getName());
+
+        field = smaliClass.findFieldByName("baseField", false);
+        Assert.assertNull(field);
+
+        field = smaliClass.findFieldByName("baseField", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("baseField", field.getName());
+
+        field = smaliClass.findFieldByName("nonExistantField", true);
+        Assert.assertNull(field);
+        field = smaliClass.findFieldByName("nonExistantField", false);
+        Assert.assertNull(field);
+    }
+
+    public void testJavaSuperField() {
+        myFixture.addFileToProject("my/pkg/base.java",
+                "package my.pkg; public class base { public int baseField; }");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Lmy/pkg/base;\n" +
+                        ".field public myField:I");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        PsiField[] fields = smaliClass.getFields();
+        Assert.assertEquals(1, fields.length);
+        Assert.assertEquals("myField", fields[0].getName());
+
+        fields = smaliClass.getAllFields();
+        Assert.assertEquals(2, fields.length);
+
+        Assert.assertTrue(fields[0].getName().equals("myField") || fields[1].getName().equals("myField"));
+        Assert.assertTrue(fields[0].getName().equals("baseField") || fields[1].getName().equals("baseField"));
+
+        PsiField field = smaliClass.findFieldByName("myField", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField", field.getName());
+
+        field = smaliClass.findFieldByName("myField", false);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField", field.getName());
+
+        field = smaliClass.findFieldByName("baseField", false);
+        Assert.assertNull(field);
+
+        field = smaliClass.findFieldByName("baseField", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("baseField", field.getName());
+
+        field = smaliClass.findFieldByName("nonExistantField", true);
+        Assert.assertNull(field);
+        field = smaliClass.findFieldByName("nonExistantField", false);
+        Assert.assertNull(field);
+    }
+
+    public void testMultipleField() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".field public myField:I\n" +
+                ".field public myField2:Ljava/lang/String;\n" +
+                ".field public myField3:[Ljava/lang/String;\n" +
+                ".field public myField4:[[[Ljava/lang/String;\n");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        SmaliField[] fields = smaliClass.getFields();
+        Assert.assertEquals(4, fields.length);
+        Assert.assertEquals("myField", fields[0].getName());
+        Assert.assertEquals("myField2", fields[1].getName());
+        Assert.assertEquals("myField3", fields[2].getName());
+        Assert.assertEquals("myField4", fields[3].getName());
+        Assert.assertEquals("int", fields[0].getType().getCanonicalText());
+        Assert.assertEquals("java.lang.String", fields[1].getType().getCanonicalText());
+        Assert.assertEquals("java.lang.String[]", fields[2].getType().getCanonicalText());
+        Assert.assertEquals("java.lang.String[][][]", fields[3].getType().getCanonicalText());
+
+        PsiField field = smaliClass.findFieldByName("myField", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField", field.getName());
+
+        field = smaliClass.findFieldByName("myField2", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField2", field.getName());
+
+        field = smaliClass.findFieldByName("myField3", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField3", field.getName());
+
+        field = smaliClass.findFieldByName("myField4", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField4", field.getName());
+
+        field = smaliClass.findFieldByName("nonExistantField", true);
+        Assert.assertNull(field);
+        field = smaliClass.findFieldByName("nonExistantField", false);
+        Assert.assertNull(field);
+    }
+
+    public void testFieldAnnotations() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".field public myField:I");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertEquals("my.pkg.blah", smaliClass.getQualifiedName());
+
+        SmaliField[] fields = smaliClass.getFields();
+        Assert.assertEquals(1, fields.length);
+        Assert.assertEquals("myField", fields[0].getName());
+        Assert.assertTrue(fields[0].getType() instanceof PsiPrimitiveType);
+        Assert.assertEquals("int", fields[0].getType().getCanonicalText());
+        PsiTypeElement typeElement = fields[0].getTypeElement();
+        Assert.assertNotNull("I", typeElement);
+        Assert.assertEquals("I", typeElement.getText());
+
+        SmaliModifierList modifierList = fields[0].getModifierList();
+        Assert.assertNotNull(modifierList);
+        Assert.assertEquals(AccessFlags.PUBLIC.getValue(), modifierList.getAccessFlags());
+        Assert.assertTrue(modifierList.hasExplicitModifier("public"));
+        Assert.assertTrue(modifierList.hasModifierProperty("public"));
+        Assert.assertTrue(fields[0].hasModifierProperty("public"));
+
+        PsiField[] psifields = smaliClass.getAllFields();
+        Assert.assertEquals(1, psifields.length);
+        Assert.assertEquals("myField", psifields[0].getName());
+
+        PsiField field = smaliClass.findFieldByName("myField", true);
+        Assert.assertNotNull(field);
+        Assert.assertEquals("myField", field.getName());
+
+        field = smaliClass.findFieldByName("nonExistantField", true);
+        Assert.assertNull(field);
+        field = smaliClass.findFieldByName("nonExistantField", false);
+        Assert.assertNull(field);
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliFileTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliFileTest.java
new file mode 100644
index 0000000..e55bdd3
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliFileTest.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.junit.Assert;
+
+public class SmaliFileTest extends LightCodeInsightFixtureTestCase {
+    public void testFile() {
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;");
+
+        Assert.assertEquals(SmaliFileType.INSTANCE, file.getFileType());
+
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertNotNull(smaliClass);
+
+        SmaliClass[] smaliClasses = file.getClasses();
+        Assert.assertNotNull(smaliClasses);
+        Assert.assertEquals(1, smaliClasses.length);
+        Assert.assertEquals(smaliClass, smaliClasses[0]);
+
+        String packageName = file.getPackageName();
+        Assert.assertEquals("my.pkg", packageName);
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliFileTypeTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliFileTypeTest.java
new file mode 100644
index 0000000..c2d7336
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliFileTypeTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.PsiFile;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.junit.Assert;
+
+/**
+ * Tests that .smali files are properly detected
+ */
+public class SmaliFileTypeTest extends LightCodeInsightFixtureTestCase {
+    public void testImportSmaliClass() {
+        PsiFile file = myFixture.addFileToProject("my/pkg/blah.smali", ".class public Lmy/pkg/blah; .super Ljava/lang/Object;");
+        Assert.assertEquals(SmaliFileType.INSTANCE, file.getVirtualFile().getFileType());
+        Assert.assertEquals(SmaliFileType.INSTANCE, file.getFileType());
+        Assert.assertEquals(SmaliLanguage.INSTANCE, file.getLanguage());
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliImplementsExtendsTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliImplementsExtendsTest.java
new file mode 100644
index 0000000..597cf70
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliImplementsExtendsTest.java
@@ -0,0 +1,124 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.PsiClass;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliExtendsList;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliImplementsList;
+import org.junit.Assert;
+
+public class SmaliImplementsExtendsTest extends LightCodeInsightFixtureTestCase {
+    public void testNormalClass() {
+        myFixture.addFileToProject("my/pkg/base.smali",
+                ".class public Lmy/pkg/base; .super Ljava/lang/Object;");
+        myFixture.addFileToProject("my/pkg/iface.smali",
+                ".class public Lmy/pkg/iface; .super Ljava/lang/Object;");
+        myFixture.addFileToProject("my/pkg/iface2.smali",
+                ".class public Lmy/pkg/iface2; .super Ljava/lang/Object;");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public Lmy/pkg/blah; .implements Lmy/pkg/iface; .super Lmy/pkg/base; " +
+                ".implements Lmy/pkg/iface2;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliExtendsList extendsList = smaliClass.getExtendsList();
+        Assert.assertEquals(1, extendsList.getReferencedTypes().length);
+        Assert.assertEquals("my.pkg.base", extendsList.getReferencedTypes()[0].getCanonicalText());
+        Assert.assertEquals(1, extendsList.getReferenceNames().length);
+        Assert.assertEquals("my.pkg.base", extendsList.getReferenceNames()[0]);
+        Assert.assertEquals(1, smaliClass.getExtendsListTypes().length);
+        Assert.assertEquals("my.pkg.base", smaliClass.getExtendsListTypes()[0].getCanonicalText());
+
+        PsiClass resolvedSuper = extendsList.getReferencedTypes()[0].resolve();
+        Assert.assertNotNull(resolvedSuper);
+        Assert.assertEquals("my.pkg.base", resolvedSuper.getQualifiedName());
+
+        SmaliImplementsList implementsList = smaliClass.getImplementsList();
+        Assert.assertEquals(2, implementsList.getReferencedTypes().length);
+        Assert.assertEquals("my.pkg.iface", implementsList.getReferencedTypes()[0].getCanonicalText());
+        Assert.assertEquals("my.pkg.iface2", implementsList.getReferencedTypes()[1].getCanonicalText());
+        Assert.assertEquals(2, implementsList.getReferenceNames().length);
+        Assert.assertEquals("my.pkg.iface", implementsList.getReferenceNames()[0]);
+        Assert.assertEquals("my.pkg.iface2", implementsList.getReferenceNames()[1]);
+        Assert.assertEquals(2, smaliClass.getImplementsListTypes().length);
+        Assert.assertEquals("my.pkg.iface", smaliClass.getImplementsListTypes()[0].getCanonicalText());
+        Assert.assertEquals("my.pkg.iface2", smaliClass.getImplementsListTypes()[1].getCanonicalText());
+
+        PsiClass resolvedInterface = implementsList.getReferencedTypes()[0].resolve();
+        Assert.assertNotNull(resolvedInterface);
+        Assert.assertEquals("my.pkg.iface", resolvedInterface.getQualifiedName());
+
+        resolvedInterface = implementsList.getReferencedTypes()[1].resolve();
+        Assert.assertNotNull(resolvedInterface);
+        Assert.assertEquals("my.pkg.iface2", resolvedInterface.getQualifiedName());
+    }
+
+    public void testInterface() {
+        myFixture.addFileToProject("my/pkg/iface.smali",
+                ".class public Lmy/pkg/iface; .super Ljava/lang/Object;");
+        myFixture.addFileToProject("my/pkg/iface2.smali",
+                ".class public Lmy/pkg/iface2; .super Ljava/lang/Object;");
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                ".class public interface Lmy/pkg/blah; .implements Lmy/pkg/iface; .super Ljava/lang/Object; " +
+                        ".implements Lmy/pkg/iface2;");
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliExtendsList extendsList = smaliClass.getExtendsList();
+
+        Assert.assertEquals(2, extendsList.getReferencedTypes().length);
+        Assert.assertEquals("my.pkg.iface", extendsList.getReferencedTypes()[0].getCanonicalText());
+        Assert.assertEquals("my.pkg.iface2", extendsList.getReferencedTypes()[1].getCanonicalText());
+        Assert.assertEquals(2, extendsList.getReferenceNames().length);
+        Assert.assertEquals("my.pkg.iface", extendsList.getReferenceNames()[0]);
+        Assert.assertEquals("my.pkg.iface2", extendsList.getReferenceNames()[1]);
+        Assert.assertEquals(2, smaliClass.getExtendsListTypes().length);
+        Assert.assertEquals("my.pkg.iface", smaliClass.getExtendsListTypes()[0].getCanonicalText());
+        Assert.assertEquals("my.pkg.iface2", smaliClass.getExtendsListTypes()[1].getCanonicalText());
+
+        PsiClass resolvedInterface = extendsList.getReferencedTypes()[0].resolve();
+        Assert.assertNotNull(resolvedInterface);
+        Assert.assertEquals("my.pkg.iface", resolvedInterface.getQualifiedName());
+
+        resolvedInterface = extendsList.getReferencedTypes()[1].resolve();
+        Assert.assertNotNull(resolvedInterface);
+        Assert.assertEquals("my.pkg.iface2", resolvedInterface.getQualifiedName());
+
+        SmaliImplementsList implementsList = smaliClass.getImplementsList();
+        Assert.assertEquals(0, implementsList.getReferencedTypes().length);
+        Assert.assertEquals(0, implementsList.getReferenceNames().length);
+        Assert.assertEquals(0, smaliClass.getImplementsListTypes().length);
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliInstructionTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliInstructionTest.java
new file mode 100644
index 0000000..37ea555
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliInstructionTest.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.dexlib2.Opcode;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+import org.junit.Assert;
+
+public class SmaliInstructionTest extends LightCodeInsightFixtureTestCase {
+    public void testSingleInstruction() {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method blah(IJLjava/lang/String;)V\n" +
+                        "    .locals 0\n" +
+                        "    r<ref>eturn-void\n" +
+                        ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                text.replace("<ref>", ""));
+
+        PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(leafElement);
+        SmaliInstruction instructionElement = (SmaliInstruction)leafElement.getParent();
+        Assert.assertNotNull(instructionElement);
+
+        Assert.assertEquals(Opcode.RETURN_VOID, instructionElement.getOpcode());
+        Assert.assertEquals(0, instructionElement.getOffset());
+    }
+
+    public void testMultipleInstructions() {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method blah(IJLjava/lang/String;)I\n" +
+                        "    .locals 1\n" +
+                        "    const v0, 1234\n" +
+                        "    r<ref>eturn v0\n" +
+                        ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                text.replace("<ref>", ""));
+
+        PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(leafElement);
+        SmaliInstruction instructionElement = (SmaliInstruction)leafElement.getParent();
+        Assert.assertNotNull(instructionElement);
+
+        Assert.assertEquals(Opcode.RETURN, instructionElement.getOpcode());
+        Assert.assertEquals(6, instructionElement.getOffset());
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliLabelReferenceTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliLabelReferenceTest.java
new file mode 100644
index 0000000..628cf0f
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliLabelReferenceTest.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.testFramework.ResolveTestCase;
+import org.jf.dexlib2.Opcode;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+import org.jf.smalidea.psi.impl.SmaliLabel;
+import org.jf.smalidea.psi.impl.SmaliLabelReference;
+import org.junit.Assert;
+
+public class SmaliLabelReferenceTest extends ResolveTestCase {
+
+    public void testLabelReference() throws Exception {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method public getRandomParentType(I)I\n" +
+                        "    .registers 4\n" +
+                        "    .param p1, \"edge\"    # I\n" +
+                        "\n" +
+                        "    .prologue\n" +
+                        "    const/4 v1, 0x2\n" +
+                        "\n" +
+                        "    .line 179\n" +
+                        "    if-nez p1, :cond_5\n" +
+                        "\n" +
+                        "    move v0, v1\n" +
+                        "\n" +
+                        "    .line 185\n" +
+                        "    :goto_4\n" +
+                        "    return v0\n" +
+                        "\n" +
+                        "    .line 182\n" +
+                        "    :cond_5\n" +
+                        "    if-ne p1, v1, :cond_f\n" +
+                        "\n" +
+                        "    .line 183\n" +
+                        "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                        "\n" +
+                        "    const/4 v1, 0x3\n" +
+                        "\n" +
+                        "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                        "\n" +
+                        "    move-result v0\n" +
+                        "\n" +
+                        "    goto :goto_4\n" +
+                        "\n" +
+                        "    .line 185\n" +
+                        "    :cond_f\n" +
+                        "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                        "\n" +
+                        "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                        "\n" +
+                        "    move-result v0\n" +
+                        "\n" +
+                        "    goto :go<ref>to_4\n" +
+                        ".end method";;
+
+        SmaliLabelReference labelReference = (SmaliLabelReference)configureByFileText(text, "blah.smali");
+
+        Assert.assertNotNull(labelReference);
+        Assert.assertEquals("goto_4", labelReference.getName());
+
+        SmaliLabel resolvedLabel = labelReference.resolve();
+        Assert.assertNotNull(resolvedLabel);
+        Assert.assertEquals("goto_4", resolvedLabel.getName());
+
+        SmaliInstruction nextInstruction = resolvedLabel.findNextSiblingByClass(SmaliInstruction.class);
+        Assert.assertNotNull(nextInstruction);
+        Assert.assertEquals(8, nextInstruction.getOffset());
+        Assert.assertEquals(Opcode.RETURN, nextInstruction.getOpcode());
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliLexerTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliLexerTest.java
new file mode 100644
index 0000000..8383f96
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliLexerTest.java
@@ -0,0 +1,204 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.lexer.Lexer;
+import com.intellij.testFramework.LexerTestCase;
+
+import java.util.Random;
+
+/**
+ * This is mostly just a smoke test to make sure the lexer is working. The lexer itself has its
+ * own tests in the smali module
+ */
+public class SmaliLexerTest extends LexerTestCase {
+    public void testHelloWorld() {
+        String text =
+                ".class public LHelloWorld;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method public static main([Ljava/lang/String;)V\n" +
+                "    .registers 2\n" +
+                "    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" +
+                "    const-string v1, \"Hello World!\"\n" +
+                "    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" +
+                "    return-void\n" +
+                ".end method";
+
+        doTest(text,
+                "CLASS_DIRECTIVE ('.class')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "ACCESS_SPEC ('public')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "CLASS_DESCRIPTOR ('LHelloWorld;')\n" +
+                "WHITE_SPACE ('\\n')\n" +
+                "SUPER_DIRECTIVE ('.super')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "CLASS_DESCRIPTOR ('Ljava/lang/Object;')\n" +
+                "WHITE_SPACE ('\\n')\n" +
+                "METHOD_DIRECTIVE ('.method')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "ACCESS_SPEC ('public')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "ACCESS_SPEC ('static')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "SIMPLE_NAME ('main')\n" +
+                "OPEN_PAREN ('(')\n" +
+                "ARRAY_TYPE_PREFIX ('[')\n" +
+                "CLASS_DESCRIPTOR ('Ljava/lang/String;')\n" +
+                "CLOSE_PAREN (')')\n" +
+                "VOID_TYPE ('V')\n" +
+                "WHITE_SPACE ('\\n    ')\n" +
+                "REGISTERS_DIRECTIVE ('.registers')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "POSITIVE_INTEGER_LITERAL ('2')\n" +
+                "WHITE_SPACE ('\\n    ')\n" +
+                "INSTRUCTION_FORMAT21c_FIELD ('sget-object')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "REGISTER ('v0')\n" +
+                "COMMA (',')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "CLASS_DESCRIPTOR ('Ljava/lang/System;')\n" +
+                "ARROW ('->')\n" +
+                "SIMPLE_NAME ('out')\n" +
+                "COLON (':')\n" +
+                "CLASS_DESCRIPTOR ('Ljava/io/PrintStream;')\n" +
+                "WHITE_SPACE ('\\n    ')\n" +
+                "INSTRUCTION_FORMAT21c_STRING ('const-string')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "REGISTER ('v1')\n" +
+                "COMMA (',')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "STRING_LITERAL ('\"Hello World!\"')\n" +
+                "WHITE_SPACE ('\\n    ')\n" +
+                "INSTRUCTION_FORMAT35c_METHOD ('invoke-virtual')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "OPEN_BRACE ('{')\n" +
+                "REGISTER ('v0')\n" +
+                "COMMA (',')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "REGISTER ('v1')\n" +
+                "CLOSE_BRACE ('}')\n" +
+                "COMMA (',')\n" +
+                "WHITE_SPACE (' ')\n" +
+                "CLASS_DESCRIPTOR ('Ljava/io/PrintStream;')\n" +
+                "ARROW ('->')\n" +
+                "SIMPLE_NAME ('println')\n" +
+                "OPEN_PAREN ('(')\n" +
+                "CLASS_DESCRIPTOR ('Ljava/lang/String;')\n" +
+                "CLOSE_PAREN (')')\n" +
+                "VOID_TYPE ('V')\n" +
+                "WHITE_SPACE ('\\n    ')\n" +
+                "INSTRUCTION_FORMAT10x ('return-void')\n" +
+                "WHITE_SPACE ('\\n')\n" +
+                "END_METHOD_DIRECTIVE ('.end method')"
+        );
+    }
+
+    @Override protected Lexer createLexer() {
+        return new SmaliLexer();
+    }
+
+    @Override protected String getDirPath() {
+        return "";
+    }
+
+    public void testErrorToken() {
+        String text = ".class public .blah";
+        doTest(text,
+                "CLASS_DIRECTIVE ('.class')\n" +
+                        "WHITE_SPACE (' ')\n" +
+                        "ACCESS_SPEC ('public')\n" +
+                        "WHITE_SPACE (' ')\n" +
+                        "BAD_CHARACTER ('.blah')\n");
+    }
+
+    /**
+     * Type out an example smali file character by character, ensuring that no exceptions are thrown
+     */
+    public void testPartialText() {
+        String text =
+                ".class public LHelloWorld;\n" +
+                        ".super Ljava/lang/Object;\n" +
+                        ".method public static main([Ljava/lang/String;)V\n" +
+                        "    .registers 2\n" +
+                        "    sget-object v0, Ljava/lang/System;->out:Ljava/io/PrintStream;\n" +
+                        "    const-string v1, \"Hello World!\"\n" +
+                        "    invoke-virtual {v0, v1}, Ljava/io/PrintStream;->println(Ljava/lang/String;)V\n" +
+                        "    return-void\n" +
+                        ".end method";
+
+        for (int i=1; i<text.length(); i++) {
+            printTokens(text.substring(i), 0);
+        }
+    }
+
+    /**
+     * Generate some random text and make sure the lexer doesn't throw any exceptions
+     */
+    public void testRandomText() {
+        for (int i=0; i<100; i++) {
+            String randomString = randomString(1000);
+
+            printTokens(randomString, 0);
+        }
+    }
+
+    private Random random = new Random(123456789);
+    private String randomString(int length) {
+        StringBuilder sb = new StringBuilder();
+        for (int i=0; i<length; i++) {
+            int type = random.nextInt(10);
+
+            if (type == 9) {
+                int randomCodepoint;
+                do {
+                    randomCodepoint = random.nextInt();
+                } while(!Character.isValidCodePoint(randomCodepoint));
+                sb.appendCodePoint(randomCodepoint);
+            } else if (type == 8) {
+                char randomChar;
+                do {
+                    randomChar = (char)random.nextInt(2^16);
+                } while(!Character.isValidCodePoint(randomChar));
+                sb.append(randomChar);
+            } else if (type > 4) {
+                sb.append((char)random.nextInt(256));
+            } else if (type == 4) {
+                sb.append(' ');
+            } else {
+                sb.append((char)random.nextInt(128));
+            }
+        }
+
+        return sb.toString();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliLiteralTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliLiteralTest.java
new file mode 100644
index 0000000..64d38a9
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliLiteralTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliLiteral;
+import org.junit.Assert;
+
+public class SmaliLiteralTest extends LightCodeInsightFixtureTestCase {
+    private void doTest(long expectedValue, String literalValue) {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".method blah()V\n" +
+                "    .registers <ref>" + literalValue + "\n" +
+                "    return-void\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                text.replace("<ref>", ""));
+
+        PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(leafElement);
+        SmaliLiteral literalElement = (SmaliLiteral)leafElement.getParent();
+        Assert.assertNotNull(literalElement);
+
+        Assert.assertEquals(expectedValue, literalElement.getIntegralValue());
+    }
+
+    public void testIntegerValue() {
+        doTest(123, "123");
+    }
+
+    public void testLongValue() {
+        doTest(100, "100L");
+    }
+
+    public void testShortValue() {
+        doTest(99, "99s");
+    }
+
+    public void testByteValue() {
+        doTest(127, "127t");
+    }
+
+    // TODO: test char
+    // TODO: test bool
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliMethodTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliMethodTest.java
new file mode 100644
index 0000000..6af9675
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliMethodTest.java
@@ -0,0 +1,309 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.debugger.SourcePosition;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiPrimitiveType;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.dexlib2.Opcode;
+import org.jf.smalidea.psi.impl.*;
+import org.junit.Assert;
+
+import java.util.List;
+
+public class SmaliMethodTest extends LightCodeInsightFixtureTestCase {
+    public void testMethodRegisters() {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".me<ref>thod blah()V\n" +
+                "    .registers 123\n" +
+                "    return-void\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                text.replace("<ref>", ""));
+
+        PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(leafElement);
+        SmaliMethod methodElement = (SmaliMethod)leafElement.getParent();
+        Assert.assertNotNull(methodElement);
+
+        Assert.assertEquals(123, methodElement.getRegisterCount());
+        Assert.assertEquals(1, methodElement.getParameterRegisterCount());
+    }
+
+    public void testMethodRegisters2() {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".me<ref>thod blah(IJLjava/lang/String;)V\n" +
+                "    .locals 123\n" +
+                "    return-void\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                text.replace("<ref>", ""));
+
+        PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(leafElement);
+        SmaliMethod methodElement = (SmaliMethod)leafElement.getParent();
+        Assert.assertNotNull(methodElement);
+
+        Assert.assertEquals(128, methodElement.getRegisterCount());
+        Assert.assertEquals(5, methodElement.getParameterRegisterCount());
+    }
+
+    public void testStaticRegisterCount() {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".method static blah(IJLjava/lang/String;)V\n" +
+                "    .locals 123\n" +
+                "    return-void\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        Assert.assertEquals(127, smaliMethod.getRegisterCount());
+        Assert.assertEquals(4, smaliMethod.getParameterRegisterCount());
+
+        Assert.assertEquals(0, smaliMethod.getParameterList().getParameters()[0].getParameterRegisterNumber());
+        Assert.assertEquals(123, smaliMethod.getParameterList().getParameters()[0].getRegisterNumber());
+    }
+
+    public void testMethodParams() {
+        myFixture.addFileToProject("my/TestAnnotation.smali",
+                ".class public interface abstract annotation Lmy/TestAnnotation;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract testBooleanValue()Z\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringArrayValue()[Ljava/lang/String;\n" +
+                ".end method\n" +
+                "\n" +
+                ".method public abstract testStringValue()Ljava/lang/String;\n" +
+                ".end method");
+
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".method blah(IJLjava/lang/String;)V\n" +
+                "    .locals 123\n" +
+                "    .param p1, \"anInt\"\n" +
+                "    .param p2\n" +
+                "        .annotation runtime Lmy/TestAnnotation;\n" +
+                "            testStringValue = \"myValue\"\n" +
+                "        .end annotation\n" +
+                "    .end param\n" +
+                "    return-void\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        SmaliMethodParamList paramList = smaliMethod.getParameterList();
+        SmaliMethodParameter[] parameters = paramList.getParameters();
+        Assert.assertEquals(3, parameters.length);
+
+        Assert.assertEquals("int", parameters[0].getType().getCanonicalText());
+        Assert.assertEquals("\"anInt\"", parameters[0].getName());
+        Assert.assertEquals(1, parameters[0].getRegisterCount());
+        Assert.assertEquals(124, parameters[0].getRegisterNumber());
+        Assert.assertEquals(1, parameters[0].getParameterRegisterNumber());
+        Assert.assertEquals(0, parameters[0].getAnnotations().length);
+
+        Assert.assertEquals("long", parameters[1].getType().getCanonicalText());
+        Assert.assertNull(parameters[1].getName());
+        Assert.assertEquals(2, parameters[1].getRegisterCount());
+        Assert.assertEquals(125, parameters[1].getRegisterNumber());
+        Assert.assertEquals(2, parameters[1].getParameterRegisterNumber());
+        Assert.assertEquals(1, parameters[1].getAnnotations().length);
+        Assert.assertEquals("my.TestAnnotation", parameters[1].getAnnotations()[0].getQualifiedName());
+
+        Assert.assertEquals("java.lang.String", parameters[2].getType().getCanonicalText());
+        Assert.assertNull(parameters[2].getName());
+        Assert.assertEquals(1, parameters[2].getRegisterCount());
+        Assert.assertEquals(127, parameters[2].getRegisterNumber());
+        Assert.assertEquals(4, parameters[2].getParameterRegisterNumber());
+        Assert.assertEquals(0, parameters[2].getAnnotations().length);
+    }
+
+    public void testVarArgsMethod() {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".method varargs static blah(IJ[Ljava/lang/String;)V\n" +
+                "    .locals 123\n" +
+                "    return-void\n" +
+                ".end method\n" +
+                ".method varargs static blah2(IJLjava/lang/String;)V\n" +
+                "    .locals 123\n" +
+                "    return-void\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        Assert.assertTrue(smaliMethod.isVarArgs());
+        Assert.assertFalse(smaliMethod.getParameterList().getParameters()[0].isVarArgs());
+        Assert.assertFalse(smaliMethod.getParameterList().getParameters()[1].isVarArgs());
+        Assert.assertTrue(smaliMethod.getParameterList().getParameters()[2].isVarArgs());
+
+        smaliMethod = smaliClass.getMethods()[1];
+        Assert.assertTrue(smaliMethod.isVarArgs());
+        Assert.assertFalse(smaliMethod.getParameterList().getParameters()[0].isVarArgs());
+        Assert.assertFalse(smaliMethod.getParameterList().getParameters()[1].isVarArgs());
+        Assert.assertFalse(smaliMethod.getParameterList().getParameters()[2].isVarArgs());
+    }
+
+    private static final String instructionsTestClass =
+            ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                    ".method public getRandomParentType(I)I\n" +
+                    "    .registers 4\n" +
+                    "    .param p1, \"edge\"    # I\n" +
+                    "\n" +
+                    "    .prologue\n" +
+                    "    const/4 v1, 0x2\n" +
+                    "\n" +
+                    "    .line 179\n" +
+                    "    if-nez p1, :cond_5\n" +
+                    "\n" +
+                    "    move v0, v1\n" +
+                    "\n" +
+                    "    .line 185\n" +
+                    "    :goto_4\n" +
+                    "    return v0\n" +
+                    "\n" +
+                    "    .line 182\n" +
+                    "    :cond_5\n" +
+                    "    if-ne p1, v1, :cond_f\n" +
+                    "\n" +
+                    "    .line 183\n" +
+                    "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                    "\n" +
+                    "    const/4 v1, 0x3\n" +
+                    "\n" +
+                    "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                    "\n" +
+                    "    move-result v0\n" +
+                    "\n" +
+                    "    goto :goto_4\n" +
+                    "\n" +
+                    "    .line 185\n" +
+                    "    :cond_f\n" +
+                    "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                    "\n" +
+                    "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                    "\n" +
+                    "    move-result v0\n" +
+                    "\n" +
+                    "    goto :goto_4\n" +
+                    ".end method";
+
+    public void testGetInstructions() {
+        String text = instructionsTestClass;
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        List<SmaliInstruction> instructions = smaliMethod.getInstructions();
+        Assert.assertEquals(14, instructions.size());
+    }
+
+    private void checkSourcePosition(SmaliMethod smaliMethod, int codeOffset, Opcode opcode) {
+        SourcePosition sourcePosition = smaliMethod.getSourcePositionForCodeOffset(codeOffset);
+        Assert.assertNotNull(sourcePosition);
+
+        SmaliInstruction instruction = (SmaliInstruction)sourcePosition.getElementAt();
+        Assert.assertEquals(opcode, instruction.getOpcode());
+        Assert.assertEquals(codeOffset, instruction.getOffset());
+    }
+
+    public void testGetSourcePositionForCodeOffset() {
+        String text = instructionsTestClass;
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        checkSourcePosition(smaliMethod, 0, Opcode.CONST_4);
+        checkSourcePosition(smaliMethod, 2, Opcode.IF_NEZ);
+        checkSourcePosition(smaliMethod, 6, Opcode.MOVE);
+        checkSourcePosition(smaliMethod, 8, Opcode.RETURN);
+        checkSourcePosition(smaliMethod, 10, Opcode.IF_NE);
+        checkSourcePosition(smaliMethod, 14, Opcode.SGET_OBJECT);
+        checkSourcePosition(smaliMethod, 18, Opcode.CONST_4);
+        checkSourcePosition(smaliMethod, 20, Opcode.INVOKE_VIRTUAL);
+        checkSourcePosition(smaliMethod, 26, Opcode.MOVE_RESULT);
+        checkSourcePosition(smaliMethod, 28, Opcode.GOTO);
+        checkSourcePosition(smaliMethod, 30, Opcode.SGET_OBJECT);
+        checkSourcePosition(smaliMethod, 34, Opcode.INVOKE_VIRTUAL);
+        checkSourcePosition(smaliMethod, 40, Opcode.MOVE_RESULT);
+        checkSourcePosition(smaliMethod, 42, Opcode.GOTO);
+    }
+
+    public void testThrowsList() {
+        String text = instructionsTestClass;
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        SmaliThrowsList throwsList = smaliMethod.getThrowsList();
+        Assert.assertNotNull(throwsList);
+        Assert.assertEquals(0, throwsList.getReferencedTypes().length);
+        Assert.assertEquals(0, throwsList.getReferenceElements().length);
+    }
+
+    public void testPrimitiveReturnType() {
+        String text = "" +
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".method blah()I\n" +
+                "    .registers 123\n" +
+                "    return-void\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        Assert.assertNotNull(smaliClass);
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        Assert.assertNotNull(smaliMethod.getReturnType());
+        Assert.assertTrue(smaliMethod.getReturnType().isConvertibleFrom(PsiPrimitiveType.INT));
+        Assert.assertTrue(smaliMethod.getReturnType().isAssignableFrom(PsiPrimitiveType.INT));
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliPositionManagerTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliPositionManagerTest.java
new file mode 100644
index 0000000..596c11e
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliPositionManagerTest.java
@@ -0,0 +1,237 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.google.common.collect.Lists;
+import com.intellij.debugger.NoDataException;
+import com.intellij.debugger.PositionManager;
+import com.intellij.debugger.SourcePosition;
+import com.intellij.debugger.engine.DebugProcess;
+import com.intellij.debugger.engine.DebugProcessListener;
+import com.intellij.debugger.engine.evaluation.EvaluateException;
+import com.intellij.debugger.engine.evaluation.EvaluationContext;
+import com.intellij.debugger.engine.jdi.VirtualMachineProxy;
+import com.intellij.debugger.engine.managerThread.DebuggerManagerThread;
+import com.intellij.debugger.requests.RequestManager;
+import com.intellij.execution.ExecutionResult;
+import com.intellij.execution.process.ProcessHandler;
+import com.intellij.openapi.project.Project;
+import com.intellij.openapi.util.Key;
+import com.intellij.psi.search.GlobalSearchScope;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import com.sun.jdi.*;
+import org.jetbrains.annotations.NotNull;
+import org.jf.dexlib2.Opcode;
+import org.jf.smalidea.debugging.SmaliPositionManager;
+import org.jf.smalidea.psi.impl.SmaliInstruction;
+import org.junit.Assert;
+
+import java.util.List;
+import java.util.Map;
+
+public class SmaliPositionManagerTest extends LightCodeInsightFixtureTestCase {
+    private static final String testClass =
+            "\n\n.class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                    ".method public getRandomParentType(I)I\n" +
+                    "    .registers 4\n" +
+                    "    .param p1, \"edge\"    # I\n" +
+                    "\n" +
+                    "    .prologue\n" +
+                    "    const/4 v1, 0x2\n" +
+                    "\n" +
+                    "    .line 179\n" +
+                    "    if-nez p1, :cond_5\n" +
+                    "\n" +
+                    "    move v0, v1\n" +
+                    "\n" +
+                    "    .line 185\n" +
+                    "    :goto_4\n" +
+                    "    return v0\n" +
+                    "\n" +
+                    "    .line 182\n" +
+                    "    :cond_5\n" +
+                    "    if-ne p1, v1, :cond_f\n" +
+                    "\n" +
+                    "    .line 183\n" +
+                    "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                    "\n" +
+                    "    const/4 v1, 0x3\n" +
+                    "\n" +
+                    "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                    "\n" +
+                    "    move-result v0\n" +
+                    "\n" +
+                    "    goto :goto_4\n" +
+                    "\n" +
+                    "    .line 185\n" +
+                    "    :cond_f\n" +
+                    "    sget-object v0, Lorg/jf/Penroser/PenroserApp;->random:Ljava/util/Random;\n" +
+                    "\n" +
+                    "    invoke-virtual {v0, v1}, Ljava/util/Random;->nextInt(I)I\n" +
+                    "\n" +
+                    "    move-result v0\n" +
+                    "\n" +
+                    "    goto :goto_4\n" +
+                    ".end method";
+
+    public void testGetSourcePosition() throws NoDataException {
+        myFixture.addFileToProject("my/pkg/blah.smali", testClass);
+
+        SmaliPositionManager positionManager = new SmaliPositionManager(new MockDebugProcess());
+
+        SourcePosition sourcePosition = positionManager.getSourcePosition(
+                "my.pkg.blah", "getRandomParentType", "(I)I", 0);
+        Assert.assertEquals(Opcode.CONST_4, ((SmaliInstruction)sourcePosition.getElementAt()).getOpcode());
+        Assert.assertEquals(0, ((SmaliInstruction)sourcePosition.getElementAt()).getOffset());
+
+        sourcePosition = positionManager.getSourcePosition("my.pkg.blah", "getRandomParentType", "(I)I", 10);
+        Assert.assertEquals(Opcode.INVOKE_VIRTUAL, ((SmaliInstruction)sourcePosition.getElementAt()).getOpcode());
+        Assert.assertEquals(20, ((SmaliInstruction)sourcePosition.getElementAt()).getOffset());
+    }
+
+    public void testGetAllClasses() throws NoDataException {
+        myFixture.addFileToProject("my/pkg/blah.smali", testClass);
+
+        SmaliPositionManager positionManager = new SmaliPositionManager(new MockDebugProcess());
+
+        List<ReferenceType> classes = positionManager.getAllClasses(positionManager.getSourcePosition(
+                "my.pkg.blah", "getRandomParentType", "(I)I", 0));
+        Assert.assertEquals(1, classes.size());
+        Assert.assertEquals("my.pkg.blah", classes.get(0).name());
+    }
+
+    private class MockDebugProcess implements DebugProcess {
+        @Override public Project getProject() {
+            return SmaliPositionManagerTest.this.getProject();
+        }
+
+        @Override public VirtualMachineProxy getVirtualMachineProxy() {
+            return new VirtualMachineProxy() {
+                @Override public List<ReferenceType> classesByName(final String s) {
+                    return Lists.<ReferenceType>newArrayList(new MockReferenceType(s));
+                }
+
+                @Override public List<ReferenceType> allClasses() { return null; }
+                @Override public boolean canGetBytecodes() { return false; }
+                @Override public boolean versionHigher(String version) { return false; }
+                @Override public boolean canWatchFieldModification() { return false; }
+                @Override public boolean canWatchFieldAccess() { return false; }
+                @Override public boolean canInvokeMethods() { return false; }
+                @Override public DebugProcess getDebugProcess() { return null; }
+                @Override public List<ReferenceType> nestedTypes(ReferenceType refType) { return null; }
+            };
+        }
+
+        @Override public void addDebugProcessListener(DebugProcessListener listener) {}
+        @Override public <T> T getUserData(Key<T> key) { return null; }
+        @Override public <T> void putUserData(Key<T> key, T value) {}
+        @Override public RequestManager getRequestsManager() { return null; }
+        @Override public PositionManager getPositionManager() { return null; }
+        @Override public void removeDebugProcessListener(DebugProcessListener listener) {}
+        @Override public void appendPositionManager(PositionManager positionManager) {}
+        @Override public void waitFor() {}
+        @Override public void waitFor(long timeout) {}
+        @Override public void stop(boolean forceTerminate) {}
+        @Override public ExecutionResult getExecutionResult() { return null; }
+        @Override public DebuggerManagerThread getManagerThread() { return null; }
+        @Override public Value invokeMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args) throws EvaluateException { return null; }
+        @Override public Value invokeMethod(EvaluationContext evaluationContext, ClassType classType, Method method, List args) throws EvaluateException { return null; }
+        @Override public Value invokeInstanceMethod(EvaluationContext evaluationContext, ObjectReference objRef, Method method, List args, int invocationOptions) throws EvaluateException { return null; }
+        @Override public ReferenceType findClass(EvaluationContext evaluationContext, String name, ClassLoaderReference classLoader) throws EvaluateException { return null; }
+        @Override public ArrayReference newInstance(ArrayType arrayType, int dimension) throws EvaluateException { return null; }
+        @Override public ObjectReference newInstance(EvaluationContext evaluationContext, ClassType classType, Method constructor, List paramList) throws EvaluateException { return null; }
+        @Override public boolean isAttached() { return false; }
+        @Override public boolean isDetached() { return false; }
+        @Override public boolean isDetaching() { return false; }
+        @NotNull @Override public GlobalSearchScope getSearchScope() { return null; }
+        @Override public void printToConsole(String text) {}
+        @Override public ProcessHandler getProcessHandler() { return null; }
+    }
+
+    private static class MockReferenceType implements ReferenceType {
+        private final String name;
+
+        public MockReferenceType(String name) {
+            this.name = name;
+        }
+
+        @Override public String name() {
+            return name;
+        }
+
+        @Override public List<Field> allFields() { return null; }
+        @Override public String genericSignature() { return null; }
+        @Override public ClassLoaderReference classLoader() { return null; }
+        @Override public String sourceName() throws AbsentInformationException { return null; }
+        @Override public List<String> sourceNames(String s) throws AbsentInformationException { return null; }
+        @Override public List<String> sourcePaths(String s) throws AbsentInformationException { return null; }
+        @Override public String sourceDebugExtension() throws AbsentInformationException { return null; }
+        @Override public boolean isStatic() { return false; }
+        @Override public boolean isAbstract() { return false; }
+        @Override public boolean isFinal() { return false; }
+        @Override public boolean isPrepared() { return false; }
+        @Override public boolean isVerified() { return false; }
+        @Override public boolean isInitialized() { return false; }
+        @Override public boolean failedToInitialize() { return false; }
+        @Override public List<Field> fields() { return null; }
+        @Override public List<Field> visibleFields() { return null; }
+        @Override public Field fieldByName(String s) { return null; }
+        @Override public List<Method> methods() { return null; }
+        @Override public List<Method> visibleMethods() { return null; }
+        @Override public List<Method> allMethods() { return null; }
+        @Override public List<Method> methodsByName(String s) { return null; }
+        @Override public List<Method> methodsByName(String s, String s1) { return null; }
+        @Override public List<ReferenceType> nestedTypes() { return null; }
+        @Override public Value getValue(Field field) { return null; }
+        @Override public Map<Field, Value> getValues(List<? extends Field> list) { return null; }
+        @Override public ClassObjectReference classObject() { return null; }
+        @Override public List<Location> allLineLocations() throws AbsentInformationException { return null; }
+        @Override public List<Location> allLineLocations(String s, String s1) throws AbsentInformationException { return null; }
+        @Override public List<Location> locationsOfLine(int i) throws AbsentInformationException { return null; }
+        @Override public List<Location> locationsOfLine(String s, String s1, int i) throws AbsentInformationException { return null; }
+        @Override public List<String> availableStrata() { return null; }
+        @Override public String defaultStratum() { return null; }
+        @Override public List<ObjectReference> instances(long l) { return null; }
+        @Override public int majorVersion() { return 0; }
+        @Override public int minorVersion() { return 0; }
+        @Override public int constantPoolCount() { return 0; }
+        @Override public byte[] constantPool() { return new byte[0]; }
+        @Override public int modifiers() { return 0; }
+        @Override public boolean isPrivate() { return false; }
+        @Override public boolean isPackagePrivate() { return false; }
+        @Override public boolean isProtected() { return false; }
+        @Override public boolean isPublic() { return false; }
+        @Override public int compareTo(ReferenceType o) { return 0; }
+        @Override public String signature() { return null; }
+        @Override public VirtualMachine virtualMachine() { return null; }
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/SmaliRegisterTest.java b/smalidea/src/test/java/org/jf/smalidea/SmaliRegisterTest.java
new file mode 100644
index 0000000..fbb4b62
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/SmaliRegisterTest.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea;
+
+import com.intellij.psi.PsiElement;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliRegisterReference;
+import org.junit.Assert;
+
+public class SmaliRegisterTest extends LightCodeInsightFixtureTestCase {
+    public void testRegisterReference() {
+        String text =
+                ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                        ".method blah()V\n" +
+                        "    .registers 123\n" +
+                        "    const <ref>v10, 123\n" +
+                        "    return-void\n" +
+                        ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali",
+                text.replace("<ref>", ""));
+
+        PsiElement leafElement = file.findElementAt(text.indexOf("<ref>"));
+        Assert.assertNotNull(leafElement);
+        SmaliRegisterReference registerReference = (SmaliRegisterReference)leafElement.getParent();
+        Assert.assertNotNull(registerReference);
+
+        Assert.assertEquals(10, registerReference.getRegisterNumber());
+        // TODO: test parameter register
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/dexlib/SmalideaMethodTest.java b/smalidea/src/test/java/org/jf/smalidea/dexlib/SmalideaMethodTest.java
new file mode 100644
index 0000000..515c9bb
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/dexlib/SmalideaMethodTest.java
@@ -0,0 +1,580 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.dexlib;
+
+import com.google.common.collect.Lists;
+import com.intellij.testFramework.fixtures.LightCodeInsightFixtureTestCase;
+import org.jf.dexlib2.AccessFlags;
+import org.jf.dexlib2.Opcode;
+import org.jf.dexlib2.iface.ExceptionHandler;
+import org.jf.dexlib2.iface.MethodImplementation;
+import org.jf.dexlib2.iface.MethodParameter;
+import org.jf.dexlib2.iface.TryBlock;
+import org.jf.dexlib2.iface.instruction.Instruction;
+import org.jf.dexlib2.iface.instruction.SwitchElement;
+import org.jf.dexlib2.iface.instruction.formats.*;
+import org.jf.dexlib2.iface.reference.FieldReference;
+import org.jf.dexlib2.iface.reference.StringReference;
+import org.jf.dexlib2.util.ReferenceUtil;
+import org.jf.smalidea.psi.impl.SmaliClass;
+import org.jf.smalidea.psi.impl.SmaliFile;
+import org.jf.smalidea.psi.impl.SmaliMethod;
+import org.junit.Assert;
+
+import java.util.List;
+
+public class SmalideaMethodTest extends LightCodeInsightFixtureTestCase {
+
+    public void testSmalideaMethod() {
+        String text = ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".method public someMethodName(I)I\n" +
+                "    .registers 4\n" +
+                "    .param p1, \"edge\"    # I\n" +
+
+                "    goto :here  #0: 10t\n" +
+                "    :here\n" +
+                "    return-void  #1: 21c\n" +
+                "    const/4 v0, 1234 #2: 11n\n" +
+                "    monitor-enter v1, #3: 11x\n" +
+                "    move v1, v0 #4: 12x\n" +
+                "    goto/16 :here #5: 20t\n" +
+                "    sget v0, La/b/c;->blah:I #6: 21c\n" +
+                "    const/high16 v0, 0x12340000 #7: 21ih\n" +
+                "    const-wide/high16 v0, 0x1234000000000000L #8: 21lh\n" +
+                "    const-wide/16 v0, 1234 #9: 21s\n" +
+                "    if-eqz v0, :here #10: 21t\n" +
+                "    add-int/lit8 v0, v1, 123 #11: 22b\n" +
+                "    iget v1, v2, Labc;->blort:Z #12: 22c\n" +
+                "    add-int/lit16 v0, v1, 1234 #13: 22s\n" +
+                "    if-eq v0, v1, :here #14: 22t\n" +
+                "    move/from16 v0, v1 #15: 22x\n" +
+                "    cmpl-float v0, v1, v2 #16: 23x\n" +
+                "    goto/32 :here #17: 30t\n" +
+                "    const-string/jumbo v0, \"abcd\" #18: 31c\n" +
+                "    const v0, 1234 #19: 31i\n" +
+                "    move/16 v0, v1 #20: 32x\n" +
+                "    invoke-virtual {v0, v1, v2, v3, v4}, Lblah;->blort(IIII)I #21: 35c\n" +
+                "    invoke-virtual/range {v0..v4}, Lblah;->blort(IIII)I #22: 3rc\n" +
+                "    const-wide v0, 0x1234567890L #23: 51i\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        SmalideaMethod method = new SmalideaMethod(smaliMethod);
+        Assert.assertEquals("Lmy/pkg/blah;", method.getDefiningClass());
+        Assert.assertEquals("someMethodName", method.getName());
+        Assert.assertEquals("I", method.getReturnType());
+
+        List<? extends CharSequence> parameterTypes = method.getParameterTypes();
+        Assert.assertEquals(1, parameterTypes.size());
+        Assert.assertEquals("I", parameterTypes.get(0));
+
+        List<? extends MethodParameter> parameters = method.getParameters();
+        Assert.assertEquals(1, parameters.size());
+        Assert.assertEquals("I", parameters.get(0).getType());
+        Assert.assertEquals("edge", parameters.get(0).getName());
+
+        Assert.assertEquals(AccessFlags.PUBLIC.getValue(), method.getAccessFlags());
+
+        MethodImplementation impl = method.getImplementation();
+        Assert.assertNotNull(impl);
+
+        Assert.assertEquals(4, impl.getRegisterCount());
+
+        List<? extends Instruction> instructions = Lists.newArrayList(impl.getInstructions());
+
+        {
+            Instruction10t instruction = (Instruction10t)instructions.get(0);
+            Assert.assertEquals(Opcode.GOTO, instruction.getOpcode());
+            Assert.assertEquals(1, instruction.getCodeOffset());
+        }
+
+        {
+            Instruction10x instruction = (Instruction10x)instructions.get(1);
+            Assert.assertEquals(Opcode.RETURN_VOID, instruction.getOpcode());
+        }
+
+        {
+            Instruction11n instruction = (Instruction11n)instructions.get(2);
+            Assert.assertEquals(Opcode.CONST_4, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1234, instruction.getNarrowLiteral());
+        }
+
+        {
+            Instruction11x instruction = (Instruction11x)instructions.get(3);
+            Assert.assertEquals(Opcode.MONITOR_ENTER, instruction.getOpcode());
+            Assert.assertEquals(1, instruction.getRegisterA());
+        }
+
+        {
+            Instruction12x instruction = (Instruction12x)instructions.get(4);
+            Assert.assertEquals(Opcode.MOVE, instruction.getOpcode());
+            Assert.assertEquals(1, instruction.getRegisterA());
+            Assert.assertEquals(0, instruction.getRegisterB());
+        }
+
+        {
+            Instruction20t instruction = (Instruction20t)instructions.get(5);
+            Assert.assertEquals(Opcode.GOTO_16, instruction.getOpcode());
+            Assert.assertEquals(-4, instruction.getCodeOffset());
+        }
+
+        {
+            Instruction21c instruction = (Instruction21c)instructions.get(6);
+            Assert.assertEquals(Opcode.SGET, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals("La/b/c;->blah:I", ReferenceUtil.getFieldDescriptor(
+                    (FieldReference)instruction.getReference()));
+        }
+
+        {
+            Instruction21ih instruction = (Instruction21ih)instructions.get(7);
+            Assert.assertEquals(Opcode.CONST_HIGH16, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(0x1234, instruction.getHatLiteral());
+            Assert.assertEquals(0x12340000, instruction.getNarrowLiteral());
+            Assert.assertEquals(0x12340000, instruction.getWideLiteral());
+        }
+
+        {
+            Instruction21lh instruction = (Instruction21lh)instructions.get(8);
+            Assert.assertEquals(Opcode.CONST_WIDE_HIGH16, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(0x1234, instruction.getHatLiteral());
+            Assert.assertEquals(0x1234000000000000L, instruction.getWideLiteral());
+        }
+
+        {
+            Instruction21s instruction = (Instruction21s)instructions.get(9);
+            Assert.assertEquals(Opcode.CONST_WIDE_16, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1234, instruction.getWideLiteral());
+        }
+
+        {
+            Instruction21t instruction = (Instruction21t)instructions.get(10);
+            Assert.assertEquals(Opcode.IF_EQZ, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(-14, instruction.getCodeOffset());
+        }
+
+        {
+            Instruction22b instruction = (Instruction22b)instructions.get(11);
+            Assert.assertEquals(Opcode.ADD_INT_LIT8, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1, instruction.getRegisterB());
+            Assert.assertEquals(123, instruction.getNarrowLiteral());
+        }
+
+        {
+            Instruction22c instruction = (Instruction22c)instructions.get(12);
+            Assert.assertEquals(Opcode.IGET, instruction.getOpcode());
+            Assert.assertEquals(1, instruction.getRegisterA());
+            Assert.assertEquals(2, instruction.getRegisterB());
+            Assert.assertEquals("Labc;->blort:Z", ReferenceUtil.getFieldDescriptor(
+                    (FieldReference)instruction.getReference()));
+        }
+
+        {
+            Instruction22s instruction = (Instruction22s)instructions.get(13);
+            Assert.assertEquals(Opcode.ADD_INT_LIT16, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1, instruction.getRegisterB());
+            Assert.assertEquals(1234, instruction.getNarrowLiteral());
+        }
+
+        {
+            Instruction22t instruction = (Instruction22t)instructions.get(14);
+            Assert.assertEquals(Opcode.IF_EQ, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1, instruction.getRegisterB());
+            Assert.assertEquals(-22, instruction.getCodeOffset());
+        }
+
+        {
+            Instruction22x instruction = (Instruction22x)instructions.get(15);
+            Assert.assertEquals(Opcode.MOVE_FROM16, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1, instruction.getRegisterB());
+        }
+
+        {
+            Instruction23x instruction = (Instruction23x)instructions.get(16);
+            Assert.assertEquals(Opcode.CMPL_FLOAT, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1, instruction.getRegisterB());
+            Assert.assertEquals(2, instruction.getRegisterC());
+        }
+
+        {
+            Instruction30t instruction = (Instruction30t)instructions.get(17);
+            Assert.assertEquals(Opcode.GOTO_32, instruction.getOpcode());
+            Assert.assertEquals(-28, instruction.getCodeOffset());
+        }
+
+        {
+            Instruction31c instruction = (Instruction31c)instructions.get(18);
+            Assert.assertEquals(Opcode.CONST_STRING_JUMBO, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals("abcd", ((StringReference)instruction.getReference()).getString());
+        }
+
+        {
+            Instruction31i instruction = (Instruction31i)instructions.get(19);
+            Assert.assertEquals(Opcode.CONST, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1234, instruction.getNarrowLiteral());
+        }
+
+        {
+            Instruction32x instruction = (Instruction32x)instructions.get(20);
+            Assert.assertEquals(Opcode.MOVE_16, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(1, instruction.getRegisterB());
+        }
+
+        {
+            Instruction35c instruction = (Instruction35c)instructions.get(21);
+            Assert.assertEquals(Opcode.INVOKE_VIRTUAL, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterC());
+            Assert.assertEquals(1, instruction.getRegisterD());
+            Assert.assertEquals(2, instruction.getRegisterE());
+            Assert.assertEquals(3, instruction.getRegisterF());
+            Assert.assertEquals(4, instruction.getRegisterG());
+            Assert.assertEquals("Lblah;->blort(IIII)I", ReferenceUtil.getReferenceString(instruction.getReference()));
+        }
+
+        {
+            Instruction3rc instruction = (Instruction3rc)instructions.get(22);
+            Assert.assertEquals(Opcode.INVOKE_VIRTUAL_RANGE, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getStartRegister());
+            Assert.assertEquals(5, instruction.getRegisterCount());
+            Assert.assertEquals("Lblah;->blort(IIII)I", ReferenceUtil.getReferenceString(instruction.getReference()));
+        }
+
+        {
+            Instruction51l instruction = (Instruction51l)instructions.get(23);
+            Assert.assertEquals(Opcode.CONST_WIDE, instruction.getOpcode());
+            Assert.assertEquals(0, instruction.getRegisterA());
+            Assert.assertEquals(0x1234567890L, instruction.getWideLiteral());
+        }
+    }
+
+    public void testCatchBlocks() {
+        String text = ".class public Lmy/pkg/blah; .super Ljava/lang/Object;\n" +
+                ".method public onCreateEngine()Landroid/service/wallpaper/WallpaperService$Engine;\n" +
+                "    .registers 5\n" +
+                "\n" +
+                "    .prologue\n" +
+                "    .line 88\n" +
+                "    new-instance v0, Lorg/jf/Penroser/PenroserLiveWallpaper$PenroserGLEngine;\n" +
+                "\n" +
+                "    invoke-direct {v0, p0}, Lorg/jf/Penroser/PenroserLiveWallpaper$PenroserGLEngine;-><init>(Lorg/jf/Penroser/PenroserLiveWallpaper;)V\n" +
+                "\n" +
+                "    .line 89\n" +
+                "    .local v0, \"engine\":Lorg/jf/Penroser/PenroserLiveWallpaper$PenroserGLEngine;\n" +
+                "    sget-object v1, Lorg/jf/Penroser/PenroserLiveWallpaper;->engines:Ljava/util/LinkedList;\n" +
+                "\n" +
+                "    monitor-enter v1\n" +
+                "\n" +
+                "    .line 90\n" +
+                "    :try_start_8\n" +
+                "    sget-object v2, Lorg/jf/Penroser/PenroserLiveWallpaper;->engines:Ljava/util/LinkedList;\n" +
+                "\n" +
+                "    new-instance v3, Ljava/lang/ref/WeakReference;\n" +
+                "\n" +
+                "    invoke-direct {v3, v0}, Ljava/lang/ref/WeakReference;-><init>(Ljava/lang/Object;)V\n" +
+                "\n" +
+                "    invoke-virtual {v2, v3}, Ljava/util/LinkedList;->addLast(Ljava/lang/Object;)V\n" +
+                "\n" +
+                "    .line 91\n" +
+                "    monitor-exit v1\n" +
+                "\n" +
+                "    .line 92\n" +
+                "    return-object v0\n" +
+                "\n" +
+                "    .line 91\n" +
+                "    :catchall_14\n" +
+                "    move-exception v2\n" +
+                "\n" +
+                "    monitor-exit v1\n" +
+                "    :try_end_16\n" +
+                "    .catch Ljava/lang/RuntimeException; {:try_start_8 .. :try_end_16} :newcatch\n" +
+                "    .catchall {:try_start_8 .. :try_end_16} :catchall_14\n" +
+                "\n" +
+                "    throw v2\n" +
+                "\n" +
+                "    :newcatch\n" +
+                "    move-exception v2\n" +
+                "    throw v2\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        SmalideaMethod method = new SmalideaMethod(smaliMethod);
+
+        MethodImplementation impl = method.getImplementation();
+        Assert.assertNotNull(impl);
+
+        List<? extends TryBlock<? extends ExceptionHandler>> tryBlocks = impl.getTryBlocks();
+        Assert.assertEquals(2, tryBlocks.size());
+
+        TryBlock<? extends ExceptionHandler> tryBlock = tryBlocks.get(0);
+        Assert.assertEquals(8, tryBlock.getStartCodeAddress());
+        Assert.assertEquals(14, tryBlock.getCodeUnitCount());
+        Assert.assertEquals(1, tryBlock.getExceptionHandlers().size());
+        Assert.assertEquals("Ljava/lang/RuntimeException;", tryBlock.getExceptionHandlers().get(0).getExceptionType());
+        Assert.assertEquals(23, tryBlock.getExceptionHandlers().get(0).getHandlerCodeAddress());
+
+        tryBlock = tryBlocks.get(1);
+        Assert.assertEquals(8, tryBlock.getStartCodeAddress());
+        Assert.assertEquals(14, tryBlock.getCodeUnitCount());
+        Assert.assertEquals(1, tryBlock.getExceptionHandlers().size());
+        Assert.assertEquals(null, tryBlock.getExceptionHandlers().get(0).getExceptionType());
+        Assert.assertEquals(20, tryBlock.getExceptionHandlers().get(0).getHandlerCodeAddress());
+    }
+
+    private static void checkSwitchElement(SwitchElement element, int key, int offset) {
+        Assert.assertEquals(key, element.getKey());
+        Assert.assertEquals(offset, element.getOffset());
+    }
+
+    public void testPackedSwitch() {
+        String text =
+                ".class public LFormat31t;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".source \"Format31t.smali\"" +
+                "\n" +
+                ".method public test_packed-switch()V\n" +
+                "    .registers 1\n" +
+                "    .annotation runtime Lorg/junit/Test;\n" +
+                "    .end annotation\n" +
+                "\n" +
+                "    const v0, 12\n" +
+                "\n" +
+                ":switch\n" +
+                "    packed-switch v0, :PackedSwitch\n" +
+                "\n" +
+                ":Label10\n" +
+                "    invoke-static {}, Lorg/junit/Assert;->fail()V\n" +
+                "    return-void\n" +
+                "\n" +
+                ":Label11\n" +
+                "    invoke-static {}, Lorg/junit/Assert;->fail()V\n" +
+                "    return-void\n" +
+                "\n" +
+                ":Label12\n" +
+                "    return-void\n" +
+                "\n" +
+                ":Label13\n" +
+                "    invoke-static {}, Lorg/junit/Assert;->fail()V\n" +
+                "    return-void\n" +
+                "\n" +
+                ":PackedSwitch\n" +
+                "    .packed-switch 10\n" +
+                "        :Label10\n" +
+                "        :Label11\n" +
+                "        :Label12\n" +
+                "        :Label13\n" +
+                "    .end packed-switch\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        SmalideaMethod method = new SmalideaMethod(smaliMethod);
+
+        MethodImplementation impl = method.getImplementation();
+        Assert.assertNotNull(impl);
+
+        List<Instruction> instructions = Lists.newArrayList(impl.getInstructions());
+
+        PackedSwitchPayload packedSwitchPayload = (PackedSwitchPayload)instructions.get(9);
+        List<? extends SwitchElement> switchElements = packedSwitchPayload.getSwitchElements();
+        Assert.assertEquals(4, switchElements.size());
+
+        checkSwitchElement(switchElements.get(0), 10, 6);
+        checkSwitchElement(switchElements.get(1), 11, 14);
+        checkSwitchElement(switchElements.get(2), 12, 22);
+        checkSwitchElement(switchElements.get(3), 13, 24);
+    }
+
+    public void testSparseSwitch() {
+        String text =
+                ".class public LFormat31t;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".source \"Format31t.smali\"" +
+                "\n" +
+                ".method public test_sparse-switch()V\n" +
+                "    .registers 1\n" +
+                "    .annotation runtime Lorg/junit/Test;\n" +
+                "    .end annotation\n" +
+                "\n" +
+                "    const v0, 13\n" +
+                "\n" +
+                ":switch\n" +
+                "    sparse-switch v0, :SparseSwitch\n" +
+                "\n" +
+                ":Label10\n" +
+                "    invoke-static {}, Lorg/junit/Assert;->fail()V\n" +
+                "    return-void\n" +
+                "\n" +
+                ":Label20\n" +
+                "    invoke-static {}, Lorg/junit/Assert;->fail()V\n" +
+                "    return-void\n" +
+                "\n" +
+                ":Label15\n" +
+                "    invoke-static {}, Lorg/junit/Assert;->fail()V\n" +
+                "    return-void\n" +
+                "\n" +
+                ":Label13\n" +
+                "    return-void\n" +
+                "\n" +
+                ":Label99\n" +
+                "    invoke-static {}, Lorg/junit/Assert;->fail()V\n" +
+                "    return-void\n" +
+                "\n" +
+                ":SparseSwitch\n" +
+                "    .sparse-switch\n" +
+                "        10 -> :Label10\n" +
+                "        13 -> :Label13\n" +
+                "        15 -> :Label15\n" +
+                "        20 -> :Label20\n" +
+                "        99 -> :Label99\n" +
+                "    .end sparse-switch\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        SmalideaMethod method = new SmalideaMethod(smaliMethod);
+
+        MethodImplementation impl = method.getImplementation();
+        Assert.assertNotNull(impl);
+
+        List<Instruction> instructions = Lists.newArrayList(impl.getInstructions());
+
+        SparseSwitchPayload sparseSwitchPayload = (SparseSwitchPayload)instructions.get(11);
+        List<? extends SwitchElement> switchElements = sparseSwitchPayload.getSwitchElements();
+        Assert.assertEquals(5, switchElements.size());
+
+        checkSwitchElement(switchElements.get(0), 10, 6);
+        checkSwitchElement(switchElements.get(1), 13, 30);
+        checkSwitchElement(switchElements.get(2), 15, 22);
+        checkSwitchElement(switchElements.get(3), 20, 14);
+        checkSwitchElement(switchElements.get(4), 99, 32);
+    }
+
+    public void testArrayData() {
+        String text =
+                ".class public LFormat31t;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".source \"Format31t.smali\"" +
+                "\n" +
+                ".method public test_fill-array-data()V\n" +
+                "    .registers 3\n" +
+                "    .annotation runtime Lorg/junit/Test;\n" +
+                "    .end annotation\n" +
+                "\n" +
+                "    const v0, 6\n" +
+                "    new-array v0, v0, [I\n" +
+                "    fill-array-data v0, :ArrayData\n" +
+                "\n" +
+                "    const v1, 0\n" +
+                "    aget v2, v0, v1\n" +
+                "    const v1, 1\n" +
+                "    invoke-static {v1, v2}, LAssert;->assertEquals(II)V\n" +
+                "\n" +
+                "    const v1, 1\n" +
+                "    aget v2, v0, v1\n" +
+                "    const v1, 2\n" +
+                "    invoke-static {v1, v2}, LAssert;->assertEquals(II)V\n" +
+                "\n" +
+                "    const v1, 2\n" +
+                "    aget v2, v0, v1\n" +
+                "    const v1, 3\n" +
+                "    invoke-static {v1, v2}, LAssert;->assertEquals(II)V\n" +
+                "\n" +
+                "    const v1, 3\n" +
+                "    aget v2, v0, v1\n" +
+                "    const v1, 4\n" +
+                "    invoke-static {v1, v2}, LAssert;->assertEquals(II)V\n" +
+                "\n" +
+                "    const v1, 4\n" +
+                "    aget v2, v0, v1\n" +
+                "    const v1, 5\n" +
+                "    invoke-static {v1, v2}, LAssert;->assertEquals(II)V\n" +
+                "\n" +
+                "    const v1, 5\n" +
+                "    aget v2, v0, v1\n" +
+                "    const v1, 6\n" +
+                "    invoke-static {v1, v2}, LAssert;->assertEquals(II)V\n" +
+                "\n" +
+                "    return-void\n" +
+                "\n" +
+                ":ArrayData\n" +
+                "    .array-data 4\n" +
+                "        1 2 128 -256 65536 0x7fffffff\n" +
+                "    .end array-data\n" +
+                ".end method";
+
+        SmaliFile file = (SmaliFile)myFixture.addFileToProject("my/pkg/blah.smali", text);
+        SmaliClass smaliClass = file.getPsiClass();
+        SmaliMethod smaliMethod = smaliClass.getMethods()[0];
+
+        SmalideaMethod method = new SmalideaMethod(smaliMethod);
+
+        MethodImplementation impl = method.getImplementation();
+        Assert.assertNotNull(impl);
+
+        List<Instruction> instructions = Lists.newArrayList(impl.getInstructions());
+
+        ArrayPayload arrayPayload = (ArrayPayload)instructions.get(28);
+        Assert.assertEquals(4, arrayPayload.getElementWidth());
+        List<Number> elements = arrayPayload.getArrayElements();
+        Assert.assertEquals(6, elements.size());
+
+        Assert.assertEquals(1L, elements.get(0).longValue());
+        Assert.assertEquals(2L, elements.get(1).longValue());
+        Assert.assertEquals(128L, elements.get(2));
+        Assert.assertEquals(-256L, elements.get(3));
+        Assert.assertEquals(65536L, elements.get(4));
+        Assert.assertEquals(0x7fffffffL, elements.get(5));
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/ClassUsageTypeTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/ClassUsageTypeTest.java
new file mode 100644
index 0000000..18a6194
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/ClassUsageTypeTest.java
@@ -0,0 +1,74 @@
+package org.jf.smalidea.findUsages;
+
+import com.intellij.usages.impl.rules.UsageType;
+
+public class ClassUsageTypeTest extends UsageTypeTest {
+    public ClassUsageTypeTest() {
+        super(new SmaliUsageTypeProvider());
+    }
+
+    public void testClassUsageTypes() throws Exception {
+        doTest("blah.smali", "" +
+                        ".class public Lbl<ref:1>ah;\n" +
+                        ".super Lbl<ref:2>ah;\n" +
+                        ".implements Lbl<ref:3>ah;\n" +
+                        "\n" +
+                        ".annotation build Lbl<ref:22>ah;\n" +
+                        "    value = .subannotation Lbl<ref:23>ah;\n" +
+                        "                value = Lbl<ref:24>ah;\n" +
+                        "            .end subannotation\n" +
+                        ".end annotation\n" +
+                        "\n" +
+                        ".field static public blah:Lbl<ref:4>ah; = Lbl<ref:25>ah;\n" +
+                        "\n" +
+                        ".method public blah(Lbl<ref:5>ah;)Lbl<ref:6>ah;\n" +
+                        "    .registers 2\n" +
+                        "    .local p0, \"this\":Lbl<ref:7>ah;\n" +
+                        "\n" +
+                        "    :start\n" +
+                        "        iget-object v0, v0, Lbl<ref:8>ah;->blah:Lbl<ref:9>ah;\n" +
+                        "\n" +
+                        "        invoke-virtual {v0}, Lbl<ref:10>ah;->blah(Lbl<ref:11>ah;)Lbl<ref:12>ah;\n" +
+                        "\n" +
+                        "        instance-of v0, v0, Lbl<ref:13>ah;\n" +
+                        "        check-cast v0, Lbl<ref:14>ah;\n" +
+                        "        new-instance v0, Lbl<ref:15>ah;\n" +
+                        "        const-class v0, Lbl<ref:16>ah;\n" +
+                        "        throw-verification-error generic-error, Lbl<ref:17>ah;\n" +
+                        "\n" +
+                        "        filled-new-array {v0, v0, v0, v0, v0}, Lbl<ref:18>ah;\n" +
+                        "        new-array v0, v0, Lbl<ref:19>ah;\n" +
+                        "        filled-new-array/range {v0}, Lbl<ref:20>ah;\n" +
+                        "    :end\n" +
+                        "\n" +
+                        "    .catch Lbl<ref:21>ah; { :start .. :end } :handler\n" +
+                        "    :handler\n" +
+                        "    return-void\n" +
+                        ".end method",
+                1, SmaliUsageTypeProvider.CLASS_DECLARATION,
+                2, UsageType.CLASS_EXTENDS_IMPLEMENTS_LIST,
+                3, UsageType.CLASS_EXTENDS_IMPLEMENTS_LIST,
+                4, UsageType.CLASS_FIELD_DECLARATION,
+                5, UsageType.CLASS_METHOD_PARAMETER_DECLARATION,
+                6, UsageType.CLASS_METHOD_RETURN_TYPE,
+                7, UsageType.CLASS_LOCAL_VAR_DECLARATION,
+                8, SmaliUsageTypeProvider.FIELD_DECLARING_TYPE_REFERENCE,
+                9, SmaliUsageTypeProvider.FIELD_TYPE_REFERENCE,
+                10, SmaliUsageTypeProvider.METHOD_DECLARING_TYPE_REFERENCE,
+                11, SmaliUsageTypeProvider.METHOD_PARAM_REFERENCE,
+                12, SmaliUsageTypeProvider.METHOD_RETURN_TYPE_REFERENCE,
+                13, UsageType.CLASS_INSTANCE_OF,
+                14, UsageType.CLASS_CAST_TO,
+                15, UsageType.CLASS_NEW_OPERATOR,
+                16, UsageType.CLASS_CLASS_OBJECT_ACCESS,
+                17, SmaliUsageTypeProvider.VERIFICATION_ERROR,
+                18, UsageType.CLASS_NEW_ARRAY,
+                19, UsageType.CLASS_NEW_ARRAY,
+                20, UsageType.CLASS_NEW_ARRAY,
+                21, UsageType.CLASS_CATCH_CLAUSE_PARAMETER_DECLARATION,
+                22, UsageType.ANNOTATION,
+                23, UsageType.ANNOTATION,
+                24, SmaliUsageTypeProvider.LITERAL,
+                25, SmaliUsageTypeProvider.LITERAL);
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/FieldUsageTypeTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/FieldUsageTypeTest.java
new file mode 100644
index 0000000..128c97b
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/FieldUsageTypeTest.java
@@ -0,0 +1,115 @@
+package org.jf.smalidea.findUsages;
+
+import com.intellij.usages.impl.rules.UsageType;
+
+public class FieldUsageTypeTest extends UsageTypeTest {
+    public FieldUsageTypeTest() {
+        super(new SmaliUsageTypeProvider());
+    }
+
+    public void testFieldUsageTypes() throws Exception {
+        doTest("blah.smali", "" +
+                        ".class public Lblah;\n" +
+                        ".super Ljava/lang/Object;\n" +
+                        "\n" +
+                        ".annotation runtime Lblah;\n" +
+                        "    element = Lblah;->bl<ref:1>ah:Lblah;\n" +
+                        "    element2 = .enum Lblah;->bl<ref:2>ah:Lblah;\n" +
+                        ".end annotation\n" +
+                        "\n" +
+                        ".field public blah:Lblah;\n" +
+                        "\n" +
+                        ".method public blah(Lblah;)Lblah;\n" +
+                        "    .registers 2\n" +
+                        "\n" +
+                        "    iget v0, v0, Lblah;->bl<ref:3>ah:Lblah;\n" +
+                        "    iget-object v0, v0, Lblah;->bl<ref:4>ah:Lblah;\n" +
+                        "    iget-byte v0, v0, Lblah;->bl<ref:5>ah:Lblah;\n" +
+                        "    iget-char v0, v0, Lblah;->bl<ref:6>ah:Lblah;\n" +
+                        "    iget-object v0, v0, Lblah;->bl<ref:7>ah:Lblah;\n" +
+                        "    iget-object-volatile v0, v0, Lblah;->bl<ref:8>ah:Lblah;\n" +
+                        "    iget-short v0, v0, Lblah;->bl<ref:9>ah:Lblah;\n" +
+                        "    iget-volatile v0, v0, Lblah;->bl<ref:10>ah:Lblah;\n" +
+                        "    iget-wide v0, v0, Lblah;->bl<ref:11>ah:Lblah;\n" +
+                        "    iget-wide-volatile v0, v0, Lblah;->bl<ref:12>ah:Lblah;\n" +
+                        "    sget v0, Lblah;->bl<ref:13>ah:Lblah;\n" +
+                        "    sget-boolean v0, Lblah;->bl<ref:14>ah:Lblah;\n" +
+                        "    sget-byte v0, Lblah;->bl<ref:15>ah:Lblah;\n" +
+                        "    sget-char v0, Lblah;->bl<ref:16>ah:Lblah;\n" +
+                        "    sget-object v0, Lblah;->bl<ref:17>ah:Lblah;\n" +
+                        "    sget-object-volatile v0, Lblah;->bl<ref:18>ah:Lblah;\n" +
+                        "    sget-short v0, Lblah;->bl<ref:19>ah:Lblah;\n" +
+                        "    sget-volatile v0, Lblah;->bl<ref:20>ah:Lblah;\n" +
+                        "    sget-wide v0, Lblah;->bl<ref:21>ah:Lblah;\n" +
+                        "    sget-wide-volatile v0, Lblah;->bl<ref:22>ah:Lblah;\n" +
+                        "    \n" +
+                        "    iput v0, v0, Lblah;->bl<ref:23>ah:Lblah;\n" +
+                        "    iput-object v0, v0, Lblah;->bl<ref:24>ah:Lblah;\n" +
+                        "    iput-byte v0, v0, Lblah;->bl<ref:25>ah:Lblah;\n" +
+                        "    iput-char v0, v0, Lblah;->bl<ref:26>ah:Lblah;\n" +
+                        "    iput-object v0, v0, Lblah;->bl<ref:27>ah:Lblah;\n" +
+                        "    iput-object-volatile v0, v0, Lblah;->bl<ref:28>ah:Lblah;\n" +
+                        "    iput-short v0, v0, Lblah;->bl<ref:29>ah:Lblah;\n" +
+                        "    iput-volatile v0, v0, Lblah;->bl<ref:30>ah:Lblah;\n" +
+                        "    iput-wide v0, v0, Lblah;->bl<ref:31>ah:Lblah;\n" +
+                        "    iput-wide-volatile v0, v0, Lblah;->bl<ref:32>ah:Lblah;\n" +
+                        "    sput v0, Lblah;->bl<ref:33>ah:Lblah;\n" +
+                        "    sput-boolean v0, Lblah;->bl<ref:34>ah:Lblah;\n" +
+                        "    sput-byte v0, Lblah;->bl<ref:35>ah:Lblah;\n" +
+                        "    sput-char v0, Lblah;->bl<ref:36>ah:Lblah;\n" +
+                        "    sput-object v0, Lblah;->bl<ref:37>ah:Lblah;\n" +
+                        "    sput-object-volatile v0, Lblah;->bl<ref:38>ah:Lblah;\n" +
+                        "    sput-short v0, Lblah;->bl<ref:39>ah:Lblah;\n" +
+                        "    sput-volatile v0, Lblah;->bl<ref:40>ah:Lblah;\n" +
+                        "    sput-wide v0, Lblah;->bl<ref:41>ah:Lblah;\n" +
+                        "    sput-wide-volatile v0, Lblah;->bl<ref:42>ah:Lblah;\n" +
+                        "\n" +
+                        "    throw-verification-error generic-error, Lblah;->bl<ref:43>ah:Lblah;\n" +
+                        "\n" +
+                        "    return-void\n" +
+                        ".end method\n",
+                1, SmaliUsageTypeProvider.LITERAL,
+                2, SmaliUsageTypeProvider.LITERAL,
+                3, UsageType.READ,
+                4, UsageType.READ,
+                5, UsageType.READ,
+                6, UsageType.READ,
+                7, UsageType.READ,
+                8, UsageType.READ,
+                9, UsageType.READ,
+                10, UsageType.READ,
+                11, UsageType.READ,
+                12, UsageType.READ,
+                13, UsageType.READ,
+                14, UsageType.READ,
+                15, UsageType.READ,
+                16, UsageType.READ,
+                17, UsageType.READ,
+                18, UsageType.READ,
+                19, UsageType.READ,
+                20, UsageType.READ,
+                21, UsageType.READ,
+                22, UsageType.READ,
+                23, UsageType.WRITE,
+                24, UsageType.WRITE,
+                25, UsageType.WRITE,
+                26, UsageType.WRITE,
+                27, UsageType.WRITE,
+                28, UsageType.WRITE,
+                29, UsageType.WRITE,
+                30, UsageType.WRITE,
+                31, UsageType.WRITE,
+                32, UsageType.WRITE,
+                33, UsageType.WRITE,
+                34, UsageType.WRITE,
+                35, UsageType.WRITE,
+                36, UsageType.WRITE,
+                37, UsageType.WRITE,
+                38, UsageType.WRITE,
+                39, UsageType.WRITE,
+                40, UsageType.WRITE,
+                41, UsageType.WRITE,
+                42, UsageType.WRITE,
+                43, SmaliUsageTypeProvider.VERIFICATION_ERROR);
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/FindAnnotationElementUsagesTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindAnnotationElementUsagesTest.java
new file mode 100644
index 0000000..ec889cc
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindAnnotationElementUsagesTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+public class FindAnnotationElementUsagesTest extends FindUsagesTest {
+    public void testSmaliUsageInSmaliFile() throws Exception {
+        addFile("AnnotationWithValues.smali", "" +
+                ".class public abstract interface annotation LAnnotationWithValues;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract intValue()I\n" +
+                ".end method\n" +
+                ".annotation system Ldalvik/annotation/AnnotationDefault;\n" +
+                "    value = .subannotation LAnnotationWithValues;\n" +
+                "                int<usage>Value = 4\n" +
+                "            .end subannotation\n" +
+                ".end annotation");
+        addFile("blarg.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".annotation runtime LAnnotationWithValues;\n" +
+                "  int<usage><ref>Value = 123\n" +
+                ".end annotation");
+        doTest();
+    }
+
+    public void testJavaUsageInSmaliFile() throws Exception {
+        addFile("AnnotationWithValues.java", "" +
+                "\n" +
+                "public @interface AnnotationWithValues {\n" +
+                "    int int<ref>Value() default 4;\n" +
+                "}");
+        addFile("blarg.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".annotation runtime LAnnotationWithValues;\n" +
+                "  int<usage>Value = 123\n" +
+                ".end annotation");
+        doTest();
+    }
+
+    public void testSmaliUsageInJavaFile() throws Exception {
+        addFile("AnnotationWithValues.smali", "" +
+                ".class public abstract interface annotation LAnnotationWithValues;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".implements Ljava/lang/annotation/Annotation;\n" +
+                "\n" +
+                ".method public abstract intValue()I\n" +
+                ".end method\n" +
+                ".annotation system Ldalvik/annotation/AnnotationDefault;\n" +
+                "    value = .subannotation LAnnotationWithValues;\n" +
+                "                int<usage><ref>Value = 4\n" +
+                "            .end subannotation\n" +
+                ".end annotation");
+        addFile("blarg.java", "" +
+                "\n" +
+                "@AnnotationWithValues(int<usage>Value=123)\n" +
+                "public class blarg {}");
+        doTest();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/FindClassUsagesTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindClassUsagesTest.java
new file mode 100644
index 0000000..810ac5b
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindClassUsagesTest.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+public class FindClassUsagesTest extends FindUsagesTest {
+    public void testSmaliUsageInSmaliFile() throws Exception {
+        addFile("blah.smali", ".class public Lbl<ref><usage>ah; .super Ljava/lang/Object;");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg; .super Ljava/lang/Object;\n" +
+                ".method public doSomething()V\n" +
+                "  .registers 1\n" +
+                "  new-instance v0, Lbl<usage>ah;\n" +
+                "  invoke-direct {v0}, Lbl<usage>ah;-><init>()V\n" +
+                "  return-void\n" +
+                ".end method");
+        doTest();
+    }
+
+    public void testSmaliUsageInJavaFile() throws Exception {
+        addFile("blah.smali", ".class public Lbl<ref><usage>ah; .super Ljava/lang/Object;");
+        addFile("blarg.java", "" +
+                "public class blarg {\n" +
+                "    public void blargMethod() {\n" +
+                "        new bl<usage>ah();\n" +
+                "    }\n" +
+                "}");
+        doTest();
+    }
+
+    public void testJavaUsageInSmaliFile() throws Exception {
+        addFile("blah.java", "" +
+                "public class blah {\n" +
+                "    public bl<usage>ah blahMethod() {\n" +
+                "        return new bl<usage><ref>ah();\n" +
+                "    }\n" +
+                "}");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg; .super Ljava/lang/Object;\n" +
+                ".method public doSomething()V\n" +
+                "  .registers 1\n" +
+                "  new-instance v0, Lbl<usage>ah;\n" +
+                "  invoke-direct {v0}, Lbl<usage>ah;-><init>()V\n" +
+                "  return-void\n" +
+                ".end method");
+        doTest();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/FindFieldUsagesTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindFieldUsagesTest.java
new file mode 100644
index 0000000..80c0054
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindFieldUsagesTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+public class FindFieldUsagesTest extends FindUsagesTest {
+    public void testSmaliUsageInSmaliFile() throws Exception {
+        addFile("blah.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".field blah<ref>Field:I");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blargMethod()V\n" +
+                "  iget v0, v0, Lblah;->blah<usage>Field:I\n" +
+                ".end method");
+        doTest();
+    }
+
+    public void testSmaliUsageInJavaFile() throws Exception {
+        addFile("blah.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".field blah<ref>Field:I");
+        addFile("blarg.java", "" +
+                "public class blarg {\n" +
+                "    public int blargMethod() {\n" +
+                "        blah b = new blah();\n" +
+                "        return b.blah<usage>Field;\n" +
+                "    }\n" +
+                "}");
+        doTest();
+    }
+
+    public void testJavaUsageInSmaliFile() throws Exception {
+        addFile("blah.java", "" +
+                "public class blah {\n" +
+                "    public int blah<ref>Field;\n" +
+                "}");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blargMethod()V\n" +
+                "  iget v0, v0, Lblah;->blah<usage>Field:I\n" +
+                ".end method");
+        doTest();
+    }
+
+    public void testPrimitiveListMethod() throws Exception {
+        addFile("blah.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".field II<ref>II:I");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blargMethod()V\n" +
+                "  iget v0, v0, Lblah;->II<usage>II:I\n" +
+                ".end method");
+       doTest();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/FindMethodUsagesTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindMethodUsagesTest.java
new file mode 100644
index 0000000..311c9ee
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindMethodUsagesTest.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+public class FindMethodUsagesTest extends FindUsagesTest {
+    public void testSmaliUsageInSmaliFile() throws Exception {
+        addFile("blah.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blah<ref>Method()V\n" +
+                ".end method");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blargMethod()V\n" +
+                "  invoke-virtual {v0}, Lblah;->blah<usage>Method()V\n" +
+                ".end method");
+        doTest();
+    }
+
+    public void testSmaliUsageInJavaFile() throws Exception {
+        addFile("blah.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blah<ref>Method()V\n" +
+                ".end method");
+        addFile("blarg.java", "" +
+                "public class blarg {\n" +
+                "    public void blargMethod() {\n" +
+                "        blah b = new blah();\n" +
+                "        b.blah<usage>Method();\n" +
+                "    }\n" +
+                "}");
+        doTest();
+    }
+
+    public void testJavaUsageInSmaliFile() throws Exception {
+        addFile("blah.java", "" +
+                "public class blah {\n" +
+                "    public void blah<ref>Method() {\n" +
+                "    }\n" +
+                "}");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blargMethod()V\n" +
+                "  invoke-virtual {v0}, Lblah;->blah<usage>Method()V\n" +
+                ".end method");
+        doTest();
+    }
+
+    public void testPrimitiveListMethod() throws Exception {
+        addFile("blah.smali", "" +
+                ".class public Lblah;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract II<ref>II()V\n" +
+                ".end method");
+        addFile("blarg.smali", "" +
+                ".class public Lblarg;\n" +
+                ".super Ljava/lang/Object;\n" +
+                ".method abstract blargMethod()V\n" +
+                "  invoke-virtual {v0}, Lblah;->II<usage>II()V\n" +
+                ".end method");
+       doTest();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/FindUsagesTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindUsagesTest.java
new file mode 100644
index 0000000..f4e2b0f
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/FindUsagesTest.java
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+import com.google.common.collect.Lists;
+import com.intellij.codeInsight.TargetElementUtilBase;
+import com.intellij.find.FindManager;
+import com.intellij.find.findUsages.FindUsagesHandler;
+import com.intellij.find.findUsages.FindUsagesManager;
+import com.intellij.find.findUsages.FindUsagesOptions;
+import com.intellij.find.impl.FindManagerImpl;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.testFramework.PsiTestCase;
+import com.intellij.usageView.UsageInfo;
+import com.intellij.usages.PsiElementUsageTarget;
+import com.intellij.usages.UsageTarget;
+import com.intellij.usages.UsageTargetUtil;
+import com.intellij.util.CommonProcessors;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class FindUsagesTest extends PsiTestCase {
+    public static final String USAGE_TAG = "<usage>";
+    public static final String REF_TAG = "<ref>";
+
+    private class TestFile {
+        @NotNull public final String fileName;
+        @NotNull public final String fileText;
+        @NotNull public final PsiFile psiFile;
+
+        public TestFile(@NotNull String fileName, @NotNull String fileText) throws Exception {
+            this.fileName = fileName;
+            this.fileText = fileText;
+            this.psiFile = createFile(fileName, getText());
+        }
+
+        @NotNull
+        public String getText() {
+            return fileText.replace(REF_TAG, "").replace(USAGE_TAG, "");
+        }
+
+        public int getRefIndex() {
+            return fileText.replace(USAGE_TAG, "").indexOf(REF_TAG);
+        }
+
+        public List<Integer> getUsageIndices() {
+            Matcher matcher = Pattern.compile(USAGE_TAG).matcher(fileText.replace(REF_TAG, ""));
+            List<Integer> matches = Lists.newArrayList();
+
+            int adjustment = 0;
+            while (matcher.find()) {
+                matches.add(matcher.start() - adjustment);
+                adjustment += USAGE_TAG.length();
+            }
+            return matches;
+        }
+    }
+
+    private List<TestFile> testFiles;
+
+    @Override
+    public void setUp() throws Exception {
+        testFiles = Lists.newArrayList();
+        super.setUp();
+    }
+
+    protected void addFile(String fileName, String fileText) throws Exception {
+        testFiles.add(new TestFile(fileName, fileText));
+    }
+
+    protected void doTest() {
+
+        PsiReference reference = null;
+        PsiElement targetElement = null;
+
+        for (TestFile testFile: testFiles) {
+            int refIndex = testFile.getRefIndex();
+            if (refIndex != -1) {
+                PsiElement element = testFile.psiFile.findElementAt(refIndex);
+
+                UsageTarget[] targets = UsageTargetUtil.findUsageTargets(element);
+                if (targets != null) {
+                    for (UsageTarget target : targets) {
+                        if (target instanceof PsiElementUsageTarget) {
+                            targetElement = ((PsiElementUsageTarget)target).getElement();
+                            break;
+                        }
+                    }
+                }
+
+                if (targetElement == null) {
+                    reference = testFile.psiFile.findReferenceAt(refIndex);
+                    if (reference != null) {
+                        targetElement = reference.resolve();
+                    } else {
+                        targetElement = TargetElementUtilBase.getInstance().getNamedElement(
+                                testFile.psiFile.findElementAt(refIndex), 0);
+                    }
+                }
+                break;
+            }
+        }
+
+        Assert.assertNotNull(targetElement);
+
+        Collection<UsageInfo> usages = findUsages(targetElement);
+        for (TestFile testFile: testFiles) {
+            assertUsages(testFile, usages);
+        }
+    }
+
+    private void assertUsages(@NotNull TestFile testFile, @NotNull Collection<UsageInfo> usages) {
+        List<UsageInfo> fileUsages = Lists.newArrayList();
+        for (UsageInfo usage: usages) {
+            if (usage.getFile().getName().equals(testFile.fileName)) {
+                fileUsages.add(usage);
+            }
+        }
+
+        for (Integer usageIndex: testFile.getUsageIndices()) {
+            boolean found = false;
+            for (UsageInfo usage: fileUsages) {
+                int startOffset = usage.getElement().getNode().getStartOffset();
+                int length = usage.getElement().getTextLength();
+
+                if (usageIndex >= startOffset && usageIndex < startOffset + length) {
+                    fileUsages.remove(usage);
+                    found = true;
+                    break;
+                }
+            }
+            Assert.assertTrue(found);
+        }
+        Assert.assertEquals(0, fileUsages.size());
+    }
+
+    private Collection<UsageInfo> findUsages(@NotNull PsiElement element) {
+        FindUsagesManager findUsagesManager =
+                ((FindManagerImpl)FindManager.getInstance(getProject())).getFindUsagesManager();
+
+        FindUsagesHandler findUsagesHandler =
+                findUsagesManager.getFindUsagesHandler(element, false);
+        Assert.assertNotNull(findUsagesHandler);
+
+        final FindUsagesOptions options = findUsagesHandler.getFindUsagesOptions();
+        final CommonProcessors.CollectProcessor<UsageInfo> processor =
+                new CommonProcessors.CollectProcessor<UsageInfo>();
+
+        for (PsiElement primaryElement : findUsagesHandler.getPrimaryElements()) {
+            findUsagesHandler.processElementUsages(primaryElement, processor, options);
+        }
+
+        for (PsiElement secondaryElement: findUsagesHandler.getSecondaryElements()) {
+            findUsagesHandler.processElementUsages(secondaryElement, processor, options);
+        }
+
+        return processor.getResults();
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/HighlightLocalClassUsagesTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/HighlightLocalClassUsagesTest.java
new file mode 100644
index 0000000..ffec82c
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/HighlightLocalClassUsagesTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2016, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.findUsages;
+
+import com.google.common.collect.Lists;
+import com.intellij.codeInsight.TargetElementUtilBase;
+import com.intellij.psi.PsiElement;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.search.LocalSearchScope;
+import com.intellij.psi.search.searches.ReferencesSearch;
+import com.intellij.testFramework.PsiTestCase;
+import org.junit.Assert;
+
+import java.util.List;
+
+public class HighlightLocalClassUsagesTest extends PsiTestCase {
+    public void testHighlightLocalClassUsage() throws Exception {
+        String fileText = "" +
+                ".class public Lbl<ref>arg; .super Ljava/lang/Object;\n" +
+                ".method public doSomething()V\n" +
+                "  .registers 1\n" +
+                "  new-instance v0, Lbl<ref>arg;\n" +
+                "  invoke-direct {v0}, Lblah;-><init>()V\n" +
+                "  return-void\n" +
+                ".end method";
+
+        PsiFile file = createFile("blarg.smali", fileText.replace("<ref>", ""));
+        PsiElement target;
+
+        int refIndex = fileText.indexOf("<ref>");
+        PsiReference reference = file.findReferenceAt(refIndex);
+        if (reference != null) {
+            target = reference.resolve();
+        } else {
+            target = TargetElementUtilBase.getInstance().getNamedElement(
+                    file.findElementAt(refIndex), 0);
+        }
+
+        final LocalSearchScope scope = new LocalSearchScope(file);
+
+        List<PsiReference> refs = Lists.newArrayList(ReferencesSearch.search(target, scope).findAll());
+        Assert.assertEquals(2, refs.size());
+
+        Assert.assertEquals(file.findElementAt(refIndex).getTextOffset(), refs.get(0).getElement().getTextOffset());
+        Assert.assertEquals(file.findElementAt(fileText.replaceFirst("<ref>", "").indexOf("<ref>")).getTextOffset(),
+                refs.get(1).getElement().getTextOffset());
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/MethodUsageTypeTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/MethodUsageTypeTest.java
new file mode 100644
index 0000000..eea3b15
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/MethodUsageTypeTest.java
@@ -0,0 +1,54 @@
+package org.jf.smalidea.findUsages;
+
+import com.intellij.usages.impl.rules.UsageType;
+
+public class MethodUsageTypeTest extends UsageTypeTest {
+    public MethodUsageTypeTest() {
+        super(new SmaliUsageTypeProvider());
+    }
+
+    public void testMethodUsageTypes() throws Exception {
+        doTest("blah.smali", "" +
+                        ".class public Lblah;\n" +
+                        ".super Ljava/lang/Object;\n" +
+                        "\n" +
+                        ".annotation runtime Lblah;\n" +
+                        "    element = Lblah;->bl<ref:1>ah()V;\n" +
+                        ".end annotation\n" +
+                        "\n" +
+                        ".method public blah()V\n" +
+                        "    .registers 2\n" +
+                        "\n" +
+                        "    invoke-direct {v0}, Lblah;->bl<ref:2>ah()V\n" +
+                        "    invoke-direct/empty {v0}, Lblah;->bl<ref:3>ah()V\n" +
+                        "    invoke-direct/range {v0}, Lblah;->bl<ref:4>ah()V\n" +
+                        "    invoke-interface {v0}, Lblah;->bl<ref:5>ah()V\n" +
+                        "    invoke-interface/range {v0}, Lblah;->bl<ref:6>ah()V\n" +
+                        "    invoke-object-init/range {v0}, Lblah;->bl<ref:7>ah()V\n" +
+                        "    invoke-static {v0}, Lblah;->bl<ref:8>ah()V\n" +
+                        "    invoke-static/range {v0}, Lblah;->bl<ref:9>ah()V\n" +
+                        "    invoke-super {v0}, Lblah;->bl<ref:10>ah()V\n" +
+                        "    invoke-super/range {v0}, Lblah;->bl<ref:11>ah()V\n" +
+                        "    invoke-virtual {v0}, Lblah;->bl<ref:12>ah()V\n" +
+                        "    invoke-virtual/range {v0}, Lblah;->bl<ref:13>ah()V\n" +
+                        "\n" +
+                        "    throw-verification-error generic-error, Lblah;->bl<ref:14>ah()V\n" +
+                        "\n" +
+                        "    return-void\n" +
+                        ".end method\n",
+                1, SmaliUsageTypeProvider.LITERAL,
+                2, UsageType.UNCLASSIFIED,
+                3, UsageType.UNCLASSIFIED,
+                4, UsageType.UNCLASSIFIED,
+                5, UsageType.UNCLASSIFIED,
+                6, UsageType.UNCLASSIFIED,
+                7, UsageType.UNCLASSIFIED,
+                8, UsageType.UNCLASSIFIED,
+                9, UsageType.UNCLASSIFIED,
+                10, UsageType.UNCLASSIFIED,
+                11, UsageType.UNCLASSIFIED,
+                12, UsageType.UNCLASSIFIED,
+                13, UsageType.UNCLASSIFIED,
+                14, SmaliUsageTypeProvider.VERIFICATION_ERROR);
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/findUsages/UsageTypeTest.java b/smalidea/src/test/java/org/jf/smalidea/findUsages/UsageTypeTest.java
new file mode 100644
index 0000000..44c2f49
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/findUsages/UsageTypeTest.java
@@ -0,0 +1,73 @@
+package org.jf.smalidea.findUsages;
+
+import com.google.common.collect.Maps;
+import com.intellij.psi.PsiFile;
+import com.intellij.psi.PsiReference;
+import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference;
+import com.intellij.testFramework.PsiTestCase;
+import com.intellij.usages.impl.rules.UsageType;
+import com.intellij.usages.impl.rules.UsageTypeProvider;
+import org.jetbrains.annotations.NotNull;
+import org.junit.Assert;
+
+import java.util.HashMap;
+import java.util.Map;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+public abstract class UsageTypeTest extends PsiTestCase {
+    // e.g. <ref:1>, <ref:1234>, etc.
+    private static final Pattern REF_PATTERN = Pattern.compile("(<ref:([0-9]+)>)");
+
+    @NotNull
+    private final UsageTypeProvider usageTypeProvider;
+
+    public UsageTypeTest(@NotNull UsageTypeProvider usageTypeProvider) {
+        this.usageTypeProvider = usageTypeProvider;
+    }
+
+    protected void doTest(@NotNull String fileName, @NotNull String text, @NotNull Object... expectedUsageTypes)
+            throws Exception {
+        Assert.assertTrue(expectedUsageTypes.length % 2 == 0);
+
+        Map<Integer, UsageType> expectedUsageTypesMap = Maps.newHashMap();
+        for (int i=0; i<expectedUsageTypes.length; i+=2) {
+            expectedUsageTypesMap.put((Integer) expectedUsageTypes[i], (UsageType) expectedUsageTypes[i + 1]);
+        }
+
+        PsiFile psiFile = createFile(fileName, REF_PATTERN.matcher(text).replaceAll(""));
+        Map<Integer, Integer> refIndexMap = getRefIndexes(text);
+
+        for (Map.Entry<Integer, Integer> entry: refIndexMap.entrySet()) {
+            int refId = entry.getKey();
+            int index = entry.getValue();
+
+            PsiReference reference = psiFile.getFirstChild().findReferenceAt(index);
+            Assert.assertNotNull(reference);
+            if (reference instanceof PsiMultiReference) {
+                // If there are multiple reference parents, the default seems to be the last one,
+                // i.e. the highest parent. We actually want the lowest one here.
+                reference = ((PsiMultiReference) reference).getReferences()[0];
+            }
+
+            UsageType usageType = usageTypeProvider.getUsageType(reference.getElement());
+            Assert.assertNotNull(usageType);
+            Assert.assertSame(expectedUsageTypesMap.get(refId), usageType);
+            expectedUsageTypesMap.remove(refId);
+        }
+        Assert.assertTrue(expectedUsageTypesMap.isEmpty());
+    }
+
+    @NotNull
+    private Map<Integer, Integer> getRefIndexes(@NotNull String text) {
+        Matcher m = REF_PATTERN.matcher(text);
+        int correction = 0;
+        Map<Integer, Integer> refIndexes = new HashMap<Integer, Integer>();
+        while (m.find()) {
+            int refId = Integer.parseInt(m.group(2));
+            refIndexes.put(refId, m.start() - correction);
+            correction += m.end() - m.start();
+        }
+        return refIndexes;
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/util/NameUtilsTest.java b/smalidea/src/test/java/org/jf/smalidea/util/NameUtilsTest.java
new file mode 100644
index 0000000..60aef8b
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/util/NameUtilsTest.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.util;
+
+import junit.framework.Assert;
+import org.junit.Test;
+
+public class NameUtilsTest {
+
+    @Test
+    public void testConversions() {
+        testConversion("boolean", "Z");
+        testConversion("byte", "B");
+        testConversion("char", "C");
+        testConversion("short", "S");
+        testConversion("int", "I");
+        testConversion("long", "J");
+        testConversion("float", "F");
+        testConversion("double", "D");
+
+        testConversion("blah", "Lblah;");
+        testConversion("my.blah", "Lmy/blah;");
+
+        testConversion("boolean[]", "[Z");
+        testConversion("byte[]", "[B");
+        testConversion("char[]", "[C");
+        testConversion("short[]", "[S");
+        testConversion("int[]", "[I");
+        testConversion("long[]", "[J");
+        testConversion("float[]", "[F");
+        testConversion("double[]", "[D");
+
+        testConversion("blah[]", "[Lblah;");
+        testConversion("my.blah[]", "[Lmy/blah;");
+
+        testConversion("boolean[][][][]", "[[[[Z");
+        testConversion("byte[][][][]", "[[[[B");
+        testConversion("char[][][][]", "[[[[C");
+        testConversion("short[][][][]", "[[[[S");
+        testConversion("int[][][][]", "[[[[I");
+        testConversion("long[][][][]", "[[[[J");
+        testConversion("float[][][][]", "[[[[F");
+        testConversion("double[][][][]", "[[[[D");
+
+        testConversion("blah[][][][]", "[[[[Lblah;");
+        testConversion("my.blah[][][][]", "[[[[Lmy/blah;");
+    }
+
+    private static void testConversion(String javaType, String smaliType) {
+        Assert.assertEquals(javaType, NameUtils.smaliToJavaType(smaliType));
+        Assert.assertEquals(smaliType, NameUtils.javaToSmaliType(javaType));
+    }
+
+    public void testShortNameFromQualifiedName() {
+        Assert.assertEquals("blah", NameUtils.shortNameFromQualifiedName("org.blah.blah"));
+        Assert.assertEquals("blah", NameUtils.shortNameFromQualifiedName("blah"));
+    }
+}
diff --git a/smalidea/src/test/java/org/jf/smalidea/util/StringUtilsTest.java b/smalidea/src/test/java/org/jf/smalidea/util/StringUtilsTest.java
new file mode 100644
index 0000000..4c5a9a6
--- /dev/null
+++ b/smalidea/src/test/java/org/jf/smalidea/util/StringUtilsTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.smalidea.util;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class StringUtilsTest {
+    @Test
+    public void testStringUtilsBasic() throws Exception {
+        String parsedString = StringUtils.parseQuotedString("\"abcd\"");
+        Assert.assertEquals("abcd", parsedString);
+    }
+
+    @Test
+    public void testStringUtilsQuotedChars() throws Exception {
+        String parsedString = StringUtils.parseQuotedString("\"a\\\\b\\nc\\uabcdd\"");
+        Assert.assertEquals("a\\b\nc\uabcdd", parsedString);
+    }
+}
diff --git a/smalidea/testData/Empty.smalidea b/smalidea/testData/Empty.smalidea
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/smalidea/testData/Empty.smalidea
diff --git a/smalidea/testData/Empty.txt b/smalidea/testData/Empty.txt
new file mode 100644
index 0000000..9165509
--- /dev/null
+++ b/smalidea/testData/Empty.txt
@@ -0,0 +1,8 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    PsiErrorElement:required (...)+ loop did not match anything at input '<EOF>'
+      <empty list>
\ No newline at end of file
diff --git a/smalidea/testData/FieldAnnotations.smalidea b/smalidea/testData/FieldAnnotations.smalidea
new file mode 100644
index 0000000..3716769
--- /dev/null
+++ b/smalidea/testData/FieldAnnotations.smalidea
@@ -0,0 +1,15 @@
+.field public blah:I
+
+.field public blah2:I
+    .annotation runtime Lblah;
+    .end annotation
+    .annotation runtime Lblah;
+    .end annotation
+.end field
+
+.field public blah2:I
+
+.annotation runtime Lblah;
+.end annotation
+.annotation runtime Lblah;
+.end annotation
diff --git a/smalidea/testData/FieldAnnotations.txt b/smalidea/testData/FieldAnnotations.txt
new file mode 100644
index 0000000..80332e5
--- /dev/null
+++ b/smalidea/testData/FieldAnnotations.txt
@@ -0,0 +1,91 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      PsiElement(COLON)(':')
+      PsiElement(PRIMITIVE_TYPE)
+        PsiElement(PRIMITIVE_TYPE)('I')
+    PsiWhiteSpace('\n\n')
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah2')
+      PsiElement(COLON)(':')
+      PsiElement(PRIMITIVE_TYPE)
+        PsiElement(PRIMITIVE_TYPE)('I')
+      PsiWhiteSpace('\n    ')
+      SmaliAnnotation(ANNOTATION)
+        PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+        PsiWhiteSpace(' ')
+        PsiElement(ANNOTATION_VISIBILITY)('runtime')
+        PsiWhiteSpace(' ')
+        PsiElement(CLASS_TYPE)
+          PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+        PsiWhiteSpace('\n    ')
+        PsiElement(ANNOTATION_PARAMETER_LIST)
+          <empty list>
+        PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+      PsiWhiteSpace('\n    ')
+      SmaliAnnotation(ANNOTATION)
+        PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+        PsiWhiteSpace(' ')
+        PsiElement(ANNOTATION_VISIBILITY)('runtime')
+        PsiWhiteSpace(' ')
+        PsiElement(CLASS_TYPE)
+          PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+        PsiWhiteSpace('\n    ')
+        PsiElement(ANNOTATION_PARAMETER_LIST)
+          <empty list>
+        PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+      PsiWhiteSpace('\n')
+      PsiElement(END_FIELD_DIRECTIVE)('.end field')
+    PsiWhiteSpace('\n\n')
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah2')
+      PsiElement(COLON)(':')
+      PsiElement(PRIMITIVE_TYPE)
+        PsiElement(PRIMITIVE_TYPE)('I')
+    PsiWhiteSpace('\n\n')
+    SmaliAnnotation(ANNOTATION)
+      PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+      PsiWhiteSpace(' ')
+      PsiElement(ANNOTATION_VISIBILITY)('runtime')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+      PsiWhiteSpace('\n')
+      PsiElement(ANNOTATION_PARAMETER_LIST)
+        <empty list>
+      PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+    PsiWhiteSpace('\n')
+    SmaliAnnotation(ANNOTATION)
+      PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+      PsiWhiteSpace(' ')
+      PsiElement(ANNOTATION_VISIBILITY)('runtime')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+      PsiWhiteSpace('\n')
+      PsiElement(ANNOTATION_PARAMETER_LIST)
+        <empty list>
+      PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidAnnotation.smalidea b/smalidea/testData/InvalidAnnotation.smalidea
new file mode 100644
index 0000000..9825e29
--- /dev/null
+++ b/smalidea/testData/InvalidAnnotation.smalidea
@@ -0,0 +1,19 @@
+.annotation .blah Lblah;
+.end annotation
+
+.annotation runtime .blah
+.end annotation
+
+.annotation runtime Lblah;
+ blah .blah .blah
+.end annotation
+
+.annotation runtime Lblah;
+ blah = .blah .blah
+.end annotation
+
+.annotation runtime Lblah;
+ blah = .subannotation Lblah2;
+           blah = "blah"
+        .blah
+.end annotation
\ No newline at end of file
diff --git a/smalidea/testData/InvalidAnnotation.txt b/smalidea/testData/InvalidAnnotation.txt
new file mode 100644
index 0000000..3799410
--- /dev/null
+++ b/smalidea/testData/InvalidAnnotation.txt
@@ -0,0 +1,108 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliAnnotation(ANNOTATION)
+      PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+      PsiWhiteSpace(' ')
+      PsiErrorElement:mismatched input '.blah' expecting ANNOTATION_VISIBILITY
+        PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+      PsiWhiteSpace('\n')
+      PsiElement(ANNOTATION_PARAMETER_LIST)
+        <empty list>
+      PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+    PsiWhiteSpace('\n\n')
+    SmaliAnnotation(ANNOTATION)
+      PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+      PsiWhiteSpace(' ')
+      PsiElement(ANNOTATION_VISIBILITY)('runtime')
+      PsiWhiteSpace(' ')
+      PsiErrorElement:mismatched input '.blah' expecting CLASS_DESCRIPTOR
+        PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(ANNOTATION_PARAMETER_LIST)
+        <empty list>
+      PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+    PsiWhiteSpace('\n\n')
+    SmaliAnnotation(ANNOTATION)
+      PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+      PsiWhiteSpace(' ')
+      PsiElement(ANNOTATION_VISIBILITY)('runtime')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+      PsiWhiteSpace('\n ')
+      PsiElement(ANNOTATION_PARAMETER_LIST)
+        PsiElement(ANNOTATION_ELEMENT)
+          PsiElement(ANNOTATION_ELEMENT_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiWhiteSpace(' ')
+          PsiErrorElement:mismatched input '.blah' expecting EQUAL
+            PsiElement(BAD_CHARACTER)('.blah')
+            PsiWhiteSpace(' ')
+            PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+    PsiWhiteSpace('\n\n')
+    SmaliAnnotation(ANNOTATION)
+      PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+      PsiWhiteSpace(' ')
+      PsiElement(ANNOTATION_VISIBILITY)('runtime')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+      PsiWhiteSpace('\n ')
+      PsiElement(ANNOTATION_PARAMETER_LIST)
+        PsiElement(ANNOTATION_ELEMENT)
+          PsiElement(ANNOTATION_ELEMENT_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiWhiteSpace(' ')
+          PsiElement(EQUAL)('=')
+          PsiWhiteSpace(' ')
+          PsiErrorElement:no viable alternative at input '.blah'
+            PsiElement(BAD_CHARACTER)('.blah')
+            PsiWhiteSpace(' ')
+            PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+    PsiWhiteSpace('\n\n')
+    SmaliAnnotation(ANNOTATION)
+      PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+      PsiWhiteSpace(' ')
+      PsiElement(ANNOTATION_VISIBILITY)('runtime')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+      PsiWhiteSpace('\n ')
+      PsiElement(ANNOTATION_PARAMETER_LIST)
+        PsiElement(ANNOTATION_ELEMENT)
+          PsiElement(ANNOTATION_ELEMENT_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiWhiteSpace(' ')
+          PsiElement(EQUAL)('=')
+          PsiWhiteSpace(' ')
+          SmaliAnnotation(ANNOTATION)
+            PsiElement(SUBANNOTATION_DIRECTIVE)('.subannotation')
+            PsiWhiteSpace(' ')
+            PsiElement(CLASS_TYPE)
+              PsiElement(CLASS_DESCRIPTOR)('Lblah2;')
+            PsiWhiteSpace('\n           ')
+            PsiElement(ANNOTATION_PARAMETER_LIST)
+              PsiElement(ANNOTATION_ELEMENT)
+                PsiElement(ANNOTATION_ELEMENT_NAME)
+                  PsiElement(SIMPLE_NAME)('blah')
+                PsiWhiteSpace(' ')
+                PsiElement(EQUAL)('=')
+                PsiWhiteSpace(' ')
+                PsiElement(LITERAL)
+                  PsiElement(STRING_LITERAL)('"blah"')
+            PsiWhiteSpace('\n        ')
+            PsiErrorElement:mismatched input '.blah' expecting END_SUBANNOTATION_DIRECTIVE
+              PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidClassDirective.smalidea b/smalidea/testData/InvalidClassDirective.smalidea
new file mode 100644
index 0000000..9f6b5ba
--- /dev/null
+++ b/smalidea/testData/InvalidClassDirective.smalidea
@@ -0,0 +1 @@
+.class
\ No newline at end of file
diff --git a/smalidea/testData/InvalidClassDirective.txt b/smalidea/testData/InvalidClassDirective.txt
new file mode 100644
index 0000000..e9f89c0
--- /dev/null
+++ b/smalidea/testData/InvalidClassDirective.txt
@@ -0,0 +1,12 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiErrorElement:mismatched input '<EOF>' expecting CLASS_DESCRIPTOR
+        <empty list>
\ No newline at end of file
diff --git a/smalidea/testData/InvalidClassDirective2.smalidea b/smalidea/testData/InvalidClassDirective2.smalidea
new file mode 100644
index 0000000..ee4d27e
--- /dev/null
+++ b/smalidea/testData/InvalidClassDirective2.smalidea
@@ -0,0 +1,2 @@
+.class
+.super Ljava/lang/Object;
\ No newline at end of file
diff --git a/smalidea/testData/InvalidClassDirective2.txt b/smalidea/testData/InvalidClassDirective2.txt
new file mode 100644
index 0000000..6f36abe
--- /dev/null
+++ b/smalidea/testData/InvalidClassDirective2.txt
@@ -0,0 +1,15 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace('\n')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiErrorElement:extraneous input '.super' expecting CLASS_DESCRIPTOR
+        PsiElement(SUPER_DIRECTIVE)('.super')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_DESCRIPTOR)('Ljava/lang/Object;')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidClassDirective3.smalidea b/smalidea/testData/InvalidClassDirective3.smalidea
new file mode 100644
index 0000000..fd5d360
--- /dev/null
+++ b/smalidea/testData/InvalidClassDirective3.smalidea
@@ -0,0 +1 @@
+.class public .class Ltest;
\ No newline at end of file
diff --git a/smalidea/testData/InvalidClassDirective3.txt b/smalidea/testData/InvalidClassDirective3.txt
new file mode 100644
index 0000000..ed98ac6
--- /dev/null
+++ b/smalidea/testData/InvalidClassDirective3.txt
@@ -0,0 +1,16 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiErrorElement:extraneous input '.class' expecting CLASS_DESCRIPTOR
+        PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_DESCRIPTOR)('Ltest;')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidEnumLiteral.smalidea b/smalidea/testData/InvalidEnumLiteral.smalidea
new file mode 100644
index 0000000..8853cc3
--- /dev/null
+++ b/smalidea/testData/InvalidEnumLiteral.smalidea
@@ -0,0 +1 @@
+.field public static blah:Ljava/lang/Object; = .enum Lblah;->blah .blah Lblah;
\ No newline at end of file
diff --git a/smalidea/testData/InvalidEnumLiteral.txt b/smalidea/testData/InvalidEnumLiteral.txt
new file mode 100644
index 0000000..b2acd87
--- /dev/null
+++ b/smalidea/testData/InvalidEnumLiteral.txt
@@ -0,0 +1,38 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+        PsiWhiteSpace(' ')
+        PsiElement(ACCESS_SPEC)('static')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      PsiElement(COLON)(':')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Ljava/lang/Object;')
+      PsiWhiteSpace(' ')
+      PsiElement(FIELD_INITIALIZER)
+        PsiElement(EQUAL)('=')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(ENUM_DIRECTIVE)('.enum')
+          PsiWhiteSpace(' ')
+          PsiElement(FIELD_REFERENCE)
+            PsiElement(CLASS_TYPE)
+              PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+            PsiElement(ARROW)('->')
+            PsiElement(MEMBER_NAME)
+              PsiElement(SIMPLE_NAME)('blah')
+            PsiWhiteSpace(' ')
+            PsiErrorElement:mismatched input '.blah' expecting COLON
+              PsiElement(BAD_CHARACTER)('.blah')
+            PsiWhiteSpace(' ')
+            PsiElement(CLASS_TYPE)
+              PsiElement(CLASS_DESCRIPTOR)('Lblah;')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField.smalidea b/smalidea/testData/InvalidField.smalidea
new file mode 100644
index 0000000..8eb1120
--- /dev/null
+++ b/smalidea/testData/InvalidField.smalidea
@@ -0,0 +1 @@
+.field
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField.txt b/smalidea/testData/InvalidField.txt
new file mode 100644
index 0000000..99aa29e
--- /dev/null
+++ b/smalidea/testData/InvalidField.txt
@@ -0,0 +1,13 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiErrorElement:no viable alternative at input '<EOF>'
+          <empty list>
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField2.smalidea b/smalidea/testData/InvalidField2.smalidea
new file mode 100644
index 0000000..8de7a3a
--- /dev/null
+++ b/smalidea/testData/InvalidField2.smalidea
@@ -0,0 +1 @@
+.field public blah .blah
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField2.txt b/smalidea/testData/InvalidField2.txt
new file mode 100644
index 0000000..b3e8227
--- /dev/null
+++ b/smalidea/testData/InvalidField2.txt
@@ -0,0 +1,17 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      PsiWhiteSpace(' ')
+      PsiErrorElement:mismatched input '.blah' expecting COLON
+        PsiElement(BAD_CHARACTER)('.blah')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField3.smalidea b/smalidea/testData/InvalidField3.smalidea
new file mode 100644
index 0000000..9de3142
--- /dev/null
+++ b/smalidea/testData/InvalidField3.smalidea
@@ -0,0 +1,3 @@
+.field public public:I
+.blah
+.end field
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField3.txt b/smalidea/testData/InvalidField3.txt
new file mode 100644
index 0000000..508df46
--- /dev/null
+++ b/smalidea/testData/InvalidField3.txt
@@ -0,0 +1,22 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiElement(COLON)(':')
+      PsiElement(PRIMITIVE_TYPE)
+        PsiElement(PRIMITIVE_TYPE)('I')
+    PsiWhiteSpace('\n')
+    PsiErrorElement:Unexpected tokens
+      PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(END_FIELD_DIRECTIVE)('.end field')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField4.smalidea b/smalidea/testData/InvalidField4.smalidea
new file mode 100644
index 0000000..11a8cb2
--- /dev/null
+++ b/smalidea/testData/InvalidField4.smalidea
@@ -0,0 +1 @@
+.field public blah:.blah
\ No newline at end of file
diff --git a/smalidea/testData/InvalidField4.txt b/smalidea/testData/InvalidField4.txt
new file mode 100644
index 0000000..4eedd93
--- /dev/null
+++ b/smalidea/testData/InvalidField4.txt
@@ -0,0 +1,17 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliField(FIELD)
+      PsiElement(FIELD_DIRECTIVE)('.field')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      PsiElement(COLON)(':')
+      PsiErrorElement:no viable alternative at input '.blah'
+        PsiElement(BAD_CHARACTER)('.blah')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidInstruction.smalidea b/smalidea/testData/InvalidInstruction.smalidea
new file mode 100644
index 0000000..0960b88
--- /dev/null
+++ b/smalidea/testData/InvalidInstruction.smalidea
@@ -0,0 +1,31 @@
+.method blah()V
+.registers 1
+invoke-virtual .blah v0}, Lblah;->blah()V
+.end method
+
+.method blah2()V
+.registers 1
+invoke-virtual {v1, v2} .blah Lblah;->blah()V
+.end method
+
+.method blah3()V
+  .array-data 4
+    1
+    2
+  .blah
+.end method
+
+.method blah4()V
+  .packed-switch 1
+    :blah
+    :blah2
+  .blah
+.end method
+
+.method blah5()V
+  .sparse-switch
+    1 -> :blah
+    3 -> :blah2
+    5 -> :blah3
+  .blah
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/InvalidInstruction.txt b/smalidea/testData/InvalidInstruction.txt
new file mode 100644
index 0000000..7650257
--- /dev/null
+++ b/smalidea/testData/InvalidInstruction.txt
@@ -0,0 +1,236 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+      PsiWhiteSpace('\n')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT35c_METHOD)('invoke-virtual')
+        PsiWhiteSpace(' ')
+        PsiErrorElement:mismatched input '.blah' expecting OPEN_BRACE
+          PsiElement(BAD_CHARACTER)('.blah')
+        PsiWhiteSpace(' ')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiElement(ARROW)('->')
+          PsiElement(MEMBER_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiElement(OPEN_PAREN)('(')
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+          PsiElement(CLOSE_PAREN)(')')
+          PsiElement(VOID_TYPE)
+            PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah2')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+      PsiWhiteSpace('\n')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT35c_METHOD)('invoke-virtual')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v1')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v2')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiWhiteSpace(' ')
+        PsiErrorElement:mismatched input '.blah' expecting COMMA
+          PsiElement(BAD_CHARACTER)('.blah')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiElement(ARROW)('->')
+          PsiElement(MEMBER_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiElement(OPEN_PAREN)('(')
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+          PsiElement(CLOSE_PAREN)(')')
+          PsiElement(VOID_TYPE)
+            PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah3')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n  ')
+      PsiElement(INSTRUCTION)
+        PsiElement(ARRAY_DATA_DIRECTIVE)('.array-data')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('4')
+        PsiWhiteSpace('\n    ')
+        PsiElement(ARRAY_DATA_ELEMENT)
+          PsiElement(LITERAL)
+            PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+        PsiWhiteSpace('\n    ')
+        PsiElement(ARRAY_DATA_ELEMENT)
+          PsiElement(LITERAL)
+            PsiElement(POSITIVE_INTEGER_LITERAL)('2')
+        PsiWhiteSpace('\n  ')
+        PsiErrorElement:mismatched input '.blah' expecting END_ARRAY_DATA_DIRECTIVE
+          PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah4')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n  ')
+      PsiElement(INSTRUCTION)
+        PsiElement(PACKED_SWITCH_DIRECTIVE)('.packed-switch')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+        PsiWhiteSpace('\n    ')
+        PsiElement(PACKED_SWITCH_ELEMENT)
+          PsiElement(LABEL_REFERENCE)
+            PsiElement(COLON)(':')
+            PsiElement(SIMPLE_NAME)('blah')
+        PsiWhiteSpace('\n    ')
+        PsiElement(PACKED_SWITCH_ELEMENT)
+          PsiElement(LABEL_REFERENCE)
+            PsiElement(COLON)(':')
+            PsiElement(SIMPLE_NAME)('blah2')
+        PsiWhiteSpace('\n  ')
+        PsiErrorElement:mismatched input '.blah' expecting END_PACKED_SWITCH_DIRECTIVE
+          PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah5')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n  ')
+      PsiElement(INSTRUCTION)
+        PsiElement(SPARSE_SWITCH_DIRECTIVE)('.sparse-switch')
+        PsiWhiteSpace('\n    ')
+        PsiElement(SPARSE_SWITCH_ELEMENT)
+          PsiElement(LITERAL)
+            PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+          PsiWhiteSpace(' ')
+          PsiElement(ARROW)('->')
+          PsiWhiteSpace(' ')
+          PsiElement(LABEL_REFERENCE)
+            PsiElement(COLON)(':')
+            PsiElement(SIMPLE_NAME)('blah')
+        PsiWhiteSpace('\n    ')
+        PsiElement(SPARSE_SWITCH_ELEMENT)
+          PsiElement(LITERAL)
+            PsiElement(POSITIVE_INTEGER_LITERAL)('3')
+          PsiWhiteSpace(' ')
+          PsiElement(ARROW)('->')
+          PsiWhiteSpace(' ')
+          PsiElement(LABEL_REFERENCE)
+            PsiElement(COLON)(':')
+            PsiElement(SIMPLE_NAME)('blah2')
+        PsiWhiteSpace('\n    ')
+        PsiElement(SPARSE_SWITCH_ELEMENT)
+          PsiElement(LITERAL)
+            PsiElement(POSITIVE_INTEGER_LITERAL)('5')
+          PsiWhiteSpace(' ')
+          PsiElement(ARROW)('->')
+          PsiWhiteSpace(' ')
+          PsiElement(LABEL_REFERENCE)
+            PsiElement(COLON)(':')
+            PsiElement(SIMPLE_NAME)('blah3')
+        PsiWhiteSpace('\n  ')
+        PsiErrorElement:mismatched input '.blah' expecting END_SPARSE_SWITCH_DIRECTIVE
+          PsiElement(BAD_CHARACTER)('.blah')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidLocal.smalidea b/smalidea/testData/InvalidLocal.smalidea
new file mode 100644
index 0000000..48bcd08
--- /dev/null
+++ b/smalidea/testData/InvalidLocal.smalidea
@@ -0,0 +1,7 @@
+.method public blah()V
+    .local v0,
+.end method
+
+.method public blah()V
+    .local v0,"":
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/InvalidLocal.txt b/smalidea/testData/InvalidLocal.txt
new file mode 100644
index 0000000..b639c46
--- /dev/null
+++ b/smalidea/testData/InvalidLocal.txt
@@ -0,0 +1,66 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n    ')
+      PsiElement(LOCAL_DEBUG_STATEMENT)
+        PsiElement(LOCAL_DIRECTIVE)('.local')
+        PsiWhiteSpace(' ')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(COMMA)(',')
+        PsiErrorElement:no viable alternative at input '.end method'
+          <empty list>
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n    ')
+      PsiElement(LOCAL_DEBUG_STATEMENT)
+        PsiElement(LOCAL_DIRECTIVE)('.local')
+        PsiWhiteSpace(' ')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(COMMA)(',')
+        PsiElement(LITERAL)
+          PsiElement(STRING_LITERAL)('""')
+        PsiElement(COLON)(':')
+        PsiErrorElement:no viable alternative at input '.end method'
+          <empty list>
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod.smalidea b/smalidea/testData/InvalidMethod.smalidea
new file mode 100644
index 0000000..57245e9
--- /dev/null
+++ b/smalidea/testData/InvalidMethod.smalidea
@@ -0,0 +1,4 @@
+.class Ltest;
+.super Ljava/lang/Object;
+
+.method .blah
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod.txt b/smalidea/testData/InvalidMethod.txt
new file mode 100644
index 0000000..421e084
--- /dev/null
+++ b/smalidea/testData/InvalidMethod.txt
@@ -0,0 +1,33 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Ltest;')
+    PsiWhiteSpace('\n')
+    PsiElement(SUPER_STATEMENT)
+      PsiElement(SUPER_DIRECTIVE)('.super')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Ljava/lang/Object;')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiErrorElement:no viable alternative at input '.blah'
+          PsiElement(BAD_CHARACTER)('.blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod2.smalidea b/smalidea/testData/InvalidMethod2.smalidea
new file mode 100644
index 0000000..d530143
--- /dev/null
+++ b/smalidea/testData/InvalidMethod2.smalidea
@@ -0,0 +1,6 @@
+.class Ltest;
+.super Ljava/lang/Object;
+
+.method blah
+
+.method
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod2.txt b/smalidea/testData/InvalidMethod2.txt
new file mode 100644
index 0000000..1a3a1cd
--- /dev/null
+++ b/smalidea/testData/InvalidMethod2.txt
@@ -0,0 +1,49 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Ltest;')
+    PsiWhiteSpace('\n')
+    PsiElement(SUPER_STATEMENT)
+      PsiElement(SUPER_DIRECTIVE)('.super')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Ljava/lang/Object;')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      PsiWhiteSpace('\n\n')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiErrorElement:mismatched input '.method' expecting OPEN_PAREN
+          <empty list>
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+      PsiErrorElement:missing END_METHOD_DIRECTIVE at '.method'
+        <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiErrorElement:no viable alternative at input '<EOF>'
+          <empty list>
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod3.smalidea b/smalidea/testData/InvalidMethod3.smalidea
new file mode 100644
index 0000000..24e66b6
--- /dev/null
+++ b/smalidea/testData/InvalidMethod3.smalidea
@@ -0,0 +1,11 @@
+.method blah()V
+.end
+
+
+
+.method return-object()V
+.registers 0
+
+
+
+.method
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod3.txt b/smalidea/testData/InvalidMethod3.txt
new file mode 100644
index 0000000..ce14371
--- /dev/null
+++ b/smalidea/testData/InvalidMethod3.txt
@@ -0,0 +1,63 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiErrorElement:mismatched input '.end' expecting END_METHOD_DIRECTIVE
+        PsiElement(BAD_CHARACTER)('.end')
+    PsiWhiteSpace('\n\n\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(INSTRUCTION_FORMAT11x)('return-object')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('0')
+      PsiErrorElement:missing END_METHOD_DIRECTIVE at '.method'
+        <empty list>
+    PsiWhiteSpace('\n\n\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiErrorElement:no viable alternative at input '<EOF>'
+          <empty list>
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod4.smalidea b/smalidea/testData/InvalidMethod4.smalidea
new file mode 100644
index 0000000..ff86bf3
--- /dev/null
+++ b/smalidea/testData/InvalidMethod4.smalidea
@@ -0,0 +1,8 @@
+.method blah .blah )V
+.end method
+
+.method blah(I .blah V
+.end method
+
+.method blah())V
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethod4.txt b/smalidea/testData/InvalidMethod4.txt
new file mode 100644
index 0000000..aae8065
--- /dev/null
+++ b/smalidea/testData/InvalidMethod4.txt
@@ -0,0 +1,73 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      PsiWhiteSpace(' ')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiErrorElement:mismatched input '.blah' expecting OPEN_PAREN
+          PsiElement(BAD_CHARACTER)('.blah')
+        PsiWhiteSpace(' ')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          SmaliMethodParameter(METHOD_PARAMETER)
+            SmaliModifierList(MODIFIER_LIST)
+              <empty list>
+            PsiElement(PRIMITIVE_TYPE)
+              PsiElement(PRIMITIVE_TYPE)('I')
+        PsiWhiteSpace(' ')
+        PsiErrorElement:mismatched input '.blah' expecting CLOSE_PAREN
+          PsiElement(BAD_CHARACTER)('.blah')
+        PsiWhiteSpace(' ')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiErrorElement:no viable alternative at input ')'
+          PsiElement(CLOSE_PAREN)(')')
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidMethodReference.smalidea b/smalidea/testData/InvalidMethodReference.smalidea
new file mode 100644
index 0000000..8ee6d79
--- /dev/null
+++ b/smalidea/testData/InvalidMethodReference.smalidea
@@ -0,0 +1,24 @@
+.method blah()V
+.registers 1
+invoke-virtual {v0}, .blah->
+.end method
+
+.method blah2()V
+.registers 1
+invoke-virtual {v0}, Lblah;.blah
+.end method
+
+.method blah3()V
+.registers 1
+invoke-virtual {v0}, Lblah;->blah .blah )V
+.end method
+
+.method blah4()V
+.registers 1
+invoke-virtual {v0}, Lblah;->blah(I .blah V
+.end method
+
+.method blah5()V
+.registers 1
+invoke-virtual {v0}, Lblah;->blah())V
+.end method
diff --git a/smalidea/testData/InvalidMethodReference.txt b/smalidea/testData/InvalidMethodReference.txt
new file mode 100644
index 0000000..490d38c
--- /dev/null
+++ b/smalidea/testData/InvalidMethodReference.txt
@@ -0,0 +1,241 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+      PsiWhiteSpace('\n')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT35c_METHOD)('invoke-virtual')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiErrorElement:no viable alternative at input '.blah-'
+            PsiElement(BAD_CHARACTER)('.blah-')
+            PsiElement(BAD_CHARACTER)('>')
+          PsiWhiteSpace('\n')
+          PsiElement(MEMBER_NAME)
+            <empty list>
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah2')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+      PsiWhiteSpace('\n')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT35c_METHOD)('invoke-virtual')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiErrorElement:mismatched input '.blah' expecting ARROW
+            PsiElement(BAD_CHARACTER)('.blah')
+          PsiWhiteSpace('\n')
+          PsiElement(MEMBER_NAME)
+            <empty list>
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah3')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+      PsiWhiteSpace('\n')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT35c_METHOD)('invoke-virtual')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiElement(ARROW)('->')
+          PsiElement(MEMBER_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiWhiteSpace(' ')
+          PsiErrorElement:mismatched input '.blah' expecting OPEN_PAREN
+            PsiElement(BAD_CHARACTER)('.blah')
+          PsiWhiteSpace(' ')
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+          PsiElement(CLOSE_PAREN)(')')
+          PsiElement(VOID_TYPE)
+            PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah4')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+      PsiWhiteSpace('\n')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT35c_METHOD)('invoke-virtual')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiElement(ARROW)('->')
+          PsiElement(MEMBER_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiElement(OPEN_PAREN)('(')
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            PsiElement(PRIMITIVE_TYPE)
+              PsiElement(PRIMITIVE_TYPE)('I')
+          PsiWhiteSpace(' ')
+          PsiErrorElement:mismatched input '.blah' expecting CLOSE_PAREN
+            PsiElement(BAD_CHARACTER)('.blah')
+          PsiWhiteSpace(' ')
+          PsiElement(VOID_TYPE)
+            PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah5')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(REGISTERS_STATEMENT)
+        PsiElement(REGISTERS_DIRECTIVE)('.registers')
+        PsiWhiteSpace(' ')
+        PsiElement(LITERAL)
+          PsiElement(POSITIVE_INTEGER_LITERAL)('1')
+      PsiWhiteSpace('\n')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT35c_METHOD)('invoke-virtual')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiElement(ARROW)('->')
+          PsiElement(MEMBER_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiElement(OPEN_PAREN)('(')
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+          PsiElement(CLOSE_PAREN)(')')
+          PsiErrorElement:no viable alternative at input ')'
+            PsiElement(CLOSE_PAREN)(')')
+            PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/InvalidParameter.smalidea b/smalidea/testData/InvalidParameter.smalidea
new file mode 100644
index 0000000..014299f
--- /dev/null
+++ b/smalidea/testData/InvalidParameter.smalidea
@@ -0,0 +1,10 @@
+.method public blah()V
+    .param v0, "blah"
+      .a
+.end method
+
+.method public blah()V
+    .param v0, "blah"
+    .annotation runtime Lblah; .end annotation
+    .
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/InvalidParameter.txt b/smalidea/testData/InvalidParameter.txt
new file mode 100644
index 0000000..27168ef
--- /dev/null
+++ b/smalidea/testData/InvalidParameter.txt
@@ -0,0 +1,85 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n    ')
+      PsiElement(PARAMETER_STATEMENT)
+        PsiElement(PARAMETER_DIRECTIVE)('.param')
+        PsiWhiteSpace(' ')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(LOCAL_NAME)
+          PsiElement(LITERAL)
+            PsiElement(STRING_LITERAL)('"blah"')
+      PsiWhiteSpace('\n      ')
+      PsiErrorElement:no viable alternative at input '.a'
+        PsiElement(BAD_CHARACTER)('.a')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n    ')
+      PsiElement(PARAMETER_STATEMENT)
+        PsiElement(PARAMETER_DIRECTIVE)('.param')
+        PsiWhiteSpace(' ')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(LOCAL_NAME)
+          PsiElement(LITERAL)
+            PsiElement(STRING_LITERAL)('"blah"')
+      PsiWhiteSpace('\n    ')
+      SmaliAnnotation(ANNOTATION)
+        PsiElement(ANNOTATION_DIRECTIVE)('.annotation')
+        PsiWhiteSpace(' ')
+        PsiElement(ANNOTATION_VISIBILITY)('runtime')
+        PsiWhiteSpace(' ')
+        PsiElement(CLASS_TYPE)
+          PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+        PsiWhiteSpace(' ')
+        PsiElement(ANNOTATION_PARAMETER_LIST)
+          <empty list>
+        PsiElement(END_ANNOTATION_DIRECTIVE)('.end annotation')
+      PsiWhiteSpace('\n    ')
+      PsiErrorElement:no viable alternative at input '.'
+        PsiElement(BAD_CHARACTER)('.')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/MissingDotDot.smalidea b/smalidea/testData/MissingDotDot.smalidea
new file mode 100644
index 0000000..5e8e6b6
--- /dev/null
+++ b/smalidea/testData/MissingDotDot.smalidea
@@ -0,0 +1,9 @@
+.method public blah()V
+    invoke-virtual/range {v0 v1}, Lblah;->blah()V
+
+    invoke-virtual/range {v0 .blah v1}, Lblah;->blah()V
+
+    .catch Ljava/lang/Exception; { :blah1 :blah2 } :blah3
+
+    .catch Ljava/lang/Exception; { :blah1 .blah :blah2 } :blah3
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/MissingDotDot.txt b/smalidea/testData/MissingDotDot.txt
new file mode 100644
index 0000000..53a1ecc
--- /dev/null
+++ b/smalidea/testData/MissingDotDot.txt
@@ -0,0 +1,126 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        PsiElement(ACCESS_SPEC)('public')
+      PsiWhiteSpace(' ')
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n    ')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT3rc_METHOD)('invoke-virtual/range')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiWhiteSpace(' ')
+        PsiErrorElement:extraneous input 'v1' expecting CLOSE_BRACE
+          PsiElement(REGISTER)('v1')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiElement(ARROW)('->')
+          PsiElement(MEMBER_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiElement(OPEN_PAREN)('(')
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+          PsiElement(CLOSE_PAREN)(')')
+          PsiElement(VOID_TYPE)
+            PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n\n    ')
+      PsiElement(INSTRUCTION)
+        PsiElement(INSTRUCTION_FORMAT3rc_METHOD)('invoke-virtual/range')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiElement(REGISTER_REFERENCE)
+          PsiElement(REGISTER)('v0')
+        PsiWhiteSpace(' ')
+        PsiErrorElement:mismatched input '.blah' expecting CLOSE_BRACE
+          PsiElement(BAD_CHARACTER)('.blah')
+          PsiWhiteSpace(' ')
+          PsiElement(REGISTER)('v1')
+          PsiElement(CLOSE_BRACE)('}')
+        PsiElement(COMMA)(',')
+        PsiWhiteSpace(' ')
+        PsiElement(METHOD_REFERENCE)
+          PsiElement(CLASS_TYPE)
+            PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+          PsiElement(ARROW)('->')
+          PsiElement(MEMBER_NAME)
+            PsiElement(SIMPLE_NAME)('blah')
+          PsiElement(OPEN_PAREN)('(')
+          PsiElement(METHOD_REFERENCE_PARAM_LIST)
+            <empty list>
+          PsiElement(CLOSE_PAREN)(')')
+          PsiElement(VOID_TYPE)
+            PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n\n    ')
+      PsiElement(CATCH_STATEMENT)
+        PsiElement(CATCH_DIRECTIVE)('.catch')
+        PsiWhiteSpace(' ')
+        PsiElement(CLASS_TYPE)
+          PsiElement(CLASS_DESCRIPTOR)('Ljava/lang/Exception;')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiWhiteSpace(' ')
+        PsiElement(LABEL_REFERENCE)
+          PsiElement(COLON)(':')
+          PsiElement(SIMPLE_NAME)('blah1')
+        PsiErrorElement:missing DOTDOT at ':'
+          <empty list>
+        PsiWhiteSpace(' ')
+        PsiElement(LABEL_REFERENCE)
+          PsiElement(COLON)(':')
+          PsiElement(SIMPLE_NAME)('blah2')
+        PsiWhiteSpace(' ')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiWhiteSpace(' ')
+        PsiElement(LABEL_REFERENCE)
+          PsiElement(COLON)(':')
+          PsiElement(SIMPLE_NAME)('blah3')
+      PsiWhiteSpace('\n\n    ')
+      PsiElement(CATCH_STATEMENT)
+        PsiElement(CATCH_DIRECTIVE)('.catch')
+        PsiWhiteSpace(' ')
+        PsiElement(CLASS_TYPE)
+          PsiElement(CLASS_DESCRIPTOR)('Ljava/lang/Exception;')
+        PsiWhiteSpace(' ')
+        PsiElement(OPEN_BRACE)('{')
+        PsiWhiteSpace(' ')
+        PsiElement(LABEL_REFERENCE)
+          PsiElement(COLON)(':')
+          PsiElement(SIMPLE_NAME)('blah1')
+        PsiWhiteSpace(' ')
+        PsiErrorElement:mismatched input '.blah' expecting DOTDOT
+          PsiElement(BAD_CHARACTER)('.blah')
+        PsiWhiteSpace(' ')
+        PsiElement(LABEL_REFERENCE)
+          PsiElement(COLON)(':')
+          PsiElement(SIMPLE_NAME)('blah2')
+        PsiWhiteSpace(' ')
+        PsiElement(CLOSE_BRACE)('}')
+        PsiWhiteSpace(' ')
+        PsiElement(LABEL_REFERENCE)
+          PsiElement(COLON)(':')
+          PsiElement(SIMPLE_NAME)('blah3')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/ParamListInvalidParameter.smalidea b/smalidea/testData/ParamListInvalidParameter.smalidea
new file mode 100644
index 0000000..f9a8728
--- /dev/null
+++ b/smalidea/testData/ParamListInvalidParameter.smalidea
@@ -0,0 +1,5 @@
+.class Lblah;
+.super Ljava/lang/Object;
+
+.method blah(f)
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/ParamListInvalidParameter.txt b/smalidea/testData/ParamListInvalidParameter.txt
new file mode 100644
index 0000000..ea94d46
--- /dev/null
+++ b/smalidea/testData/ParamListInvalidParameter.txt
@@ -0,0 +1,38 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+    PsiWhiteSpace('\n')
+    PsiElement(SUPER_STATEMENT)
+      PsiElement(SUPER_DIRECTIVE)('.super')
+      PsiWhiteSpace(' ')
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Ljava/lang/Object;')
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiErrorElement:extraneous input 'f' expecting CLOSE_PAREN
+          PsiElement(SIMPLE_NAME)('f')
+        PsiElement(CLOSE_PAREN)(')')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/SuperClassInvalidSyntax.smalidea b/smalidea/testData/SuperClassInvalidSyntax.smalidea
new file mode 100644
index 0000000..cea9d10
--- /dev/null
+++ b/smalidea/testData/SuperClassInvalidSyntax.smalidea
@@ -0,0 +1,2 @@
+.class Lblah;
+.super
\ No newline at end of file
diff --git a/smalidea/testData/SuperClassInvalidSyntax.txt b/smalidea/testData/SuperClassInvalidSyntax.txt
new file mode 100644
index 0000000..316041c
--- /dev/null
+++ b/smalidea/testData/SuperClassInvalidSyntax.txt
@@ -0,0 +1,18 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+    PsiWhiteSpace('\n')
+    PsiElement(SUPER_STATEMENT)
+      PsiElement(SUPER_DIRECTIVE)('.super')
+      PsiErrorElement:mismatched input '<EOF>' expecting CLASS_DESCRIPTOR
+        <empty list>
\ No newline at end of file
diff --git a/smalidea/testData/SuperClassInvalidSyntax2.smalidea b/smalidea/testData/SuperClassInvalidSyntax2.smalidea
new file mode 100644
index 0000000..8793e8c
--- /dev/null
+++ b/smalidea/testData/SuperClassInvalidSyntax2.smalidea
@@ -0,0 +1,5 @@
+.class Lblah;
+.super
+
+.method blah()V
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/SuperClassInvalidSyntax2.txt b/smalidea/testData/SuperClassInvalidSyntax2.txt
new file mode 100644
index 0000000..348fab4
--- /dev/null
+++ b/smalidea/testData/SuperClassInvalidSyntax2.txt
@@ -0,0 +1,37 @@
+smali.FILE
+  SmaliClass(CLASS)
+    SmaliExtendsList(EXTENDS_LIST)
+      <empty list>
+    SmaliImplementsList(IMPLEMENTS_LIST)
+      <empty list>
+    SmaliClassStatement(CLASS_STATEMENT)
+      PsiElement(CLASS_DIRECTIVE)('.class')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(CLASS_TYPE)
+        PsiElement(CLASS_DESCRIPTOR)('Lblah;')
+    PsiWhiteSpace('\n')
+    PsiElement(SUPER_STATEMENT)
+      PsiElement(SUPER_DIRECTIVE)('.super')
+      PsiErrorElement:missing CLASS_DESCRIPTOR at '.method'
+        <empty list>
+    PsiWhiteSpace('\n\n')
+    SmaliMethod(METHOD)
+      SmaliThrowsList(THROWS_LIST)
+        <empty list>
+      PsiElement(METHOD_DIRECTIVE)('.method')
+      PsiWhiteSpace(' ')
+      SmaliModifierList(MODIFIER_LIST)
+        <empty list>
+      PsiElement(MEMBER_NAME)
+        PsiElement(SIMPLE_NAME)('blah')
+      SmaliMethodPrototype(METHOD_PROTOTYPE)
+        PsiElement(OPEN_PAREN)('(')
+        SmaliMethodParamList(METHOD_PARAM_LIST)
+          <empty list>
+        PsiElement(CLOSE_PAREN)(')')
+        PsiElement(VOID_TYPE)
+          PsiElement(VOID_TYPE)('V')
+      PsiWhiteSpace('\n')
+      PsiElement(END_METHOD_DIRECTIVE)('.end method')
\ No newline at end of file
diff --git a/smalidea/testData/classMove/basicFromNoPackage/after/my/blah.smali b/smalidea/testData/classMove/basicFromNoPackage/after/my/blah.smali
new file mode 100644
index 0000000..dc4522c
--- /dev/null
+++ b/smalidea/testData/classMove/basicFromNoPackage/after/my/blah.smali
@@ -0,0 +1,36 @@
+.class public Lmy/blah;
+.super Lmy/blah;
+.implements Lmy/blah;
+
+.annotation build Lmy/blah;
+    value = .subannotation Lmy/blah;
+                value = Lmy/blah;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lmy/blah; = Lmy/blah;
+
+.method public blah(Lmy/blah;)Lmy/blah;
+    .registers 2
+    .local p0, "this":Lmy/blah;
+
+    :start
+        iget-object v0, v0, Lmy/blah;->blah:Lmy/blah;
+
+        invoke-virtual {v0}, Lmy/blah;->blah(Lmy/blah;)Lmy/blah;
+
+        instance-of v0, v0, Lmy/blah;
+        check-cast v0, Lmy/blah;
+        new-instance v0, Lmy/blah;
+        const-class v0, Lmy/blah;
+        throw-verification-error generic-error, Lmy/blah;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lmy/blah;
+        new-array v0, v0, Lmy/blah;
+        filled-new-array/range {v0}, Lmy/blah;
+    :end
+
+    .catch Lmy/blah; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/classMove/basicFromNoPackage/before/blah.smali b/smalidea/testData/classMove/basicFromNoPackage/before/blah.smali
new file mode 100644
index 0000000..0a72c71
--- /dev/null
+++ b/smalidea/testData/classMove/basicFromNoPackage/before/blah.smali
@@ -0,0 +1,36 @@
+.class public Lblah;
+.super Lblah;
+.implements Lblah;
+
+.annotation build Lblah;
+    value = .subannotation Lblah;
+                value = Lblah;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lblah; = Lblah;
+
+.method public blah(Lblah;)Lblah;
+    .registers 2
+    .local p0, "this":Lblah;
+
+    :start
+        iget-object v0, v0, Lblah;->blah:Lblah;
+
+        invoke-virtual {v0}, Lblah;->blah(Lblah;)Lblah;
+
+        instance-of v0, v0, Lblah;
+        check-cast v0, Lblah;
+        new-instance v0, Lblah;
+        const-class v0, Lblah;
+        throw-verification-error generic-error, Lblah;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lblah;
+        new-array v0, v0, Lblah;
+        filled-new-array/range {v0}, Lblah;
+    :end
+
+    .catch Lblah; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/classMove/basicToNoPackage/after/blah.smali b/smalidea/testData/classMove/basicToNoPackage/after/blah.smali
new file mode 100644
index 0000000..0a72c71
--- /dev/null
+++ b/smalidea/testData/classMove/basicToNoPackage/after/blah.smali
@@ -0,0 +1,36 @@
+.class public Lblah;
+.super Lblah;
+.implements Lblah;
+
+.annotation build Lblah;
+    value = .subannotation Lblah;
+                value = Lblah;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lblah; = Lblah;
+
+.method public blah(Lblah;)Lblah;
+    .registers 2
+    .local p0, "this":Lblah;
+
+    :start
+        iget-object v0, v0, Lblah;->blah:Lblah;
+
+        invoke-virtual {v0}, Lblah;->blah(Lblah;)Lblah;
+
+        instance-of v0, v0, Lblah;
+        check-cast v0, Lblah;
+        new-instance v0, Lblah;
+        const-class v0, Lblah;
+        throw-verification-error generic-error, Lblah;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lblah;
+        new-array v0, v0, Lblah;
+        filled-new-array/range {v0}, Lblah;
+    :end
+
+    .catch Lblah; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/classMove/basicToNoPackage/after/my/placeholder.smali b/smalidea/testData/classMove/basicToNoPackage/after/my/placeholder.smali
new file mode 100644
index 0000000..0281344
--- /dev/null
+++ b/smalidea/testData/classMove/basicToNoPackage/after/my/placeholder.smali
@@ -0,0 +1,2 @@
+.class public Lmy/placeholder;
+.super Ljava/lang/Object;
\ No newline at end of file
diff --git a/smalidea/testData/classMove/basicToNoPackage/before/my/blah.smali b/smalidea/testData/classMove/basicToNoPackage/before/my/blah.smali
new file mode 100644
index 0000000..dc4522c
--- /dev/null
+++ b/smalidea/testData/classMove/basicToNoPackage/before/my/blah.smali
@@ -0,0 +1,36 @@
+.class public Lmy/blah;
+.super Lmy/blah;
+.implements Lmy/blah;
+
+.annotation build Lmy/blah;
+    value = .subannotation Lmy/blah;
+                value = Lmy/blah;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lmy/blah; = Lmy/blah;
+
+.method public blah(Lmy/blah;)Lmy/blah;
+    .registers 2
+    .local p0, "this":Lmy/blah;
+
+    :start
+        iget-object v0, v0, Lmy/blah;->blah:Lmy/blah;
+
+        invoke-virtual {v0}, Lmy/blah;->blah(Lmy/blah;)Lmy/blah;
+
+        instance-of v0, v0, Lmy/blah;
+        check-cast v0, Lmy/blah;
+        new-instance v0, Lmy/blah;
+        const-class v0, Lmy/blah;
+        throw-verification-error generic-error, Lmy/blah;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lmy/blah;
+        new-array v0, v0, Lmy/blah;
+        filled-new-array/range {v0}, Lmy/blah;
+    :end
+
+    .catch Lmy/blah; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/classMove/basicToNoPackage/before/my/placeholder.smali b/smalidea/testData/classMove/basicToNoPackage/before/my/placeholder.smali
new file mode 100644
index 0000000..0281344
--- /dev/null
+++ b/smalidea/testData/classMove/basicToNoPackage/before/my/placeholder.smali
@@ -0,0 +1,2 @@
+.class public Lmy/placeholder;
+.super Ljava/lang/Object;
\ No newline at end of file
diff --git a/smalidea/testData/classRename/basicNoPackage/after/blah2.smali b/smalidea/testData/classRename/basicNoPackage/after/blah2.smali
new file mode 100644
index 0000000..112b106
--- /dev/null
+++ b/smalidea/testData/classRename/basicNoPackage/after/blah2.smali
@@ -0,0 +1,36 @@
+.class public Lblah2;
+.super Lblah2;
+.implements Lblah2;
+
+.annotation build Lblah2;
+    value = .subannotation Lblah2;
+                value = Lblah2;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lblah2; = Lblah2;
+
+.method public blah(Lblah2;)Lblah2;
+    .registers 2
+    .local p0, "this":Lblah2;
+
+    :start
+        iget-object v0, v0, Lblah2;->blah:Lblah2;
+
+        invoke-virtual {v0}, Lblah2;->blah(Lblah2;)Lblah2;
+
+        instance-of v0, v0, Lblah2;
+        check-cast v0, Lblah2;
+        new-instance v0, Lblah2;
+        const-class v0, Lblah2;
+        throw-verification-error generic-error, Lblah2;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lblah2;
+        new-array v0, v0, Lblah2;
+        filled-new-array/range {v0}, Lblah2;
+    :end
+
+    .catch Lblah2; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/classRename/basicNoPackage/before/blah.smali b/smalidea/testData/classRename/basicNoPackage/before/blah.smali
new file mode 100644
index 0000000..0a72c71
--- /dev/null
+++ b/smalidea/testData/classRename/basicNoPackage/before/blah.smali
@@ -0,0 +1,36 @@
+.class public Lblah;
+.super Lblah;
+.implements Lblah;
+
+.annotation build Lblah;
+    value = .subannotation Lblah;
+                value = Lblah;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lblah; = Lblah;
+
+.method public blah(Lblah;)Lblah;
+    .registers 2
+    .local p0, "this":Lblah;
+
+    :start
+        iget-object v0, v0, Lblah;->blah:Lblah;
+
+        invoke-virtual {v0}, Lblah;->blah(Lblah;)Lblah;
+
+        instance-of v0, v0, Lblah;
+        check-cast v0, Lblah;
+        new-instance v0, Lblah;
+        const-class v0, Lblah;
+        throw-verification-error generic-error, Lblah;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lblah;
+        new-array v0, v0, Lblah;
+        filled-new-array/range {v0}, Lblah;
+    :end
+
+    .catch Lblah; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/classRename/basicWithPackage/after/my/blah2.smali b/smalidea/testData/classRename/basicWithPackage/after/my/blah2.smali
new file mode 100644
index 0000000..f08d513
--- /dev/null
+++ b/smalidea/testData/classRename/basicWithPackage/after/my/blah2.smali
@@ -0,0 +1,36 @@
+.class public Lmy/blah2;
+.super Lmy/blah2;
+.implements Lmy/blah2;
+
+.annotation build Lmy/blah2;
+    value = .subannotation Lmy/blah2;
+                value = Lmy/blah2;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lmy/blah2; = Lmy/blah2;
+
+.method public blah(Lmy/blah2;)Lmy/blah2;
+    .registers 2
+    .local p0, "this":Lmy/blah2;
+
+    :start
+        iget-object v0, v0, Lmy/blah2;->blah:Lmy/blah2;
+
+        invoke-virtual {v0}, Lmy/blah2;->blah(Lmy/blah2;)Lmy/blah2;
+
+        instance-of v0, v0, Lmy/blah2;
+        check-cast v0, Lmy/blah2;
+        new-instance v0, Lmy/blah2;
+        const-class v0, Lmy/blah2;
+        throw-verification-error generic-error, Lmy/blah2;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lmy/blah2;
+        new-array v0, v0, Lmy/blah2;
+        filled-new-array/range {v0}, Lmy/blah2;
+    :end
+
+    .catch Lmy/blah2; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/classRename/basicWithPackage/before/my/blah.smali b/smalidea/testData/classRename/basicWithPackage/before/my/blah.smali
new file mode 100644
index 0000000..dc4522c
--- /dev/null
+++ b/smalidea/testData/classRename/basicWithPackage/before/my/blah.smali
@@ -0,0 +1,36 @@
+.class public Lmy/blah;
+.super Lmy/blah;
+.implements Lmy/blah;
+
+.annotation build Lmy/blah;
+    value = .subannotation Lmy/blah;
+                value = Lmy/blah;
+            .end subannotation
+.end annotation
+
+.field static public blah:Lmy/blah; = Lmy/blah;
+
+.method public blah(Lmy/blah;)Lmy/blah;
+    .registers 2
+    .local p0, "this":Lmy/blah;
+
+    :start
+        iget-object v0, v0, Lmy/blah;->blah:Lmy/blah;
+
+        invoke-virtual {v0}, Lmy/blah;->blah(Lmy/blah;)Lmy/blah;
+
+        instance-of v0, v0, Lmy/blah;
+        check-cast v0, Lmy/blah;
+        new-instance v0, Lmy/blah;
+        const-class v0, Lmy/blah;
+        throw-verification-error generic-error, Lmy/blah;
+
+        filled-new-array {v0, v0, v0, v0, v0}, Lmy/blah;
+        new-array v0, v0, Lmy/blah;
+        filled-new-array/range {v0}, Lmy/blah;
+    :end
+
+    .catch Lmy/blah; { :start .. :end } :handler
+    :handler
+    return-void
+.end method
\ No newline at end of file
diff --git a/smalidea/testData/fieldRename/fieldRename/after/blah.smali b/smalidea/testData/fieldRename/fieldRename/after/blah.smali
new file mode 100644
index 0000000..e2a2369
--- /dev/null
+++ b/smalidea/testData/fieldRename/fieldRename/after/blah.smali
@@ -0,0 +1,59 @@
+.class public Lblah;
+.super Ljava/lang/Object;
+
+.annotation runtime Lblah;
+    element = Lblah;->blort:Lblah;
+    element2 = .enum Lblah;->blort:Lblah;
+.end annotation
+
+.field public blort:Lblah;
+
+.method public blah(Lblah;)Lblah;
+    .registers 2
+
+    iget v0, v0, Lblah;->blort:Lblah;
+    iget-object v0, v0, Lblah;->blort:Lblah;
+    iget-byte v0, v0, Lblah;->blort:Lblah;
+    iget-char v0, v0, Lblah;->blort:Lblah;
+    iget-object v0, v0, Lblah;->blort:Lblah;
+    iget-object-volatile v0, v0, Lblah;->blort:Lblah;
+    iget-short v0, v0, Lblah;->blort:Lblah;
+    iget-volatile v0, v0, Lblah;->blort:Lblah;
+    iget-wide v0, v0, Lblah;->blort:Lblah;
+    iget-wide-volatile v0, v0, Lblah;->blort:Lblah;
+    sget v0, Lblah;->blort:Lblah;
+    sget-boolean v0, Lblah;->blort:Lblah;
+    sget-byte v0, Lblah;->blort:Lblah;
+    sget-char v0, Lblah;->blort:Lblah;
+    sget-object v0, Lblah;->blort:Lblah;
+    sget-object-volatile v0, Lblah;->blort:Lblah;
+    sget-short v0, Lblah;->blort:Lblah;
+    sget-volatile v0, Lblah;->blort:Lblah;
+    sget-wide v0, Lblah;->blort:Lblah;
+    sget-wide-volatile v0, Lblah;->blort:Lblah;
+
+    iput v0, v0, Lblah;->blort:Lblah;
+    iput-object v0, v0, Lblah;->blort:Lblah;
+    iput-byte v0, v0, Lblah;->blort:Lblah;
+    iput-char v0, v0, Lblah;->blort:Lblah;
+    iput-object v0, v0, Lblah;->blort:Lblah;
+    iput-object-volatile v0, v0, Lblah;->blort:Lblah;
+    iput-short v0, v0, Lblah;->blort:Lblah;
+    iput-volatile v0, v0, Lblah;->blort:Lblah;
+    iput-wide v0, v0, Lblah;->blort:Lblah;
+    iput-wide-volatile v0, v0, Lblah;->blort:Lblah;
+    sput v0, Lblah;->blort:Lblah;
+    sput-boolean v0, Lblah;->blort:Lblah;
+    sput-byte v0, Lblah;->blort:Lblah;
+    sput-char v0, Lblah;->blort:Lblah;
+    sput-object v0, Lblah;->blort:Lblah;
+    sput-object-volatile v0, Lblah;->blort:Lblah;
+    sput-short v0, Lblah;->blort:Lblah;
+    sput-volatile v0, Lblah;->blort:Lblah;
+    sput-wide v0, Lblah;->blort:Lblah;
+    sput-wide-volatile v0, Lblah;->blort:Lblah;
+
+    throw-verification-error generic-error, Lblah;->blort:Lblah;
+
+    return-void
+.end method
diff --git a/smalidea/testData/fieldRename/fieldRename/before/blah.smali b/smalidea/testData/fieldRename/fieldRename/before/blah.smali
new file mode 100644
index 0000000..ab4dc6c
--- /dev/null
+++ b/smalidea/testData/fieldRename/fieldRename/before/blah.smali
@@ -0,0 +1,59 @@
+.class public Lblah;
+.super Ljava/lang/Object;
+
+.annotation runtime Lblah;
+    element = Lblah;->blah:Lblah;
+    element2 = .enum Lblah;->blah:Lblah;
+.end annotation
+
+.field public blah:Lblah;
+
+.method public blah(Lblah;)Lblah;
+    .registers 2
+
+    iget v0, v0, Lblah;->blah:Lblah;
+    iget-object v0, v0, Lblah;->blah:Lblah;
+    iget-byte v0, v0, Lblah;->blah:Lblah;
+    iget-char v0, v0, Lblah;->blah:Lblah;
+    iget-object v0, v0, Lblah;->blah:Lblah;
+    iget-object-volatile v0, v0, Lblah;->blah:Lblah;
+    iget-short v0, v0, Lblah;->blah:Lblah;
+    iget-volatile v0, v0, Lblah;->blah:Lblah;
+    iget-wide v0, v0, Lblah;->blah:Lblah;
+    iget-wide-volatile v0, v0, Lblah;->blah:Lblah;
+    sget v0, Lblah;->blah:Lblah;
+    sget-boolean v0, Lblah;->blah:Lblah;
+    sget-byte v0, Lblah;->blah:Lblah;
+    sget-char v0, Lblah;->blah:Lblah;
+    sget-object v0, Lblah;->blah:Lblah;
+    sget-object-volatile v0, Lblah;->blah:Lblah;
+    sget-short v0, Lblah;->blah:Lblah;
+    sget-volatile v0, Lblah;->blah:Lblah;
+    sget-wide v0, Lblah;->blah:Lblah;
+    sget-wide-volatile v0, Lblah;->blah:Lblah;
+
+    iput v0, v0, Lblah;->blah:Lblah;
+    iput-object v0, v0, Lblah;->blah:Lblah;
+    iput-byte v0, v0, Lblah;->blah:Lblah;
+    iput-char v0, v0, Lblah;->blah:Lblah;
+    iput-object v0, v0, Lblah;->blah:Lblah;
+    iput-object-volatile v0, v0, Lblah;->blah:Lblah;
+    iput-short v0, v0, Lblah;->blah:Lblah;
+    iput-volatile v0, v0, Lblah;->blah:Lblah;
+    iput-wide v0, v0, Lblah;->blah:Lblah;
+    iput-wide-volatile v0, v0, Lblah;->blah:Lblah;
+    sput v0, Lblah;->blah:Lblah;
+    sput-boolean v0, Lblah;->blah:Lblah;
+    sput-byte v0, Lblah;->blah:Lblah;
+    sput-char v0, Lblah;->blah:Lblah;
+    sput-object v0, Lblah;->blah:Lblah;
+    sput-object-volatile v0, Lblah;->blah:Lblah;
+    sput-short v0, Lblah;->blah:Lblah;
+    sput-volatile v0, Lblah;->blah:Lblah;
+    sput-wide v0, Lblah;->blah:Lblah;
+    sput-wide-volatile v0, Lblah;->blah:Lblah;
+
+    throw-verification-error generic-error, Lblah;->blah:Lblah;
+
+    return-void
+.end method
diff --git a/smalidea/testData/methodRename/methodRename/after/blah.smali b/smalidea/testData/methodRename/methodRename/after/blah.smali
new file mode 100644
index 0000000..2343699
--- /dev/null
+++ b/smalidea/testData/methodRename/methodRename/after/blah.smali
@@ -0,0 +1,27 @@
+.class public Lblah;
+.super Ljava/lang/Object;
+
+.annotation runtime Lblah;
+    element = Lblah;->blort()V;
+.end annotation
+
+.method public blort()V
+    .registers 2
+
+    invoke-direct {v0}, Lblah;->blort()V
+    invoke-direct/empty {v0}, Lblah;->blort()V
+    invoke-direct/range {v0}, Lblah;->blort()V
+    invoke-interface {v0}, Lblah;->blort()V
+    invoke-interface/range {v0}, Lblah;->blort()V
+    invoke-object-init/range {v0}, Lblah;->blort()V
+    invoke-static {v0}, Lblah;->blort()V
+    invoke-static/range {v0}, Lblah;->blort()V
+    invoke-super {v0}, Lblah;->blort()V
+    invoke-super/range {v0}, Lblah;->blort()V
+    invoke-virtual {v0}, Lblah;->blort()V
+    invoke-virtual/range {v0}, Lblah;->blort()V
+
+    throw-verification-error generic-error, Lblah;->blort()V
+
+    return-void
+.end method
diff --git a/smalidea/testData/methodRename/methodRename/before/blah.smali b/smalidea/testData/methodRename/methodRename/before/blah.smali
new file mode 100644
index 0000000..9a800dc
--- /dev/null
+++ b/smalidea/testData/methodRename/methodRename/before/blah.smali
@@ -0,0 +1,27 @@
+.class public Lblah;
+.super Ljava/lang/Object;
+
+.annotation runtime Lblah;
+    element = Lblah;->blah()V;
+.end annotation
+
+.method public blah()V
+    .registers 2
+
+    invoke-direct {v0}, Lblah;->blah()V
+    invoke-direct/empty {v0}, Lblah;->blah()V
+    invoke-direct/range {v0}, Lblah;->blah()V
+    invoke-interface {v0}, Lblah;->blah()V
+    invoke-interface/range {v0}, Lblah;->blah()V
+    invoke-object-init/range {v0}, Lblah;->blah()V
+    invoke-static {v0}, Lblah;->blah()V
+    invoke-static/range {v0}, Lblah;->blah()V
+    invoke-super {v0}, Lblah;->blah()V
+    invoke-super/range {v0}, Lblah;->blah()V
+    invoke-virtual {v0}, Lblah;->blah()V
+    invoke-virtual/range {v0}, Lblah;->blah()V
+
+    throw-verification-error generic-error, Lblah;->blah()V
+
+    return-void
+.end method
diff --git a/util/src/main/java/org/jf/util/BlankReader.java b/util/src/main/java/org/jf/util/BlankReader.java
new file mode 100644
index 0000000..ca55dd0
--- /dev/null
+++ b/util/src/main/java/org/jf/util/BlankReader.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright 2014, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ *     * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *     * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ *     * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.util;
+
+import javax.annotation.Nonnull;
+import java.io.IOException;
+import java.io.Reader;
+
+public class BlankReader extends Reader {
+    public static final BlankReader INSTANCE = new BlankReader();
+
+    @Override public int read(@Nonnull char[] chars, int i, int i2) throws IOException {
+        return -1;
+    }
+
+    @Override
+    public void close() throws IOException {
+    }
+}