aboutsummaryrefslogtreecommitdiff
path: root/dexlib2/src
diff options
context:
space:
mode:
Diffstat (limited to 'dexlib2/src')
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java114
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/Opcode.java662
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/Opcodes.java82
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java50
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java138
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedMethodUtil.java70
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ArrayProto.java7
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java159
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java300
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProvider.java40
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/DexClassProvider.java56
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java266
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java352
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/PrimitiveProto.java7
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/TypeProto.java4
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/UnknownClassProto.java7
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/util/TypeProtoUtils.java12
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java43
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java36
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexReader.java39
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java14
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedOdexFile.java5
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java496
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/HeaderItem.java24
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/raw/RawDexFile.java3
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java9
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java19
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java5
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/MethodUtil.java13
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java90
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java8
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/TypeUtils.java21
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/writer/DexWriter.java16
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/writer/InstructionWriter.java78
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/writer/builder/DexBuilder.java22
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/writer/pool/DexPool.java17
-rw-r--r--dexlib2/src/main/ragel/SyntheticAccessorFSM.rl11
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java2
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java70
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java13
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java7
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java118
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java8
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/writer/JumboStringConversionTest.java8
44 files changed, 2644 insertions, 877 deletions
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java b/dexlib2/src/main/java/org/jf/dexlib2/DexFileFactory.java
index 113b60a3..60488ba2 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 @@ public final class DexFileFactory {
// 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 @@ public final class DexFileFactory {
} 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 3b082ee8..3a642358 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),
-
- 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),
-
- 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),
-
- 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),
-
- 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),
+ 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(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(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(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(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 enum Opcode
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 @@ public enum Opcode
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();
+
+ 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);
+ }
+ }
+
+ this.apiToValueMap = apiToValueBuilder.build();
+ this.artVersionToValueMap = artVersionToValueBuilder.build();
+ this.name = opcodeName;
+ this.referenceType = referenceType;
+ this.format = format;
+ this.flags = flags;
}
- Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format) {
- this(opcodeValue, opcodeName, apiConstraints, referenceType, format, 0, (short)-1);
+ private static List<VersionConstraint> firstApi(int opcodeValue, int api) {
+ return Lists.newArrayList(new VersionConstraint(Range.atLeast(api), Range.openClosed(0, 0), opcodeValue));
}
- Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags) {
- this(opcodeValue, opcodeName, apiConstraints, referenceType, format, flags, (short)-1);
+ private static List<VersionConstraint> lastApi(int opcodeValue, int api) {
+ Range range;
+ return Lists.newArrayList(new VersionConstraint(Range.atMost(api), Range.openClosed(0, 0), opcodeValue));
}
- Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags,
- short jumboOpcodeValue) {
- this.value = opcodeValue;
- 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;
+ 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));
}
- /**
- * @return the minimum api level that can use this opcode (inclusive)
- */
- public int getMinApi() {
- return apiConstraints & 0xFFFF;
+ private static List<VersionConstraint> allApis(int opcodeValue) {
+ return Lists.newArrayList(new VersionConstraint(Range.<Integer>all(), 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> 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 @@ public enum Opcode
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 enum Opcode
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 e718e275..17f80132 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 @@ public class Opcodes {
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 00000000..42802bc0
--- /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 30cc9067..f6fc95a1 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 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
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 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
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 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
* 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 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
/**
* 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;
+ }
+
+ 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);
- RegisterType oldRegisterType = postRegisterMap[registerNumber];
- if (oldRegisterType.equals(registerType)) {
- return false;
- }
+ if (preRegisterMap[registerNumber].equals(mergedType)) {
+ return false;
+ }
+
+ preRegisterMap[registerNumber] = mergedType;
+ verifiedInstructions.clear(instructionIndex);
- postRegisterMap[registerNumber] = registerType;
- return true;
- }
+ 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 @@ public class AnalyzedInstruction implements Comparable<AnalyzedInstruction> {
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 00000000..775a819d
--- /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 8fcfc8c5..4aa9a5e4 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 @@ public class ArrayProto implements TypeProto {
@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 bd9cfb1b..9f9e396b 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 javax.annotation.Nonnull;
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;
- /**
- * Creates a new ClassPath instance that can load classes from the given dex files
- *
- * @param classPath An array of DexFile objects. When loading a class, these dex files will be searched in order
- */
- public ClassPath(DexFile... classPath) throws IOException {
- this(Lists.newArrayList(classPath), 15);
- }
+ 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 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
*/
- public ClassPath(@Nonnull Iterable<DexFile> classPath, int api) {
- this(Lists.newArrayList(classPath), api == 17);
+ 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 checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by default
+ * @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, 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 @@ public class ClassPath {
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 @@ public class ClassPath {
@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 @@ public class ClassPath {
@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 @@ public class ClassPath {
}
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 @@ public class ClassPath {
"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 @@ public class ClassPath {
}
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 f4f1dc7b..e407de7e 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.base.Suppliers;
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.Method;
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 @@ public class ClassProto implements TypeProto {
@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 @@ public class ClassProto implements TypeProto {
return vtable.get(vtableIndex);
}
+ 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 SparseArray<FieldReference> getInstanceFields() {
- return instanceFieldsSupplier.get();
+ if (classPath.isArt()) {
+ return artInstanceFieldsSupplier.get();
+ } else {
+ return dalvikInstanceFieldsSupplier.get();
+ }
}
- @Nonnull private final Supplier<SparseArray<FieldReference>> instanceFieldsSupplier =
+ @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,46 +537,225 @@ public class ClassProto implements TypeProto {
return fields;
}
- private byte getFieldType(@Nonnull FieldReference field) {
+ private void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) {
+ byte tempType = fieldTypes[position1];
+ fieldTypes[position1] = fieldTypes[position2];
+ fieldTypes[position2] = tempType;
+
+ Field tempField = fields.set(position1, fields.get(position2));
+ fields.set(position2, tempField);
+ }
+ });
+
+ 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; //REFERENCE
+ return 0;
+ /* 64 bit */
case 'J':
+ return 1;
case 'D':
- return 1; //WIDE
- default:
- return 2; //OTHER
+ 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 void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) {
- byte tempType = fieldTypes[position1];
- fieldTypes[position1] = fieldTypes[position2];
- fieldTypes[position2] = tempType;
-
- Field tempField = fields.set(position1, fields.get(position2));
- fields.set(position2, tempField);
+ 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 @@ public class ClassProto implements TypeProto {
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 @@ public class ClassProto implements TypeProto {
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 00000000..7c823ff0
--- /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 00000000..c460cc3f
--- /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 4402c624..f874f1b8 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.immutable.reference.ImmutableMethodReference;
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 @@ public class MethodAnalyzer {
@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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
int objectRegisterNumber;
switch (instruction.getOpcode().format) {
case Format10x:
- analyzeReturnVoidBarrier(analyzedInstruction, false);
+ analyzeOdexReturnVoid(analyzedInstruction, false);
continue;
case Format21c:
case Format22c:
@@ -333,34 +337,60 @@ public class MethodAnalyzer {
registerType);
}
- private void setPostRegisterTypeAndPropagateChanges(@Nonnull AnalyzedInstruction analyzedInstruction,
- int registerNumber, @Nonnull RegisterType registerType) {
-
- BitSet changedInstructions = new BitSet(analyzedInstructions.size());
-
- if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
- return;
- }
-
- propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions);
-
+ 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,
- //which requires the user to specify a larger stack size. This isn't really a problem, but it is slightly
- //annoying.
+ //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)) {
+ instructionIndex>=0;
+ instructionIndex=changedInstructions.nextSetBit(instructionIndex+1)) {
changedInstructions.clear(instructionIndex);
propagateRegisterToSuccessors(analyzedInstructions.valueAt(instructionIndex), registerNumber,
- changedInstructions);
+ 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) {
+
+ BitSet changedInstructions = new BitSet(analyzedInstructions.size());
+
+ if (!analyzedInstruction.setPostRegisterType(registerNumber, registerType)) {
+ return;
+ }
+
+ propagateRegisterToSuccessors(analyzedInstruction, registerNumber, changedInstructions, false);
+
+ propagateChanges(changedInstructions, registerNumber, false);
if (registerType.category == RegisterType.LONG_LO) {
checkWidePair(registerNumber, analyzedInstruction);
@@ -372,10 +402,10 @@ public class MethodAnalyzer {
}
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 @@ public class MethodAnalyzer {
//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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
}
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
}
resolvedMethod = superType.getMethodByVtableIndex(methodIndex);
- } else{
+ } else {
resolvedMethod = objectRegisterTypeProto.getMethodByVtableIndex(methodIndex);
}
@@ -1628,12 +1797,13 @@ public class MethodAnalyzer {
// 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 @@ public class MethodAnalyzer {
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 @@ public class MethodAnalyzer {
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 49136c35..0ed1fef1 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 @@ package org.jf.dexlib2.analysis;
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 @@ public class OdexedFieldInstructionMapper {
case 'C':
return 3;
case 'I':
- case 'F':
return 4;
+ case 'F':
+ return 5;
case 'J':
+ return 6;
case 'D':
- return 5;
+ return 7;
case 'L':
+ return 8;
case '[':
- return 6;
- default:
+ 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;
- }
- throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name));
+ private static boolean isGet(@Nonnull Opcode opcode) {
+ return (opcode.flags & Opcode.SETS_REGISTER) != 0;
}
- @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));
+ private static boolean isStatic(@Nonnull Opcode opcode) {
+ return (opcode.flags & Opcode.STATIC_FIELD_ACCESSOR) != 0;
+ }
- Opcode correctOdexedOpcode, deodexedOpcode;
+ public OdexedFieldInstructionMapper(boolean isArt) {
+ FieldOpcode[] opcodes;
+ if (isArt) {
+ opcodes = artFieldOpcodes;
+ } else {
+ opcodes = dalvikFieldOpcodes;
+ }
- correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
- deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
+ for (FieldOpcode fieldOpcode: opcodes) {
+ opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT]
+ [isStatic(fieldOpcode.normalOpcode)?STATIC:INSTANCE]
+ [getTypeIndex(fieldOpcode.type)] = fieldOpcode;
- if (correctOdexedOpcode != odexedOpcode) {
+ if (fieldOpcode.quickOpcode != null) {
+ opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
+ }
+ if (fieldOpcode.volatileOpcode != null) {
+ opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type));
+ }
+ }
+ }
+
+ @Nonnull
+ public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
+ FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT]
+ [isStatic(odexedOpcode)?STATIC:INSTANCE]
+ [getTypeIndex(fieldType.charAt(0))];
+
+ 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 06ab8e17..2c283932 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 @@ public class PrimitiveProto implements TypeProto {
@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 f6db2399..776363b8 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 @@ public interface TypeProto {
@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 38256fbe..32873456 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 @@ public class UnknownClassProto implements TypeProto {
@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 0313c7c3..e2adf1c4 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 @@ public class TypeProtoUtils {
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 84981221..b1e5dbbf 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 @@ public class MutableMethodImplementation implements MethodImplementation {
} 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 7d06bff7..eeb28227 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 @@ import javax.annotation.Nonnull;
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 class BaseDexBuffer {
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 class BaseDexBuffer {
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 @@ public class BaseDexBuffer {
(((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 class BaseDexBuffer {
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 @@ public class BaseDexBuffer {
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 96645b8c..13c0a7b0 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 class BaseDexReader<T extends BaseDexBuffer> {
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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
}
}
- offset = end;
+ offset = end - dexBuf.baseOffset;
return result;
}
@@ -93,7 +93,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
}
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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
}
}
- offset = end;
+ offset = end - dexBuf.baseOffset;
return result;
}
@@ -150,7 +150,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
* @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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
}
}
- 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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
}
}
- offset = end;
+ offset = end - dexBuf.baseOffset;
}
public int readSmallUint() {
@@ -285,7 +285,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
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 @@ public class BaseDexReader<T extends BaseDexBuffer> {
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 1774096e..32505eec 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.io.InputStream;
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 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
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 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
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 1aa9c1eb..12f19db0 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 @@ package org.jf.dexlib2.dexbacked;
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 @@ public class DexBackedOdexFile extends DexBackedDexFile {
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 00000000..dbeb67ce
--- /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 3499eadc..531ff461 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 @@ import javax.annotation.Nullable;
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 @@ public class HeaderItem {
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 @@ public class HeaderItem {
}
}
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 1fac80bb..204a29d8 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 @@ public class RawDexFile extends DexBackedDexFile {
@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 class RawDexFile extends DexBackedDexFile {
}
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 45707f03..474bfb13 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 @@ public interface DexFile {
* @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 e911eb38..2112bd07 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 @@ import java.util.Collection;
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 e482cd04..839ac096 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 @@ public class DexRewriter implements Rewriters {
@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 631b8928..dc978daf 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 com.google.common.base.Predicate;
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 @@ public final class MethodUtil {
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 @@ public final class MethodUtil {
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 aa428b05..674d5180 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_error = 0;
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 @@ static final int SyntheticAccessorFSM_en_main = 1;
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 @@ static final int SyntheticAccessorFSM_en_main = 1;
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 @@ case 1:
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 @@ case 1:
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 @@ case 1:
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 @@ case 1:
}
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 @@ case 5:
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 a46a18f0..7808f84d 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.ImmutableList;
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 class SyntheticAccessorResolver {
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 @@ public class SyntheticAccessorResolver {
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 02890b87..6be21af0 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 @@ public final class TypeUtils {
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 be23978e..4e81f7fa 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.Maps;
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 abstract class DexWriter<
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 @@ public abstract class DexWriter<
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 @@ public abstract class DexWriter<
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 @@ public abstract class DexWriter<
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 @@ public abstract class DexWriter<
}
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 @@ public abstract class DexWriter<
// 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 c9aa73a1..f16256c5 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 @@ package org.jf.dexlib2.writer;
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 @@ import java.util.List;
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 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
@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 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
}
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 class InstructionWriter<StringRef extends StringReference, TypeRef extend
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 9a727b27..d1190249 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 @@ package org.jf.dexlib2.writer.builder;
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 org.jf.util.ExceptionWithContext;
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 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
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 d7f63922..27d8044e 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 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
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 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
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 c46f9bac..96ac5367 100644
--- a/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
+++ b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
@@ -34,6 +34,7 @@ package org.jf.dexlib2.util;
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 class SyntheticAccessorFSM {
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 @@ public class SyntheticAccessorFSM {
%%{
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 924d3fd3..4c8f85bf 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 @@ public class AccessorTest {
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 f3b1094e..3f1ee56d 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 @@ package org.jf.dexlib2.analysis;
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 @@ public class CommonSuperclassTest {
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;"),
-
- // 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;")
- )));
+ 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;"),
+
+ // 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 65a82f05..90a63590 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.ImmutableList;
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 @@ public class CustomMethodInlineTableTest {
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 @@ public class CustomMethodInlineTableTest {
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 @@ public class CustomMethodInlineTableTest {
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 59b0d276..84cd284b 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 @@ package org.jf.dexlib2.analysis.util;
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 @@ import java.io.IOException;
public class SuperclassChainTest {
-
@Test
public void testGetSuperclassChain() throws IOException {
ClassDef objectClassDef = TestUtils.makeClassDef("Ljava/lang/Object;", null);
@@ -56,7 +57,7 @@ public class SuperclassChainTest {
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 @@ public class SuperclassChainTest {
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 00000000..0df5ab3d
--- /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 8ba975a1..1a0a2892 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 @@ public class DexWriterTest {
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 @@ public class DexWriterTest {
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 7e504a17..c246e0ec 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 @@ import java.util.List;
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 @@ public class JumboStringConversionTest {
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 @@ public class JumboStringConversionTest {
@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 @@ public class JumboStringConversionTest {
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);