aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java3
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java2
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java6
-rw-r--r--baksmali/src/main/java/org/jf/baksmali/baksmali.java2
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/DexTest.java78
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java30
-rw-r--r--baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java69
-rw-r--r--baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dexbin0 -> 828 bytes
-rw-r--r--build.gradle2
-rw-r--r--dexlib2/OatVersions.txt11
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/Opcode.java104
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/AnalyzedInstruction.java138
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java53
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java48
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java113
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java60
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/builder/MutableMethodImplementation.java43
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java17
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/DexBackedDexFile.java6
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java156
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/iface/DexFile.java9
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/immutable/ImmutableDexFile.java19
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/rewriter/DexRewriter.java5
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java79
-rw-r--r--dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorResolver.java8
-rw-r--r--dexlib2/src/main/ragel/SyntheticAccessorFSM.rl10
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/AccessorTest.java2
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/analysis/CommonSuperclassTest.java68
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/analysis/CustomMethodInlineTableTest.java7
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/analysis/util/SuperclassChainTest.java5
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/builder/MutableMethodImplementationTest.java118
-rw-r--r--dexlib2/src/test/java/org/jf/dexlib2/writer/DexWriterTest.java4
-rw-r--r--smali/src/main/java/org/jf/smali/main.java2
33 files changed, 947 insertions, 330 deletions
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
index b3f9ae17..fc43d6f1 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/Format/InstructionMethodItem.java
@@ -76,8 +76,7 @@ public class InstructionMethodItem<T extends Instruction> extends MethodItem {
return false;
}
- return opcode.isOdexedInstanceVolatile() || opcode.isOdexedStaticVolatile() ||
- opcode == Opcode.THROW_VERIFICATION_ERROR;
+ return opcode.isVolatileFieldAccessor() || opcode == Opcode.THROW_VERIFICATION_ERROR;
}
@Override
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
index 6e009fb7..ef2110a8 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/MethodDefinition.java
@@ -320,7 +320,7 @@ public class MethodDefinition {
String parameterType = parameter.getType();
String parameterName = parameter.getName();
Collection<? extends Annotation> annotations = parameter.getAnnotations();
- if (parameterName != null || annotations.size() != 0) {
+ if ((options.outputDebugInfo && parameterName != null) || annotations.size() != 0) {
writer.write(".param p");
writer.printSignedIntAsDec(registerNumber);
diff --git a/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java b/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java
index fa54f091..f5329388 100644
--- a/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java
+++ b/baksmali/src/main/java/org/jf/baksmali/Adaptors/PreInstructionRegisterInfoMethodItem.java
@@ -156,7 +156,8 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
RegisterType mergedRegisterType = analyzedInstruction.getPreInstructionRegisterType(registerNum);
for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
- RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
+ RegisterType predecessorRegisterType = analyzedInstruction.getPredecessorRegisterType(
+ predecessor, registerNum);
if (predecessorRegisterType.category != RegisterType.UNKNOWN &&
!predecessorRegisterType.equals(mergedRegisterType)) {
registers.set(registerNum);
@@ -179,7 +180,8 @@ public class PreInstructionRegisterInfoMethodItem extends MethodItem {
boolean first = true;
for (AnalyzedInstruction predecessor: analyzedInstruction.getPredecessors()) {
- RegisterType predecessorRegisterType = predecessor.getPostInstructionRegisterType(registerNum);
+ RegisterType predecessorRegisterType = analyzedInstruction.getPredecessorRegisterType(
+ predecessor, registerNum);
if (!first) {
writer.write(',');
diff --git a/baksmali/src/main/java/org/jf/baksmali/baksmali.java b/baksmali/src/main/java/org/jf/baksmali/baksmali.java
index 1e297029..50607340 100644
--- a/baksmali/src/main/java/org/jf/baksmali/baksmali.java
+++ b/baksmali/src/main/java/org/jf/baksmali/baksmali.java
@@ -135,7 +135,7 @@ public class baksmali {
List<? extends ClassDef> classDefs = Ordering.natural().sortedCopy(dexFile.getClasses());
if (!options.noAccessorComments) {
- options.syntheticAccessorResolver = new SyntheticAccessorResolver(classDefs);
+ options.syntheticAccessorResolver = new SyntheticAccessorResolver(dexFile.getOpcodes(), classDefs);
}
final ClassFileNameHandler fileNameHandler = new ClassFileNameHandler(outputDirectoryFile, ".smali");
diff --git a/baksmali/src/test/java/org/jf/baksmali/DexTest.java b/baksmali/src/test/java/org/jf/baksmali/DexTest.java
new file mode 100644
index 00000000..5a4db658
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/DexTest.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import org.jf.dexlib2.Opcodes;
+import org.jf.dexlib2.dexbacked.DexBackedDexFile;
+import org.junit.Assert;
+
+import javax.annotation.Nonnull;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * A base test class for performing a test using a dex file as input
+ */
+/**
+ * A base test class for performing a disassembly on a dex file and verifying the results
+ *
+ * The test accepts a single-class dex file as input. By default, the input dex file should be a resource at
+ * [testDir]/[testName]Input.dex
+ */
+public abstract class DexTest {
+ protected final String testDir;
+
+ protected DexTest(@Nonnull String testDir) {
+ this.testDir = testDir;
+ }
+
+ protected DexTest() {
+ this.testDir = this.getClass().getSimpleName();
+ }
+
+ @Nonnull
+ protected String getInputFilename(@Nonnull String testName) {
+ return String.format("%s%s%sInput.dex", testDir, File.separatorChar, testName);
+ }
+
+ @Nonnull
+ protected DexBackedDexFile getInputDexFile(@Nonnull String testName, @Nonnull baksmaliOptions options) {
+ try {
+ // Load file from resources as a stream
+ byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
+ return new DexBackedDexFile(Opcodes.forApi(options.apiLevel), inputBytes);
+ } catch (IOException ex) {
+ Assert.fail();
+ }
+ return null;
+ }
+}
diff --git a/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
index 1fbafeab..1a34e8c3 100644
--- a/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
+++ b/baksmali/src/test/java/org/jf/baksmali/DisassemblyTest.java
@@ -32,7 +32,6 @@
package org.jf.baksmali;
import com.google.common.collect.Iterables;
-import org.jf.dexlib2.Opcodes;
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
import org.jf.dexlib2.iface.ClassDef;
import org.junit.Assert;
@@ -50,21 +49,7 @@ import java.io.IOException;
* By default, the input and output files should be resources at [testDir]/[testName]Input.dex
* and [testDir]/[testName]Output.smali respectively
*/
-public class DisassemblyTest {
- protected final String testDir;
-
- protected DisassemblyTest(@Nonnull String testDir) {
- this.testDir = testDir;
- }
-
- protected DisassemblyTest() {
- this.testDir = this.getClass().getSimpleName();
- }
-
- @Nonnull
- protected String getInputFilename(@Nonnull String testName) {
- return String.format("%s%s%sInput.dex", testDir, File.separatorChar, testName);
- }
+public class DisassemblyTest extends DexTest {
@Nonnull
protected String getOutputFilename(@Nonnull String testName) {
@@ -77,22 +62,13 @@ public class DisassemblyTest {
protected void runTest(@Nonnull String testName, @Nonnull baksmaliOptions options) {
try {
- // Load file from resources as a stream
- String inputFilename = getInputFilename(testName);
- byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
-
- DexBackedDexFile inputDex = new DexBackedDexFile(Opcodes.forApi(options.apiLevel), inputBytes);
+ DexBackedDexFile inputDex = getInputDexFile(testName, options);
Assert.assertEquals(1, inputDex.getClassCount());
ClassDef inputClass = Iterables.getFirst(inputDex.getClasses(), null);
Assert.assertNotNull(inputClass);
String input = BaksmaliTestUtils.getNormalizedSmali(inputClass, options, true);
- String output;
- if (getOutputFilename(testName).equals(inputFilename)) {
- output = input;
- } else {
- output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName));
- }
+ String output = BaksmaliTestUtils.readResourceFully(getOutputFilename(testName));
output = BaksmaliTestUtils.normalizeSmali(output, true);
// Run smali, baksmali, and then compare strings are equal (minus comments/whitespace)
diff --git a/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java b/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java
new file mode 100644
index 00000000..06841310
--- /dev/null
+++ b/baksmali/src/test/java/org/jf/baksmali/FieldGapOrderTest.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright 2015, Google Inc.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ *
+ * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following disclaimer
+ * in the documentation and/or other materials provided with the
+ * distribution.
+ * Neither the name of Google Inc. nor the names of its
+ * contributors may be used to endorse or promote products derived from
+ * this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.jf.baksmali;
+
+import com.google.common.collect.Lists;
+import org.jf.dexlib2.analysis.ClassPath;
+import org.jf.dexlib2.analysis.ClassProto;
+import org.jf.dexlib2.iface.DexFile;
+import org.junit.Assert;
+import org.junit.Test;
+
+public class FieldGapOrderTest extends DexTest {
+ @Test
+ public void testOldOrder() {
+ DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions());
+ Assert.assertEquals(3, dexFile.getClasses().size());
+
+ ClassPath classPath = new ClassPath(Lists.newArrayList(dexFile), false, 66);
+ ClassProto classProto = (ClassProto)classPath.getClass("LGapOrder;");
+ Assert.assertEquals("r1", classProto.getFieldByOffset(12).getName());
+ Assert.assertEquals("r2", classProto.getFieldByOffset(16).getName());
+ Assert.assertEquals("d", classProto.getFieldByOffset(24).getName());
+ Assert.assertEquals("s", classProto.getFieldByOffset(36).getName());
+ Assert.assertEquals("i", classProto.getFieldByOffset(32).getName());
+ }
+
+ @Test
+ public void testNewOrder() {
+ DexFile dexFile = getInputDexFile("FieldGapOrder", new baksmaliOptions());
+ Assert.assertEquals(3, dexFile.getClasses().size());
+
+ ClassPath classPath = new ClassPath(Lists.newArrayList(dexFile), false, 67);
+ ClassProto classProto = (ClassProto)classPath.getClass("LGapOrder;");
+ Assert.assertEquals("s", classProto.getFieldByOffset(10).getName());
+ Assert.assertEquals("r1", classProto.getFieldByOffset(12).getName());
+ Assert.assertEquals("r2", classProto.getFieldByOffset(16).getName());
+ Assert.assertEquals("i", classProto.getFieldByOffset(20).getName());
+ Assert.assertEquals("d", classProto.getFieldByOffset(24).getName());
+ }
+}
diff --git a/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex b/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex
new file mode 100644
index 00000000..4e593516
--- /dev/null
+++ b/baksmali/src/test/resources/FieldGapOrderTest/FieldGapOrderInput.dex
Binary files differ
diff --git a/build.gradle b/build.gradle
index 5cc2aaa5..88101e6b 100644
--- a/build.gradle
+++ b/build.gradle
@@ -31,7 +31,7 @@
apply plugin: 'idea'
-version = '2.1.0'
+version = '2.1.1'
def jarVersion = version
diff --git a/dexlib2/OatVersions.txt b/dexlib2/OatVersions.txt
index e64eccf6..8aa9ea96 100644
--- a/dexlib2/OatVersions.txt
+++ b/dexlib2/OatVersions.txt
@@ -12,4 +12,13 @@ d7cbf8a6629942e7bd315ffae7e1c77b082f3e11 - 60
07785bb98dc8bbe192970e0f4c2cafd338a8dc68 - 64
fa2c054b28d4b540c1b3651401a7a091282a015f - 65
7070ccd8b6439477eafeea7ed3736645d78e003f - 64 (revert of fa2c054b)
-7bf2b4f1d08050f80782217febac55c8cfc5e4ef - 65 (re-commit of fa2c054b) \ No newline at end of file
+7bf2b4f1d08050f80782217febac55c8cfc5e4ef - 65 (re-commit of fa2c054b)
+0b71357fb52be9bb06d35396a3042b4381b01041 - 66
+fab6788358dfb64e5c370611ddbbbffab0ed0553 - 67
+- Change in FieldGap priority queue ordering
+1aee900d5a0b3a8d78725a7551356bda0d8554e1 - 68
+54b62480636ae846d705fc180c7bd6cd08ec1e42 - 69
+6e2d5747d00697a25251d25dd33b953e54709507 - 68 (revert of 54b62480)
+0747466fca310eedea5fc49e37d54f240a0b3c0f - 69 (re-commit of 54b62480)
+501fd635a557645ab05f893c56e1f358e21bab82 - 70
+99170c636dfae4908b102347cfe9f92bad1881cc - 71 \ No newline at end of file
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java
index 5fb2a8c0..3a642358 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/Opcode.java
@@ -131,20 +131,20 @@ public enum Opcode
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),
- SGET_WIDE(0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
- SGET_OBJECT(0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- SGET_BOOLEAN(0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- SGET_BYTE(0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- SGET_CHAR(0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- SGET_SHORT(0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- SPUT(0x67, "sput", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SPUT_WIDE(0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SPUT_OBJECT(0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SPUT_BOOLEAN(0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SPUT_BYTE(0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SPUT_CHAR(0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SPUT_SHORT(0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, 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),
@@ -260,15 +260,15 @@ public enum Opcode
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.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- IPUT_VOLATILE(firstApi(0xe4, 9), "iput-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SGET_VOLATILE(firstApi(0xe5, 9), "sget-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- SPUT_VOLATILE(firstApi(0xe6, 9), "sput-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- IGET_OBJECT_VOLATILE(firstApi(0xe7, 9), "iget-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | 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.ODEXED_INSTANCE_VOLATILE | 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.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SGET_WIDE_VOLATILE(firstApi(0xea, 9), "sget-wide-volatile", 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(firstApi(0xeb, 9), "sput-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ IGET_VOLATILE(firstApi(0xe3, 9), "iget-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ IPUT_VOLATILE(firstApi(0xe4, 9), "iput-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ SGET_VOLATILE(firstApi(0xe5, 9), "sget-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+ SPUT_VOLATILE(firstApi(0xe6, 9), "sput-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
+ IGET_OBJECT_VOLATILE(firstApi(0xe7, 9), "iget-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ IGET_WIDE_VOLATILE(firstApi(0xe8, 9), "iget-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
+ IPUT_WIDE_VOLATILE(firstApi(0xe9, 9), "iput-wide-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ SGET_WIDE_VOLATILE(firstApi(0xea, 9), "sget-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+ SPUT_WIDE_VOLATILE(firstApi(0xeb, 9), "sput-wide-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
THROW_VERIFICATION_ERROR(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),
@@ -277,29 +277,29 @@ public enum Opcode
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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK),
- IPUT_BYTE_QUICK(allArtVersions(0xec), "iput-byte-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.ODEXED_INSTANCE_QUICK),
- IPUT_CHAR_QUICK(allArtVersions(0xed), "iput-char-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.ODEXED_INSTANCE_QUICK),
- IPUT_SHORT_QUICK(allArtVersions(0xee), "iput-short-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.ODEXED_INSTANCE_QUICK),
- IGET_BOOLEAN_QUICK(allArtVersions(0xef), "iget-boolean-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | 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.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
+ 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.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
- SGET_OBJECT_VOLATILE(firstApi(0xfd, 9), "sget-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
- SPUT_OBJECT_VOLATILE(firstApi(0xfe, 9), "sput-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ IPUT_OBJECT_VOLATILE(firstApi(0xfc, 9), "iput-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
+ SGET_OBJECT_VOLATILE(firstApi(0xfd, 9), "sget-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.STATIC_FIELD_ACCESSOR),
+ SPUT_OBJECT_VOLATILE(firstApi(0xfe, 9), "sput-object-volatile", ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.VOLATILE_FIELD_ACCESSOR | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.STATIC_FIELD_ACCESSOR),
PACKED_SWITCH_PAYLOAD(0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
SPARSE_SWITCH_PAYLOAD(0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
@@ -327,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
@@ -451,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() {
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/ClassPath.java b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
index 4b8920f4..cf75dffe 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassPath.java
@@ -40,6 +40,7 @@ import com.google.common.collect.*;
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;
@@ -60,8 +61,10 @@ import java.util.regex.Pattern;
public class ClassPath {
@Nonnull private final TypeProto unknownClass;
@Nonnull private HashMap<String, ClassDef> availableClasses = Maps.newHashMap();
- private boolean checkPackagePrivateAccess;
- public boolean isArt;
+ private final boolean checkPackagePrivateAccess;
+ public final int oatVersion;
+
+ public static final int NOT_ART = -1;
/**
* Creates a new ClassPath instance that can load classes from the given dex files
@@ -78,7 +81,7 @@ public class ClassPath {
* @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order
* @param api API level
*/
- public ClassPath(@Nonnull Iterable<DexFile> classPath, int api) {
+ public ClassPath(@Nonnull Iterable<? extends DexFile> classPath, int api) {
this(Lists.newArrayList(classPath), api == 17);
}
@@ -89,8 +92,8 @@ public class ClassPath {
* @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by
* default
*/
- public ClassPath(@Nonnull Iterable<DexFile> classPath, boolean checkPackagePrivateAccess) {
- this(classPath, checkPackagePrivateAccess, false);
+ public ClassPath(@Nonnull Iterable<? extends DexFile> classPath, boolean checkPackagePrivateAccess) {
+ this(classPath, checkPackagePrivateAccess, NOT_ART);
}
/**
@@ -99,16 +102,17 @@ public class ClassPath {
* @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 isArt Whether this is ClassPath is for ART
+ * @param oatVersion The applicable oat version, or NOT_ART
*/
- public ClassPath(@Nonnull Iterable < DexFile > classPath, boolean checkPackagePrivateAccess, boolean isArt) {
+ public ClassPath(@Nonnull Iterable<? extends DexFile> classPath, 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.isArt = isArt;
+ this.oatVersion = oatVersion;
loadPrimitiveType("Z");
loadPrimitiveType("B");
@@ -136,13 +140,18 @@ public class ClassPath {
private static DexFile getBasicClasses() {
// fallbacks for some special classes that we assume are present
- return new ImmutableDexFile(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)));
+ return 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)));
+ }
+
+ public boolean isArt() {
+ return oatVersion != NOT_ART;
}
@Nonnull
@@ -191,15 +200,15 @@ public class ClassPath {
int api, boolean checkPackagePrivateAccess, boolean experimental) {
ArrayList<DexFile> dexFiles = Lists.newArrayList();
- boolean isArt = false;
+ int oatVersion = NOT_ART;
for (String classPathEntry: classPath) {
List<? extends DexFile> classPathDexFiles =
loadClassPathEntry(classPathDirs, classPathEntry, api, experimental);
- if (!isArt) {
+ if (oatVersion == NOT_ART) {
for (DexFile classPathDexFile: classPathDexFiles) {
if (classPathDexFile instanceof OatDexFile) {
- isArt = true;
+ oatVersion = ((OatDexFile)classPathDexFile).getOatVersion();
break;
}
}
@@ -207,20 +216,20 @@ public class ClassPath {
dexFiles.addAll(classPathDexFiles);
}
dexFiles.add(dexFile);
- return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
+ return new ClassPath(dexFiles, checkPackagePrivateAccess, oatVersion);
}
@Nonnull
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
int api, boolean checkPackagePrivateAccess, boolean experimental,
- boolean isArt) {
+ int oatVersion) {
ArrayList<DexFile> dexFiles = Lists.newArrayList();
for (String classPathEntry: classPath) {
dexFiles.addAll(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
}
dexFiles.add(dexFile);
- return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
+ return new ClassPath(dexFiles, checkPackagePrivateAccess, oatVersion);
}
private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
@@ -290,7 +299,7 @@ public class ClassPath {
private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
new Supplier<OdexedFieldInstructionMapper>() {
@Override public OdexedFieldInstructionMapper get() {
- return new OdexedFieldInstructionMapper(isArt);
+ return new OdexedFieldInstructionMapper(isArt());
}
});
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 d66e8ebd..57aae115 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/ClassProto.java
@@ -374,7 +374,7 @@ public class ClassProto implements TypeProto {
}
@Nonnull SparseArray<FieldReference> getInstanceFields() {
- if (classPath.isArt) {
+ if (classPath.isArt()) {
return artInstanceFieldsSupplier.get();
} else {
return dalvikInstanceFieldsSupplier.get();
@@ -548,21 +548,37 @@ public class ClassProto implements TypeProto {
}
});
- private static class FieldGap implements Comparable<FieldGap> {
+ private static abstract class FieldGap implements Comparable<FieldGap> {
public final int offset;
public final int size;
- public FieldGap(int offset, int size) {
- this.offset = offset;
- this.size = 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);
+ }
+ };
+ }
}
- @Override public int compareTo(@Nonnull FieldGap o) {
- int result = Ints.compare(o.size, size);
- if (result != 0) {
- return result;
- }
- return Ints.compare(o.offset, offset);
+ private FieldGap(int offset, int size) {
+ this.offset = offset;
+ this.size = size;
}
}
@@ -630,13 +646,13 @@ public class ClassProto implements TypeProto {
int remaining = gapEnd - offset;
if ((remaining >= 4) && (offset % 4 == 0)) {
- gaps.add(new FieldGap(offset, 4));
+ gaps.add(FieldGap.newFieldGap(offset, 4, classPath.oatVersion));
offset += 4;
} else if (remaining >= 2 && (offset % 2 == 0)) {
- gaps.add(new FieldGap(offset, 2));
+ gaps.add(FieldGap.newFieldGap(offset, 2, classPath.oatVersion));
offset += 2;
} else {
- gaps.add(new FieldGap(offset, 1));
+ gaps.add(FieldGap.newFieldGap(offset, 1, classPath.oatVersion));
offset += 1;
}
}
@@ -703,14 +719,14 @@ public class ClassProto implements TypeProto {
private int getNextFieldOffset() {
SparseArray<FieldReference> instanceFields = getInstanceFields();
if (instanceFields.size() == 0) {
- return classPath.isArt ? 0 : 8;
+ return classPath.isArt() ? 0 : 8;
}
int lastItemIndex = instanceFields.size()-1;
int fieldOffset = instanceFields.keyAt(lastItemIndex);
FieldReference lastField = instanceFields.valueAt(lastItemIndex);
- if (classPath.isArt) {
+ if (classPath.isArt()) {
return fieldOffset + getTypeSize(lastField.getType().charAt(0));
} else {
switch (lastField.getType().charAt(0)) {
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 e7ad17ca..eca9391f 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/MethodAnalyzer.java
@@ -337,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);
@@ -376,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);
}
}
@@ -1039,8 +1065,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");
}
@@ -1134,6 +1163,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) {
@@ -1710,7 +1777,7 @@ public class MethodAnalyzer {
}
resolvedMethod = superType.getMethodByVtableIndex(methodIndex);
- } else{
+ } else {
resolvedMethod = objectRegisterTypeProto.getMethodByVtableIndex(methodIndex);
}
@@ -1799,7 +1866,7 @@ public class MethodAnalyzer {
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 6352f08c..0ed1fef1 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/analysis/OdexedFieldInstructionMapper.java
@@ -43,26 +43,40 @@ public class OdexedFieldInstructionMapper {
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;
- @Nonnull public final Opcode quickOpcode;
+ @Nullable public final Opcode quickOpcode;
@Nullable public final Opcode volatileOpcode;
- public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode,
+ 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, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode) {
+ 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;
@@ -91,6 +105,28 @@ public class OdexedFieldInstructionMapper {
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[] {
@@ -117,7 +153,7 @@ public class OdexedFieldInstructionMapper {
new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK)
};
- private final FieldOpcode[][] opcodeMap = new FieldOpcode[2][10];
+ 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) {
@@ -169,6 +205,10 @@ public class OdexedFieldInstructionMapper {
return (opcode.flags & Opcode.SETS_REGISTER) != 0;
}
+ private static boolean isStatic(@Nonnull Opcode opcode) {
+ return (opcode.flags & Opcode.STATIC_FIELD_ACCESSOR) != 0;
+ }
+
public OdexedFieldInstructionMapper(boolean isArt) {
FieldOpcode[] opcodes;
if (isArt) {
@@ -178,9 +218,13 @@ public class OdexedFieldInstructionMapper {
}
for (FieldOpcode fieldOpcode: opcodes) {
- opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT][getTypeIndex(fieldOpcode.type)] = fieldOpcode;
+ opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT]
+ [isStatic(fieldOpcode.normalOpcode)?STATIC:INSTANCE]
+ [getTypeIndex(fieldOpcode.type)] = fieldOpcode;
- opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
+ if (fieldOpcode.quickOpcode != null) {
+ opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
+ }
if (fieldOpcode.volatileOpcode != null) {
opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type));
}
@@ -189,7 +233,9 @@ public class OdexedFieldInstructionMapper {
@Nonnull
public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
- FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT][getTypeIndex(fieldType.charAt(0))];
+ 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,
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 9cf49294..eeb28227 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/BaseDexBuffer.java
@@ -97,6 +97,23 @@ 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;
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 638fd855..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,7 +61,7 @@ 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) {
+ private DexBackedDexFile(@Nonnull Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
super(buf, offset);
this.opcodes = opcodes;
@@ -117,7 +117,7 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
return new DexBackedDexFile(opcodes, buf, 0, false);
}
- public Opcodes getOpcodes() {
+ @Override @Nonnull public Opcodes getOpcodes() {
return opcodes;
}
diff --git a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java
index b5250d0d..dbeb67ce 100644
--- a/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java
+++ b/dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java
@@ -49,29 +49,38 @@ 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 ELF_HEADER_SIZE = 52;
+ 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 = 65;
+ 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 < ELF_HEADER_SIZE) {
+ 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()) {
@@ -192,6 +201,10 @@ public class OatFile extends BaseDexBuffer {
this.filename = filename;
}
+ public int getOatVersion() {
+ return OatFile.this.getOatVersion();
+ }
+
@Override public boolean hasOdexOpcodes() {
return true;
}
@@ -254,9 +267,18 @@ public class OatFile extends BaseDexBuffer {
@Nonnull
private List<SectionHeader> getSections() {
- final int offset = readSmallUint(32);
- final int entrySize = readUshort(46);
- final int entryCount = readUshort(48);
+ 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");
@@ -267,7 +289,11 @@ public class OatFile extends BaseDexBuffer {
if (index < 0 || index >= entryCount) {
throw new IndexOutOfBoundsException();
}
- return new SectionHeader(offset + (index * entrySize));
+ if (is64bit) {
+ return new SectionHeader64Bit(offset + (index * entrySize));
+ } else {
+ return new SectionHeader32Bit(offset + (index * entrySize));
+ }
}
@Override public int size() {
@@ -300,43 +326,35 @@ public class OatFile extends BaseDexBuffer {
}
}
- private class SectionHeader {
- private final int offset;
-
+ 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();
+ }
- public SectionHeader(int offset) {
- this.offset = offset;
- }
-
- @Nonnull
- public String getName() {
- return getSectionNameStringTable().getString(readSmallUint(offset));
- }
-
- public int getType() {
- return readInt(offset + 4);
- }
-
- public long getAddress() {
- return readInt(offset + 12) & 0xFFFFFFFFL;
- }
-
- public int getOffset() {
- return readSmallUint(offset + 16);
- }
-
- public int getSize() {
- return readSmallUint(offset + 20);
- }
-
- public int getLink() {
- return readSmallUint(offset + 24);
- }
+ 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); }
+ }
- 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 {
@@ -367,7 +385,11 @@ public class OatFile extends BaseDexBuffer {
if (index < 0 || index >= entryCount) {
throw new IndexOutOfBoundsException();
}
- return new Symbol(offset + index * entrySize);
+ if (is64bit) {
+ return new Symbol64(offset + index * entrySize);
+ } else {
+ return new Symbol32(offset + index * entrySize);
+ }
}
@Override public int size() {
@@ -376,29 +398,13 @@ public class OatFile extends BaseDexBuffer {
};
}
- public class Symbol {
- private final int offset;
-
- public Symbol(int offset) {
- this.offset = offset;
- }
-
- @Nonnull
- public String getName() {
- return stringTable.getString(readSmallUint(offset));
- }
-
- public int getValue() {
- return readSmallUint(offset + 4);
- }
-
- public int getSize() {
- return readSmallUint(offset + 8);
- }
-
- public int getSectionIndex() {
- return readUshort(offset + 14);
- }
+ 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;
@@ -423,6 +429,26 @@ public class OatFile extends BaseDexBuffer {
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 {
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/SyntheticAccessorFSM.java b/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java
index 1e8b010b..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,11 +33,12 @@
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 org.jf.dexlib2.Opcodes;
+import javax.annotation.Nonnull;
import java.util.List;
public class SyntheticAccessorFSM {
@@ -212,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,15 +238,13 @@ static final int SyntheticAccessorFSM_en_main = 1;
// The return register;
int returnRegister = -1;
- Opcodes opcodes = Opcodes.forApi(20);
-
-// line 238 "/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 243 "/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;
@@ -320,19 +325,19 @@ case 1:
switch ( _SyntheticAccessorFSM_actions[_acts++] )
{
case 0:
-// line 96 "SyntheticAccessorFSM.rl"
+// line 100 "SyntheticAccessorFSM.rl"
{
putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
}
break;
case 1:
-// line 103 "SyntheticAccessorFSM.rl"
+// line 107 "SyntheticAccessorFSM.rl"
{
constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral();
}
break;
case 2:
-// line 107 "SyntheticAccessorFSM.rl"
+// line 111 "SyntheticAccessorFSM.rl"
{
mathType = INT;
mathOp = ADD;
@@ -340,146 +345,146 @@ case 1:
}
break;
case 3:
-// line 113 "SyntheticAccessorFSM.rl"
+// line 117 "SyntheticAccessorFSM.rl"
{ mathType = INT; }
break;
case 4:
-// line 114 "SyntheticAccessorFSM.rl"
+// line 118 "SyntheticAccessorFSM.rl"
{ mathType = LONG; }
break;
case 5:
-// line 115 "SyntheticAccessorFSM.rl"
+// line 119 "SyntheticAccessorFSM.rl"
{ mathType = FLOAT; }
break;
case 6:
-// line 116 "SyntheticAccessorFSM.rl"
+// line 120 "SyntheticAccessorFSM.rl"
{mathType = DOUBLE; }
break;
case 7:
-// line 116 "SyntheticAccessorFSM.rl"
+// line 120 "SyntheticAccessorFSM.rl"
{
mathOp = ADD;
}
break;
case 8:
-// line 119 "SyntheticAccessorFSM.rl"
+// line 123 "SyntheticAccessorFSM.rl"
{ mathType = INT; }
break;
case 9:
-// line 120 "SyntheticAccessorFSM.rl"
+// line 124 "SyntheticAccessorFSM.rl"
{ mathType = LONG; }
break;
case 10:
-// line 121 "SyntheticAccessorFSM.rl"
+// line 125 "SyntheticAccessorFSM.rl"
{ mathType = FLOAT; }
break;
case 11:
-// line 122 "SyntheticAccessorFSM.rl"
+// line 126 "SyntheticAccessorFSM.rl"
{mathType = DOUBLE; }
break;
case 12:
-// line 122 "SyntheticAccessorFSM.rl"
+// line 126 "SyntheticAccessorFSM.rl"
{
mathOp = SUB;
}
break;
case 13:
-// line 126 "SyntheticAccessorFSM.rl"
+// line 130 "SyntheticAccessorFSM.rl"
{
mathOp = MUL;
}
break;
case 14:
-// line 130 "SyntheticAccessorFSM.rl"
+// line 134 "SyntheticAccessorFSM.rl"
{
mathOp = DIV;
}
break;
case 15:
-// line 134 "SyntheticAccessorFSM.rl"
+// line 138 "SyntheticAccessorFSM.rl"
{
mathOp = REM;
}
break;
case 16:
-// line 137 "SyntheticAccessorFSM.rl"
+// line 141 "SyntheticAccessorFSM.rl"
{
mathOp = AND;
}
break;
case 17:
-// line 140 "SyntheticAccessorFSM.rl"
+// line 144 "SyntheticAccessorFSM.rl"
{
mathOp = OR;
}
break;
case 18:
-// line 143 "SyntheticAccessorFSM.rl"
+// line 147 "SyntheticAccessorFSM.rl"
{
mathOp = XOR;
}
break;
case 19:
-// line 146 "SyntheticAccessorFSM.rl"
+// line 150 "SyntheticAccessorFSM.rl"
{
mathOp = SHL;
}
break;
case 20:
-// line 149 "SyntheticAccessorFSM.rl"
+// line 153 "SyntheticAccessorFSM.rl"
{
mathOp = SHR;
}
break;
case 21:
-// line 152 "SyntheticAccessorFSM.rl"
+// line 156 "SyntheticAccessorFSM.rl"
{
mathOp = USHR;
}
break;
case 22:
-// line 158 "SyntheticAccessorFSM.rl"
+// line 162 "SyntheticAccessorFSM.rl"
{
returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
}
break;
case 23:
-// line 164 "SyntheticAccessorFSM.rl"
+// line 168 "SyntheticAccessorFSM.rl"
{
accessorType = SyntheticAccessorResolver.GETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
case 24:
-// line 168 "SyntheticAccessorFSM.rl"
+// line 172 "SyntheticAccessorFSM.rl"
{
accessorType = SyntheticAccessorResolver.SETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
case 25:
-// line 172 "SyntheticAccessorFSM.rl"
+// line 176 "SyntheticAccessorFSM.rl"
{
accessorType = SyntheticAccessorResolver.METHOD; { p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
case 26:
-// line 176 "SyntheticAccessorFSM.rl"
+// line 180 "SyntheticAccessorFSM.rl"
{
accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
}
break;
case 27:
-// line 180 "SyntheticAccessorFSM.rl"
+// line 184 "SyntheticAccessorFSM.rl"
{
accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
}
break;
case 28:
-// line 188 "SyntheticAccessorFSM.rl"
+// line 192 "SyntheticAccessorFSM.rl"
{
accessorType = mathOp; { p += 1; _goto_targ = 5; if (true) continue _goto;}
}
break;
-// line 483 "/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"
}
}
}
@@ -499,7 +504,7 @@ case 5:
break; }
}
-// line 201 "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/ragel/SyntheticAccessorFSM.rl b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
index 60d297bb..96ac5367 100644
--- a/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
+++ b/dexlib2/src/main/ragel/SyntheticAccessorFSM.rl
@@ -64,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();
@@ -83,8 +89,6 @@ public class SyntheticAccessorFSM {
// The return register;
int returnRegister = -1;
- Opcodes opcodes = Opcodes.forApi(20);
-
%%{
import "Opcodes.rl";
alphtype short;
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..521290f0 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,38 +54,41 @@ 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 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;")
)));
}
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 25f7778d..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,7 +67,7 @@ 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);
@@ -93,7 +94,7 @@ 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);
@@ -120,7 +121,7 @@ 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);
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..c9a9af95 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,6 +34,7 @@ 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.TestUtils;
import org.jf.dexlib2.analysis.TypeProto;
@@ -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 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 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 54372cf6..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,7 +72,7 @@ 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);
}
@@ -112,7 +112,7 @@ 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);
}
diff --git a/smali/src/main/java/org/jf/smali/main.java b/smali/src/main/java/org/jf/smali/main.java
index b36b088d..bbf4ff37 100644
--- a/smali/src/main/java/org/jf/smali/main.java
+++ b/smali/src/main/java/org/jf/smali/main.java
@@ -327,7 +327,7 @@ public class main {
LexerErrorInterface lexer;
- FileInputStream fis = new FileInputStream(smaliFile.getAbsolutePath());
+ FileInputStream fis = new FileInputStream(smaliFile);
InputStreamReader reader = new InputStreamReader(fis, "UTF-8");
lexer = new smaliFlexLexer(reader);