diff options
Diffstat (limited to 'dexlib2/src')
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); |