mirror of
https://github.com/revanced/smali.git
synced 2025-05-11 11:54:29 +02:00
Merge branch 'master' into smalidea
This commit is contained in:
commit
ea3fdd7e86
1
.gitignore
vendored
1
.gitignore
vendored
@ -8,3 +8,4 @@
|
||||
*.iml
|
||||
*.ipr
|
||||
*.iws
|
||||
.idea
|
||||
|
16
README.md
16
README.md
@ -2,16 +2,18 @@
|
||||
|
||||
smali/baksmali is an assembler/disassembler for the dex format used by dalvik, Android's Java VM implementation. The syntax is loosely based on Jasmin's/dedexer's syntax, and supports the full functionality of the dex format (annotations, debug info, line info, etc.)
|
||||
|
||||
The primary webpage is http://smali.googlecode.com, and the source is also mirrored at https://github.com/jesusfreke/smali. If you are interested in submitting a patch, feel free to send me a pull request on either site.
|
||||
Downloads are at https://bitbucket.org/JesusFreke/smali/downloads. If you are interested in submitting a patch, feel free to send me a pull request here.
|
||||
|
||||
See [the wiki](https://github.com/JesusFreke/smali/wiki) for more info/news/release notes/etc.
|
||||
|
||||
#### Support
|
||||
- [googlecode Issue tracker](https://code.google.com/p/smali/issues/list) - For any bugs/issues/feature requests
|
||||
- [#smali on freenode](http://webchat.freenode.net/?channels=smali) - Free free to drop by and ask a question. Don't expect an instant response, but if you hang around someone will respond. Think of it more in terms of.. multi-player notepad.
|
||||
- [github Issue tracker](https://github.com/JesusFreke/smali/issues) - For any bugs/issues/feature requests
|
||||
- [#smali on freenode](http://webchat.freenode.net/?channels=smali) - Free free to drop by and ask a question. Don't expect an instant response, but if you hang around someone will respond.
|
||||
|
||||
|
||||
#### Some useful links for getting started with smali
|
||||
|
||||
- [Official dex bytecode reference](http://s.android.com/tech/dalvik/dalvik-bytecode.html)
|
||||
- [Registers wiki page](https://code.google.com/p/smali/wiki/Registers)
|
||||
- [Types, Methods and Fields wiki page](https://code.google.com/p/smali/wiki/TypesMethodsAndFields)
|
||||
- [Official dex format reference](http://s.android.com/tech/dalvik/dex-format.html)
|
||||
- [Official dex bytecode reference](https://source.android.com/devices/tech/dalvik/dalvik-bytecode.html)
|
||||
- [Registers wiki page](https://github.com/JesusFreke/smali/wiki/Registers)
|
||||
- [Types, Methods and Fields wiki page](https://github.com/JesusFreke/smali/wiki/TypesMethodsAndFields)
|
||||
- [Official dex format reference](https://source.android.com/devices/tech/dalvik/dex-format.html)
|
||||
|
@ -146,8 +146,7 @@ public class ClassDefinition {
|
||||
}
|
||||
|
||||
private void writeInterfaces(IndentingWriter writer) throws IOException {
|
||||
List<String> interfaces = Lists.newArrayList(classDef.getInterfaces());
|
||||
Collections.sort(interfaces);
|
||||
List<String> interfaces = classDef.getInterfaces();
|
||||
|
||||
if (interfaces.size() != 0) {
|
||||
writer.write('\n');
|
||||
|
@ -366,7 +366,8 @@ public class MethodDefinition {
|
||||
private List<MethodItem> getMethodItems() {
|
||||
ArrayList<MethodItem> methodItems = new ArrayList<MethodItem>();
|
||||
|
||||
if ((classDef.options.registerInfo != 0) || (classDef.options.deodex && needsAnalyzed())) {
|
||||
if ((classDef.options.registerInfo != 0) || (classDef.options.normalizeVirtualMethods) ||
|
||||
(classDef.options.deodex && needsAnalyzed())) {
|
||||
addAnalyzedInstructionMethodItems(methodItems);
|
||||
} else {
|
||||
addInstructionMethodItems(methodItems);
|
||||
@ -460,7 +461,7 @@ public class MethodDefinition {
|
||||
|
||||
private void addAnalyzedInstructionMethodItems(List<MethodItem> methodItems) {
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classDef.options.classPath, method,
|
||||
classDef.options.inlineResolver);
|
||||
classDef.options.inlineResolver, classDef.options.normalizeVirtualMethods);
|
||||
|
||||
AnalysisException analysisException = methodAnalyzer.getAnalysisException();
|
||||
if (analysisException != null) {
|
||||
|
@ -44,19 +44,18 @@ import org.xml.sax.Attributes;
|
||||
import org.xml.sax.SAXException;
|
||||
import org.xml.sax.helpers.DefaultHandler;
|
||||
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import javax.xml.parsers.SAXParser;
|
||||
import javax.xml.parsers.SAXParserFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
|
||||
public class baksmali {
|
||||
|
||||
public static boolean disassembleDexFile(DexFile dexFile, final baksmaliOptions options) {
|
||||
if (options.registerInfo != 0 || options.deodex) {
|
||||
if (options.registerInfo != 0 || options.deodex || options.normalizeVirtualMethods) {
|
||||
try {
|
||||
Iterable<String> extraClassPathEntries;
|
||||
if (options.extraClassPathEntries != null) {
|
||||
|
@ -36,6 +36,7 @@ import org.jf.dexlib2.analysis.ClassPath;
|
||||
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||
import org.jf.dexlib2.util.SyntheticAccessorResolver;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.File;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
@ -54,7 +55,7 @@ public class baksmaliOptions {
|
||||
|
||||
public int apiLevel = 15;
|
||||
public String outputDirectory = "out";
|
||||
public String dexEntry = "classes.dex";
|
||||
@Nullable public String dexEntry = null;
|
||||
public List<String> bootClassPathDirs = Lists.newArrayList();
|
||||
|
||||
public List<String> bootClassPathEntries = Lists.newArrayList();
|
||||
@ -75,6 +76,7 @@ public class baksmaliOptions {
|
||||
public boolean ignoreErrors = false;
|
||||
public boolean checkPackagePrivateAccess = false;
|
||||
public boolean useImplicitReferences = false;
|
||||
public boolean normalizeVirtualMethods = false;
|
||||
public File customInlineDefinitions = null;
|
||||
public InlineMethodResolver inlineResolver = null;
|
||||
public int registerInfo = 0;
|
||||
|
@ -52,7 +52,7 @@ public class dump {
|
||||
consoleWidth = 120;
|
||||
}
|
||||
|
||||
RawDexFile rawDexFile = new RawDexFile(new Opcodes(apiLevel, experimental), dexFile);
|
||||
RawDexFile rawDexFile = new RawDexFile(Opcodes.forApi(apiLevel), dexFile);
|
||||
DexAnnotator annotator = new DexAnnotator(rawDexFile, consoleWidth);
|
||||
annotator.writeAnnotations(writer);
|
||||
} catch (IOException ex) {
|
||||
|
@ -31,9 +31,11 @@ package org.jf.baksmali;
|
||||
import com.google.common.collect.Lists;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib2.DexFileFactory;
|
||||
import org.jf.dexlib2.DexFileFactory.MultipleDexFilesException;
|
||||
import org.jf.dexlib2.analysis.InlineMethodResolver;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
import org.jf.util.SmaliHelpFormatter;
|
||||
|
||||
@ -217,6 +219,9 @@ public class main {
|
||||
case 'k':
|
||||
options.checkPackagePrivateAccess = true;
|
||||
break;
|
||||
case 'n':
|
||||
options.normalizeVirtualMethods = true;
|
||||
break;
|
||||
case 'N':
|
||||
disassemble = false;
|
||||
break;
|
||||
@ -256,10 +261,20 @@ public class main {
|
||||
}
|
||||
|
||||
//Read in and parse the dex file
|
||||
DexBackedDexFile dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry,
|
||||
options.apiLevel, options.experimental);
|
||||
DexBackedDexFile dexFile = null;
|
||||
try {
|
||||
dexFile = DexFileFactory.loadDexFile(dexFileFile, options.dexEntry, options.apiLevel, options.experimental);
|
||||
} catch (MultipleDexFilesException ex) {
|
||||
System.err.println(String.format("%s contains multiple dex files. You must specify which one to " +
|
||||
"disassemble with the -e option", dexFileFile.getName()));
|
||||
System.err.println("Valid entries include:");
|
||||
for (OatDexFile oatDexFile: ex.oatFile.getDexFiles()) {
|
||||
System.err.println(oatDexFile.filename);
|
||||
}
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (dexFile.isOdexFile()) {
|
||||
if (dexFile.hasOdexOpcodes()) {
|
||||
if (!options.deodex) {
|
||||
System.err.println("Warning: You are disassembling an odex file without deodexing it. You");
|
||||
System.err.println("won't be able to re-assemble the results unless you deodex it with the -x");
|
||||
@ -270,7 +285,7 @@ public class main {
|
||||
options.deodex = false;
|
||||
}
|
||||
|
||||
if (!setBootClassPath && (options.deodex || options.registerInfo != 0)) {
|
||||
if (!setBootClassPath && (options.deodex || options.registerInfo != 0 || options.normalizeVirtualMethods)) {
|
||||
if (dexFile instanceof DexBackedOdexFile) {
|
||||
options.bootClassPathEntries = ((DexBackedOdexFile)dexFile).getDependencies();
|
||||
} else {
|
||||
@ -391,9 +406,9 @@ public class main {
|
||||
.create("r");
|
||||
|
||||
Option classPathOption = OptionBuilder.withLongOpt("bootclasspath")
|
||||
.withDescription("the bootclasspath jars to use, for analysis. Defaults to " +
|
||||
"core.jar:ext.jar:framework.jar:android.policy.jar:services.jar. If the value begins with a " +
|
||||
":, it will be appended to the default bootclasspath instead of replacing it")
|
||||
.withDescription("A colon-separated list of bootclasspath jar/oat files to use for analysis. Add an " +
|
||||
"initial colon to specify that the jars/oats should be appended to the default bootclasspath " +
|
||||
"instead of replacing it")
|
||||
.hasOptionalArg()
|
||||
.withArgName("BOOTCLASSPATH")
|
||||
.create("c");
|
||||
@ -445,6 +460,10 @@ public class main {
|
||||
"4.2.1.")
|
||||
.create("k");
|
||||
|
||||
Option normalizeVirtualMethods = OptionBuilder.withLongOpt("normalize-virtual-methods")
|
||||
.withDescription("Normalize virtual method references to the reference the base method.")
|
||||
.create("n");
|
||||
|
||||
Option dumpOption = OptionBuilder.withLongOpt("dump-to")
|
||||
.withDescription("dumps the given dex file into a single annotated dump file named FILE" +
|
||||
" (<dexfile>.dump by default), along with the normal disassembly")
|
||||
@ -494,6 +513,7 @@ public class main {
|
||||
basicOptions.addOption(noImplicitReferencesOption);
|
||||
basicOptions.addOption(dexEntryOption);
|
||||
basicOptions.addOption(checkPackagePrivateAccessOption);
|
||||
basicOptions.addOption(normalizeVirtualMethods);
|
||||
|
||||
debugOptions.addOption(dumpOption);
|
||||
debugOptions.addOption(ignoreErrorsOption);
|
||||
@ -547,8 +567,7 @@ public class main {
|
||||
"/system/framework/services.jar",
|
||||
"/system/framework/apache-xml.jar",
|
||||
"/system/framework/filterfw.jar");
|
||||
|
||||
} else {
|
||||
} else if (apiLevel < 21) {
|
||||
// this is correct as of api 17/4.2.2
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core.jar",
|
||||
@ -561,6 +580,22 @@ public class main {
|
||||
"/system/framework/android.policy.jar",
|
||||
"/system/framework/services.jar",
|
||||
"/system/framework/apache-xml.jar");
|
||||
} else { // api >= 21
|
||||
// TODO: verify, add new ones?
|
||||
return Lists.newArrayList(
|
||||
"/system/framework/core-libart.jar",
|
||||
"/system/framework/conscrypt.jar",
|
||||
"/system/framework/okhttp.jar",
|
||||
"/system/framework/core-junit.jar",
|
||||
"/system/framework/bouncycastle.jar",
|
||||
"/system/framework/ext.jar",
|
||||
"/system/framework/framework.jar",
|
||||
"/system/framework/telephony-common.jar",
|
||||
"/system/framework/voip-common.jar",
|
||||
"/system/framework/ims-common.jar",
|
||||
"/system/framework/mms-common.jar",
|
||||
"/system/framework/android.policy.jar",
|
||||
"/system/framework/apache-xml.jar");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -81,7 +81,7 @@ public class DisassemblyTest {
|
||||
String inputFilename = getInputFilename(testName);
|
||||
byte[] inputBytes = BaksmaliTestUtils.readResourceBytesFully(getInputFilename(testName));
|
||||
|
||||
DexBackedDexFile inputDex = new DexBackedDexFile(new Opcodes(options.apiLevel, false), inputBytes);
|
||||
DexBackedDexFile inputDex = new DexBackedDexFile(Opcodes.forApi(options.apiLevel), inputBytes);
|
||||
Assert.assertEquals(1, inputDex.getClassCount());
|
||||
ClassDef inputClass = Iterables.getFirst(inputDex.getClasses(), null);
|
||||
Assert.assertNotNull(inputClass);
|
||||
|
@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright 2015, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.baksmali;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
public class InterfaceOrderTest extends IdenticalRoundtripTest {
|
||||
@Test
|
||||
public void testInterfaceOrder() {
|
||||
runTest("InterfaceOrder", new baksmaliOptions());
|
||||
}
|
||||
}
|
@ -22,7 +22,6 @@
|
||||
# return-void
|
||||
# .end method
|
||||
|
||||
|
||||
.method private clah()V
|
||||
.registers 1
|
||||
|
||||
|
@ -17,7 +17,6 @@
|
||||
# .end method
|
||||
|
||||
|
||||
|
||||
# virtual methods
|
||||
.method public alah()V
|
||||
.registers 1
|
||||
@ -40,7 +39,6 @@
|
||||
# return-void
|
||||
# .end method
|
||||
|
||||
|
||||
.method public clah()V
|
||||
.registers 1
|
||||
|
||||
|
@ -22,7 +22,6 @@
|
||||
# return-void
|
||||
# .end method
|
||||
|
||||
|
||||
.method public clah()V
|
||||
.registers 1
|
||||
|
||||
|
@ -0,0 +1,37 @@
|
||||
.class public LInterfaceOrder;
|
||||
.super Ljava/lang/Object;
|
||||
|
||||
# Note how these two interfaces are not in alphabetical order
|
||||
.implements Ljava/io/Serializable;
|
||||
.implements Ljava/util/EventListener;
|
||||
.implements Ljava/lang/Runnable;
|
||||
.implements Ljava/io/Flushable;
|
||||
.implements Ljava/lang/Clonable;
|
||||
.implements Ljava/util/Observer;
|
||||
.implements Ljava/io/Closeable;
|
||||
|
||||
# direct methods
|
||||
.method public constructor <init>()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public close()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public flush()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public run()V
|
||||
.registers 1
|
||||
return-void
|
||||
.end method
|
||||
|
||||
.method public update(Ljava/util/Observable;Ljava/lang/Object;)V
|
||||
.registers 3
|
||||
return-void
|
||||
.end method
|
24
build.gradle
24
build.gradle
@ -31,7 +31,7 @@
|
||||
|
||||
apply plugin: 'idea'
|
||||
|
||||
version = '2.0.5'
|
||||
version = '2.1.0'
|
||||
|
||||
def jarVersion = version
|
||||
|
||||
@ -57,6 +57,11 @@ if (!('release' in gradle.startParameter.taskNames)) {
|
||||
// use something like module-1.2.3-dev.jar for the jar name, rather than the full
|
||||
// module-1.2.3-001afe02-dirty.jar
|
||||
jarVersion = baseVersion + '-dev'
|
||||
} else {
|
||||
if (System.env.JDK6_HOME == null && !JavaVersion.current().isJava6()) {
|
||||
throw new InvalidUserDataException("bzzzzzzzt. Release builds must be performed with java 6. " +
|
||||
"Either run gradle with java 6, or define the JDK6_HOME environment variable.")
|
||||
}
|
||||
}
|
||||
|
||||
// Note: please don't use this. This is strictly for the official releases
|
||||
@ -79,6 +84,19 @@ subprojects {
|
||||
}
|
||||
}
|
||||
|
||||
if (System.env.JDK6_HOME != null) {
|
||||
sourceCompatibility = 1.6
|
||||
targetCompatibility = 1.6
|
||||
|
||||
tasks.withType(JavaCompile) {
|
||||
doFirst {
|
||||
options.fork = true
|
||||
options.bootClasspath = "$System.env.JDK6_HOME/jre/lib/rt.jar"
|
||||
options.bootClasspath += "$File.pathSeparator$System.env.JDK6_HOME/jre/lib/jsse.jar"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
version = parent.version
|
||||
|
||||
ext {
|
||||
@ -90,8 +108,8 @@ subprojects {
|
||||
stringtemplate: 'org.antlr:stringtemplate:3.2.1',
|
||||
commons_cli: 'commons-cli:commons-cli:1.2',
|
||||
jflex: 'de.jflex:jflex:1.4.3',
|
||||
jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.1',
|
||||
proguard_gradle: 'net.sf.proguard:proguard-gradle:5.1',
|
||||
jflex_plugin: 'co.tomlee.gradle.plugins:gradle-jflex-plugin:0.0.2',
|
||||
proguard_gradle: 'net.sf.proguard:proguard-gradle:5.2.1',
|
||||
dx: 'com.google.android.tools:dx:1.7',
|
||||
gson: 'com.google.code.gson:gson:2.3.1'
|
||||
]
|
||||
|
15
dexlib2/OatVersions.txt
Normal file
15
dexlib2/OatVersions.txt
Normal file
@ -0,0 +1,15 @@
|
||||
7642cfc90fc9c3ebfd8e3b5041915705c93b5cf0 - 56
|
||||
- first version with all stability fixes needed for deodexing
|
||||
14691c5e786e8c2c5734f687e4c96217340771be - 57
|
||||
1558b577907b613864e98f05862543557263e864 - 58
|
||||
f3251d12dfa387493dbde4c4148a633802f5f7e3 - 59
|
||||
706cae36209932f258b2fe2e396f31d2dd7d585e - 58 (revert of f3251d12)
|
||||
d7cbf8a6629942e7bd315ffae7e1c77b082f3e11 - 60
|
||||
- return-void-barrier -> return-void-no-barrier
|
||||
1412dfa4adcd511902e510fa0c948b168ab5840c - 61 (re-commit of f3251d12)
|
||||
9d6bf69ad3012a9d843268fdd5325b6719b6d5f2 - 62
|
||||
0de1133ba600f299b3d67938f650720d9f859eb2 - 63
|
||||
07785bb98dc8bbe192970e0f4c2cafd338a8dc68 - 64
|
||||
fa2c054b28d4b540c1b3651401a7a091282a015f - 65
|
||||
7070ccd8b6439477eafeea7ed3736645d78e003f - 64 (revert of fa2c054b)
|
||||
7bf2b4f1d08050f80782217febac55c8cfc5e4ef - 65 (re-commit of fa2c054b)
|
@ -29,6 +29,17 @@
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
ext.testAccessorOutputDir = file("${buildDir}/generated-src/accessorTest/java")
|
||||
ext.testAccessorOutputFile = file("${testAccessorOutputDir}/org/jf/dexlib2/AccessorTypes.java")
|
||||
|
||||
sourceSets {
|
||||
accessorTest {
|
||||
java {
|
||||
srcDir testAccessorOutputDir
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
configurations {
|
||||
accessorTestGenerator
|
||||
dx
|
||||
@ -46,9 +57,6 @@ dependencies {
|
||||
dx depends.dx
|
||||
}
|
||||
|
||||
ext.testAccessorOutputDir = file("${buildDir}/generated-accessor-test-sources")
|
||||
ext.testAccessorOutputFile = file("${buildDir}/generated-accessor-test-sources/org/jf/dexlib2/AccessorTypes.java")
|
||||
|
||||
// You must manually execute this task to regenerate SyntheticAccessorFSM.java, after modifying the ragel file
|
||||
// e.g. ./gradlew ragel
|
||||
task ragel(type:Exec) {
|
||||
@ -59,48 +67,31 @@ task ragel(type:Exec) {
|
||||
}
|
||||
|
||||
task generateAccessorTestSource(type: JavaExec) {
|
||||
doFirst {
|
||||
file(testAccessorOutputFile.parent).mkdirs()
|
||||
}
|
||||
|
||||
file(testAccessorOutputFile.parent).mkdirs()
|
||||
outputs.dir file(testAccessorOutputDir)
|
||||
sourceSets['test'].java.srcDir file(testAccessorOutputDir)
|
||||
|
||||
classpath = configurations.accessorTestGenerator
|
||||
main = 'org.jf.dexlib2.AccessorTestGenerator'
|
||||
args testAccessorOutputFile
|
||||
}
|
||||
compileTestJava.dependsOn generateAccessorTestSource
|
||||
compileAccessorTestJava.dependsOn(generateAccessorTestSource)
|
||||
|
||||
task generateAccessorTestDex(type: JavaExec, dependsOn: compileTestJava) {
|
||||
def outputDex = file(new File(sourceSets.test.output.resourcesDir, 'accessorTest.dex'))
|
||||
// You must manually execute this task to regenerate src/test/resources/accessorTest.dex
|
||||
task generateAccessorTestDex(type: JavaExec, dependsOn: compileAccessorTestJava) {
|
||||
def outputDex = file('src/test/resources/accessorTest.dex')
|
||||
file(outputDex.parent).mkdirs()
|
||||
|
||||
doFirst {
|
||||
file(outputDex.parent).mkdirs()
|
||||
|
||||
// this has to be done in doFirst, so that the generated classes will be available.
|
||||
// otherwise, it the tree will be populated while the build is being configured,
|
||||
// which is before the compileTestJava has run
|
||||
fileTree(project.sourceSets.test.output.classesDir) {
|
||||
include 'org/jf/dexlib2/AccessorTypes*.class'
|
||||
}.each { File file ->
|
||||
args file
|
||||
}
|
||||
}
|
||||
|
||||
inputs.dir(project.sourceSets.test.output.classesDir)
|
||||
inputs.dir(project.sourceSets.accessorTest.output.classesDir)
|
||||
outputs.file outputDex
|
||||
|
||||
main 'com.android.dx.command.Main'
|
||||
classpath = configurations.dx
|
||||
|
||||
workingDir project.sourceSets.test.output.classesDir
|
||||
//executable 'dx'
|
||||
args '--dex'
|
||||
args '--no-strict'
|
||||
args "--output=${outputDex}"
|
||||
args sourceSets.accessorTest.output.classesDir
|
||||
}
|
||||
test.dependsOn generateAccessorTestDex
|
||||
|
||||
uploadArchives {
|
||||
repositories.mavenDeployer {
|
||||
|
@ -31,40 +31,56 @@
|
||||
|
||||
package org.jf.dexlib2;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedOdexFile;
|
||||
import org.jf.dexlib2.dexbacked.OatFile;
|
||||
import org.jf.dexlib2.dexbacked.OatFile.NotAnOatFileException;
|
||||
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
|
||||
import org.jf.dexlib2.iface.DexFile;
|
||||
import org.jf.dexlib2.writer.pool.DexPool;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.*;
|
||||
import java.util.List;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipFile;
|
||||
|
||||
public final class DexFileFactory {
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(String path, int api, boolean experimental)
|
||||
throws IOException {
|
||||
return loadDexFile(new File(path), "classes.dex", new Opcodes(api, experimental));
|
||||
public static DexBackedDexFile loadDexFile(@Nonnull String path, int api) throws IOException {
|
||||
return loadDexFile(path, api, false);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, int api, boolean experimental)
|
||||
public static DexBackedDexFile loadDexFile(@Nonnull String path, int api, boolean experimental)
|
||||
throws IOException {
|
||||
return loadDexFile(dexFile, "classes.dex", new Opcodes(api, experimental));
|
||||
return loadDexFile(new File(path), "classes.dex", Opcodes.forApi(api, experimental));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry, int api,
|
||||
public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, int api) throws IOException {
|
||||
return loadDexFile(dexFile, api, false);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, int api, boolean experimental)
|
||||
throws IOException {
|
||||
return loadDexFile(dexFile, null, Opcodes.forApi(api, experimental));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, @Nullable String dexEntry, int api,
|
||||
boolean experimental) throws IOException {
|
||||
return loadDexFile(dexFile, dexEntry, new Opcodes(api, experimental));
|
||||
return loadDexFile(dexFile, dexEntry, Opcodes.forApi(api, experimental));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexBackedDexFile loadDexFile(File dexFile, String dexEntry,
|
||||
@Nonnull Opcodes opcodes) throws IOException {
|
||||
public static DexBackedDexFile loadDexFile(@Nonnull File dexFile, @Nullable String dexEntry,
|
||||
@Nonnull Opcodes opcodes) throws IOException {
|
||||
ZipFile zipFile = null;
|
||||
boolean isZipFile = false;
|
||||
try {
|
||||
@ -72,16 +88,18 @@ public final class DexFileFactory {
|
||||
// if we get here, it's safe to assume we have a zip file
|
||||
isZipFile = true;
|
||||
|
||||
ZipEntry zipEntry = zipFile.getEntry(dexEntry);
|
||||
String zipEntryName = MoreObjects.firstNonNull(dexEntry, "classes.dex");
|
||||
ZipEntry zipEntry = zipFile.getEntry(zipEntryName);
|
||||
if (zipEntry == null) {
|
||||
throw new NoClassesDexException("zip file %s does not contain a classes.dex file", dexFile.getName());
|
||||
throw new DexFileNotFound("zip file %s does not contain a %s file", dexFile.getName(), zipEntryName);
|
||||
}
|
||||
long fileLength = zipEntry.getSize();
|
||||
if (fileLength < 40) {
|
||||
throw new ExceptionWithContext(
|
||||
"The " + dexEntry + " file in %s is too small to be a valid dex file", dexFile.getName());
|
||||
throw new ExceptionWithContext("The %s file in %s is too small to be a valid dex file",
|
||||
zipEntryName, dexFile.getName());
|
||||
} else if (fileLength > Integer.MAX_VALUE) {
|
||||
throw new ExceptionWithContext("The " + dexEntry + " file in %s is too large to read in", dexFile.getName());
|
||||
throw new ExceptionWithContext("The %s file in %s is too large to read in",
|
||||
zipEntryName, dexFile.getName());
|
||||
}
|
||||
byte[] dexBytes = new byte[(int)fileLength];
|
||||
ByteStreams.readFully(zipFile.getInputStream(zipEntry), dexBytes);
|
||||
@ -102,41 +120,107 @@ public final class DexFileFactory {
|
||||
}
|
||||
|
||||
InputStream inputStream = new BufferedInputStream(new FileInputStream(dexFile));
|
||||
|
||||
try {
|
||||
return DexBackedDexFile.fromInputStream(opcodes, inputStream);
|
||||
} catch (DexBackedDexFile.NotADexFile ex) {
|
||||
// just eat it
|
||||
try {
|
||||
return DexBackedDexFile.fromInputStream(opcodes, inputStream);
|
||||
} catch (DexBackedDexFile.NotADexFile ex) {
|
||||
// just eat it
|
||||
}
|
||||
|
||||
// Note: DexBackedDexFile.fromInputStream will reset inputStream back to the same position, if it fails
|
||||
|
||||
try {
|
||||
return DexBackedOdexFile.fromInputStream(opcodes, inputStream);
|
||||
} catch (DexBackedOdexFile.NotAnOdexFile ex) {
|
||||
// just eat it
|
||||
}
|
||||
|
||||
OatFile oatFile = null;
|
||||
try {
|
||||
oatFile = OatFile.fromInputStream(inputStream);
|
||||
} catch (NotAnOatFileException ex) {
|
||||
// just eat it
|
||||
}
|
||||
|
||||
if (oatFile != null) {
|
||||
if (oatFile.isSupportedVersion() == OatFile.UNSUPPORTED) {
|
||||
throw new UnsupportedOatVersionException(oatFile);
|
||||
}
|
||||
|
||||
List<OatDexFile> oatDexFiles = oatFile.getDexFiles();
|
||||
|
||||
if (oatDexFiles.size() == 0) {
|
||||
throw new DexFileNotFound("Oat file %s contains no dex files", dexFile.getName());
|
||||
}
|
||||
|
||||
if (dexEntry == null) {
|
||||
if (oatDexFiles.size() > 1) {
|
||||
throw new MultipleDexFilesException(oatFile);
|
||||
}
|
||||
return oatDexFiles.get(0);
|
||||
} else {
|
||||
// first check for an exact match
|
||||
for (OatDexFile oatDexFile : oatFile.getDexFiles()) {
|
||||
if (oatDexFile.filename.equals(dexEntry)) {
|
||||
return oatDexFile;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dexEntry.contains("/")) {
|
||||
for (OatDexFile oatDexFile : oatFile.getDexFiles()) {
|
||||
File oatEntryFile = new File(oatDexFile.filename);
|
||||
if (oatEntryFile.getName().equals(dexEntry)) {
|
||||
return oatDexFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw new DexFileNotFound("oat file %s does not contain a dex file named %s",
|
||||
dexFile.getName(), dexEntry);
|
||||
}
|
||||
}
|
||||
} finally {
|
||||
inputStream.close();
|
||||
}
|
||||
|
||||
// Note: DexBackedDexFile.fromInputStream will reset inputStream back to the same position, if it fails
|
||||
|
||||
try {
|
||||
return DexBackedOdexFile.fromInputStream(opcodes, inputStream);
|
||||
} catch (DexBackedOdexFile.NotAnOdexFile ex) {
|
||||
// just eat it
|
||||
}
|
||||
|
||||
throw new ExceptionWithContext("%s is not an apk, dex file or odex file.", dexFile.getPath());
|
||||
throw new ExceptionWithContext("%s is not an apk, dex, odex or oat file.", dexFile.getPath());
|
||||
}
|
||||
|
||||
public static void writeDexFile(String path, DexFile dexFile) throws IOException {
|
||||
public static void writeDexFile(@Nonnull String path, @Nonnull DexFile dexFile) throws IOException {
|
||||
DexPool.writeTo(path, dexFile);
|
||||
}
|
||||
|
||||
private DexFileFactory() {}
|
||||
|
||||
public static class NoClassesDexException extends ExceptionWithContext {
|
||||
public NoClassesDexException(Throwable cause) {
|
||||
public static class DexFileNotFound extends ExceptionWithContext {
|
||||
public DexFileNotFound(@Nullable Throwable cause) {
|
||||
super(cause);
|
||||
}
|
||||
|
||||
public NoClassesDexException(Throwable cause, String message, Object... formatArgs) {
|
||||
public DexFileNotFound(@Nullable Throwable cause, @Nullable String message, Object... formatArgs) {
|
||||
super(cause, message, formatArgs);
|
||||
}
|
||||
|
||||
public NoClassesDexException(String message, Object... formatArgs) {
|
||||
public DexFileNotFound(@Nullable String message, Object... formatArgs) {
|
||||
super(message, formatArgs);
|
||||
}
|
||||
}
|
||||
|
||||
public static class MultipleDexFilesException extends ExceptionWithContext {
|
||||
@Nonnull public final OatFile oatFile;
|
||||
|
||||
public MultipleDexFilesException(@Nonnull OatFile oatFile) {
|
||||
super("Oat file has multiple dex files.");
|
||||
this.oatFile = oatFile;
|
||||
}
|
||||
}
|
||||
|
||||
public static class UnsupportedOatVersionException extends ExceptionWithContext {
|
||||
@Nonnull public final OatFile oatFile;
|
||||
|
||||
public UnsupportedOatVersionException(@Nonnull OatFile oatFile) {
|
||||
super("Unsupported oat version: %d", oatFile.getOatVersion());
|
||||
this.oatFile = oatFile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -31,271 +31,289 @@
|
||||
|
||||
package org.jf.dexlib2;
|
||||
|
||||
import com.google.common.collect.ImmutableRangeMap;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Range;
|
||||
import com.google.common.collect.RangeMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
public enum Opcode
|
||||
{
|
||||
NOP((short)0x00, "nop", ReferenceType.NONE, Format.Format10x, Opcode.CAN_CONTINUE),
|
||||
MOVE((short)0x01, "move", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_FROM16((short)0x02, "move/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_16((short)0x03, "move/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_WIDE((short)0x04, "move-wide", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_WIDE_FROM16((short)0x05, "move-wide/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_WIDE_16((short)0x06, "move-wide/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_OBJECT((short)0x07, "move-object", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_OBJECT_FROM16((short)0x08, "move-object/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_OBJECT_16((short)0x09, "move-object/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_RESULT((short)0x0a, "move-result", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_RESULT_WIDE((short)0x0b, "move-result-wide", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_RESULT_OBJECT((short)0x0c, "move-result-object", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_EXCEPTION((short)0x0d, "move-exception", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
RETURN_VOID((short)0x0e, "return-void", ReferenceType.NONE, Format.Format10x),
|
||||
RETURN((short)0x0f, "return", ReferenceType.NONE, Format.Format11x),
|
||||
RETURN_WIDE((short)0x10, "return-wide", ReferenceType.NONE, Format.Format11x),
|
||||
RETURN_OBJECT((short)0x11, "return-object", ReferenceType.NONE, Format.Format11x),
|
||||
CONST_4((short)0x12, "const/4", ReferenceType.NONE, Format.Format11n, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_16((short)0x13, "const/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST((short)0x14, "const", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_HIGH16((short)0x15, "const/high16", ReferenceType.NONE, Format.Format21ih, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_WIDE_16((short)0x16, "const-wide/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE_32((short)0x17, "const-wide/32", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE((short)0x18, "const-wide", ReferenceType.NONE, Format.Format51l, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE_HIGH16((short)0x19, "const-wide/high16", ReferenceType.NONE, Format.Format21lh, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_STRING((short)0x1a, "const-string", ReferenceType.STRING, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER, (short)0x1b),
|
||||
CONST_STRING_JUMBO((short)0x1b, "const-string/jumbo", ReferenceType.STRING, Format.Format31c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_CLASS((short)0x1c, "const-class", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MONITOR_ENTER((short)0x1d, "monitor-enter", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
MONITOR_EXIT((short)0x1e, "monitor-exit", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
CHECK_CAST((short)0x1f, "check-cast", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INSTANCE_OF((short)0x20, "instance-of", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ARRAY_LENGTH((short)0x21, "array-length", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEW_INSTANCE((short)0x22, "new-instance", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEW_ARRAY((short)0x23, "new-array", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
FILLED_NEW_ARRAY((short)0x24, "filled-new-array", ReferenceType.TYPE, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
FILLED_NEW_ARRAY_RANGE((short)0x25, "filled-new-array/range", ReferenceType.TYPE, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
FILL_ARRAY_DATA((short)0x26, "fill-array-data", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||
THROW((short)0x27, "throw", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW),
|
||||
GOTO((short)0x28, "goto", ReferenceType.NONE, Format.Format10t),
|
||||
GOTO_16((short)0x29, "goto/16", ReferenceType.NONE, Format.Format20t),
|
||||
GOTO_32((short)0x2a, "goto/32", ReferenceType.NONE, Format.Format30t),
|
||||
PACKED_SWITCH((short)0x2b, "packed-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||
SPARSE_SWITCH((short)0x2c, "sparse-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||
CMPL_FLOAT((short)0x2d, "cmpl-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMPG_FLOAT((short)0x2e, "cmpg-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMPL_DOUBLE((short)0x2f, "cmpl-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMPG_DOUBLE((short)0x30, "cmpg-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMP_LONG((short)0x31, "cmp-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IF_EQ((short)0x32, "if-eq", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_NE((short)0x33, "if-ne", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_LT((short)0x34, "if-lt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_GE((short)0x35, "if-ge", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_GT((short)0x36, "if-gt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_LE((short)0x37, "if-le", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_EQZ((short)0x38, "if-eqz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_NEZ((short)0x39, "if-nez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_LTZ((short)0x3a, "if-ltz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_GEZ((short)0x3b, "if-gez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_GTZ((short)0x3c, "if-gtz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_LEZ((short)0x3d, "if-lez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
AGET((short)0x44, "aget", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_WIDE((short)0x45, "aget-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
AGET_OBJECT((short)0x46, "aget-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_BOOLEAN((short)0x47, "aget-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_BYTE((short)0x48, "aget-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_CHAR((short)0x49, "aget-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_SHORT((short)0x4a, "aget-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
APUT((short)0x4b, "aput", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_WIDE((short)0x4c, "aput-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_OBJECT((short)0x4d, "aput-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_BOOLEAN((short)0x4e, "aput-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_BYTE((short)0x4f, "aput-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_CHAR((short)0x50, "aput-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_SHORT((short)0x51, "aput-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IGET((short)0x52, "iget", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_WIDE((short)0x53, "iget-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT((short)0x54, "iget-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_BOOLEAN((short)0x55, "iget-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_BYTE((short)0x56, "iget-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_CHAR((short)0x57, "iget-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_SHORT((short)0x58, "iget-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IPUT((short)0x59, "iput", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE((short)0x5a, "iput-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT((short)0x5b, "iput-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_BOOLEAN((short)0x5c, "iput-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_BYTE((short)0x5d, "iput-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_CHAR((short)0x5e, "iput-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_SHORT((short)0x5f, "iput-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SGET((short)0x60, "sget", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_WIDE((short)0x61, "sget-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SGET_OBJECT((short)0x62, "sget-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_BOOLEAN((short)0x63, "sget-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_BYTE((short)0x64, "sget-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_CHAR((short)0x65, "sget-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SGET_SHORT((short)0x66, "sget-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SPUT((short)0x67, "sput", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_WIDE((short)0x68, "sput-wide", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_OBJECT((short)0x69, "sput-object", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_BOOLEAN((short)0x6a, "sput-boolean", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_BYTE((short)0x6b, "sput-byte", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_CHAR((short)0x6c, "sput-char", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SPUT_SHORT((short)0x6d, "sput-short", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
INVOKE_VIRTUAL((short)0x6e, "invoke-virtual", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER((short)0x6f, "invoke-super", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT((short)0x70, "invoke-direct", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC((short)0x71, "invoke-static", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE((short)0x72, "invoke-interface", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_RANGE((short)0x74, "invoke-virtual/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_RANGE((short)0x75, "invoke-super/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT_RANGE((short)0x76, "invoke-direct/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC_RANGE((short)0x77, "invoke-static/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE_RANGE((short)0x78, "invoke-interface/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
NEG_INT((short)0x7b, "neg-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NOT_INT((short)0x7c, "not-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEG_LONG((short)0x7d, "neg-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
NOT_LONG((short)0x7e, "not-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
NEG_FLOAT((short)0x7f, "neg-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEG_DOUBLE((short)0x80, "neg-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
INT_TO_LONG((short)0x81, "int-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
INT_TO_FLOAT((short)0x82, "int-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_DOUBLE((short)0x83, "int-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
LONG_TO_INT((short)0x84, "long-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
LONG_TO_FLOAT((short)0x85, "long-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
LONG_TO_DOUBLE((short)0x86, "long-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
FLOAT_TO_INT((short)0x87, "float-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
FLOAT_TO_LONG((short)0x88, "float-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
FLOAT_TO_DOUBLE((short)0x89, "float-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DOUBLE_TO_INT((short)0x8a, "double-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DOUBLE_TO_LONG((short)0x8b, "double-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DOUBLE_TO_FLOAT((short)0x8c, "double-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_BYTE((short)0x8d, "int-to-byte", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_CHAR((short)0x8e, "int-to-char", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_SHORT((short)0x8f, "int-to-short", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_INT((short)0x90, "add-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_INT((short)0x91, "sub-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT((short)0x92, "mul-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT((short)0x93, "div-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT((short)0x94, "rem-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT((short)0x95, "and-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT((short)0x96, "or-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT((short)0x97, "xor-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHL_INT((short)0x98, "shl-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHR_INT((short)0x99, "shr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
USHR_INT((short)0x9a, "ushr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_LONG((short)0x9b, "add-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_LONG((short)0x9c, "sub-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_LONG((short)0x9d, "mul-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_LONG((short)0x9e, "div-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_LONG((short)0x9f, "rem-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
AND_LONG((short)0xa0, "and-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
OR_LONG((short)0xa1, "or-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
XOR_LONG((short)0xa2, "xor-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHL_LONG((short)0xa3, "shl-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHR_LONG((short)0xa4, "shr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
USHR_LONG((short)0xa5, "ushr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_FLOAT((short)0xa6, "add-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_FLOAT((short)0xa7, "sub-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_FLOAT((short)0xa8, "mul-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_FLOAT((short)0xa9, "div-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_FLOAT((short)0xaa, "rem-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_DOUBLE((short)0xab, "add-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_DOUBLE((short)0xac, "sub-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_DOUBLE((short)0xad, "mul-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_DOUBLE((short)0xae, "div-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_DOUBLE((short)0xaf, "rem-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_INT_2ADDR((short)0xb0, "add-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_INT_2ADDR((short)0xb1, "sub-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT_2ADDR((short)0xb2, "mul-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT_2ADDR((short)0xb3, "div-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT_2ADDR((short)0xb4, "rem-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT_2ADDR((short)0xb5, "and-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT_2ADDR((short)0xb6, "or-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT_2ADDR((short)0xb7, "xor-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHL_INT_2ADDR((short)0xb8, "shl-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHR_INT_2ADDR((short)0xb9, "shr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
USHR_INT_2ADDR((short)0xba, "ushr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_LONG_2ADDR((short)0xbb, "add-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_LONG_2ADDR((short)0xbc, "sub-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_LONG_2ADDR((short)0xbd, "mul-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_LONG_2ADDR((short)0xbe, "div-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_LONG_2ADDR((short)0xbf, "rem-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
AND_LONG_2ADDR((short)0xc0, "and-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
OR_LONG_2ADDR((short)0xc1, "or-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
XOR_LONG_2ADDR((short)0xc2, "xor-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHL_LONG_2ADDR((short)0xc3, "shl-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHR_LONG_2ADDR((short)0xc4, "shr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
USHR_LONG_2ADDR((short)0xc5, "ushr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_FLOAT_2ADDR((short)0xc6, "add-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_FLOAT_2ADDR((short)0xc7, "sub-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_FLOAT_2ADDR((short)0xc8, "mul-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_FLOAT_2ADDR((short)0xc9, "div-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_FLOAT_2ADDR((short)0xca, "rem-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_DOUBLE_2ADDR((short)0xcb, "add-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_DOUBLE_2ADDR((short)0xcc, "sub-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_DOUBLE_2ADDR((short)0xcd, "mul-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_DOUBLE_2ADDR((short)0xce, "div-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_DOUBLE_2ADDR((short)0xcf, "rem-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_INT_LIT16((short)0xd0, "add-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
RSUB_INT((short)0xd1, "rsub-int", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT_LIT16((short)0xd2, "mul-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT_LIT16((short)0xd3, "div-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT_LIT16((short)0xd4, "rem-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT_LIT16((short)0xd5, "and-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT_LIT16((short)0xd6, "or-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT_LIT16((short)0xd7, "xor-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_INT_LIT8((short)0xd8, "add-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
RSUB_INT_LIT8((short)0xd9, "rsub-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT_LIT8((short)0xda, "mul-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT_LIT8((short)0xdb, "div-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT_LIT8((short)0xdc, "rem-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT_LIT8((short)0xdd, "and-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT_LIT8((short)0xde, "or-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT_LIT8((short)0xdf, "xor-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHL_INT_LIT8((short)0xe0, "shl-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHR_INT_LIT8((short)0xe1, "shr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
USHR_INT_LIT8((short)0xe2, "ushr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NOP(0x00, "nop", ReferenceType.NONE, Format.Format10x, Opcode.CAN_CONTINUE),
|
||||
MOVE(0x01, "move", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_FROM16(0x02, "move/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_16(0x03, "move/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_WIDE(0x04, "move-wide", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_WIDE_FROM16(0x05, "move-wide/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_WIDE_16(0x06, "move-wide/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_OBJECT(0x07, "move-object", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_OBJECT_FROM16(0x08, "move-object/from16", ReferenceType.NONE, Format.Format22x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_OBJECT_16(0x09, "move-object/16", ReferenceType.NONE, Format.Format32x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_RESULT(0x0a, "move-result", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_RESULT_WIDE(0x0b, "move-result-wide", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MOVE_RESULT_OBJECT(0x0c, "move-result-object", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MOVE_EXCEPTION(0x0d, "move-exception", ReferenceType.NONE, Format.Format11x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
RETURN_VOID(0x0e, "return-void", ReferenceType.NONE, Format.Format10x),
|
||||
RETURN(0x0f, "return", ReferenceType.NONE, Format.Format11x),
|
||||
RETURN_WIDE(0x10, "return-wide", ReferenceType.NONE, Format.Format11x),
|
||||
RETURN_OBJECT(0x11, "return-object", ReferenceType.NONE, Format.Format11x),
|
||||
CONST_4(0x12, "const/4", ReferenceType.NONE, Format.Format11n, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_16(0x13, "const/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST(0x14, "const", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_HIGH16(0x15, "const/high16", ReferenceType.NONE, Format.Format21ih, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_WIDE_16(0x16, "const-wide/16", ReferenceType.NONE, Format.Format21s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE_32(0x17, "const-wide/32", ReferenceType.NONE, Format.Format31i, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE(0x18, "const-wide", ReferenceType.NONE, Format.Format51l, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_WIDE_HIGH16(0x19, "const-wide/high16", ReferenceType.NONE, Format.Format21lh, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
CONST_STRING(0x1a, "const-string", ReferenceType.STRING, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_STRING_JUMBO(0x1b, "const-string/jumbo", ReferenceType.STRING, Format.Format31c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CONST_CLASS(0x1c, "const-class", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MONITOR_ENTER(0x1d, "monitor-enter", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
MONITOR_EXIT(0x1e, "monitor-exit", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
CHECK_CAST(0x1f, "check-cast", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INSTANCE_OF(0x20, "instance-of", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ARRAY_LENGTH(0x21, "array-length", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEW_INSTANCE(0x22, "new-instance", ReferenceType.TYPE, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEW_ARRAY(0x23, "new-array", ReferenceType.TYPE, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
FILLED_NEW_ARRAY(0x24, "filled-new-array", ReferenceType.TYPE, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
FILLED_NEW_ARRAY_RANGE(0x25, "filled-new-array/range", ReferenceType.TYPE, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
FILL_ARRAY_DATA(0x26, "fill-array-data", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||
THROW(0x27, "throw", ReferenceType.NONE, Format.Format11x, Opcode.CAN_THROW),
|
||||
GOTO(0x28, "goto", ReferenceType.NONE, Format.Format10t),
|
||||
GOTO_16(0x29, "goto/16", ReferenceType.NONE, Format.Format20t),
|
||||
GOTO_32(0x2a, "goto/32", ReferenceType.NONE, Format.Format30t),
|
||||
PACKED_SWITCH(0x2b, "packed-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||
SPARSE_SWITCH(0x2c, "sparse-switch", ReferenceType.NONE, Format.Format31t, Opcode.CAN_CONTINUE),
|
||||
CMPL_FLOAT(0x2d, "cmpl-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMPG_FLOAT(0x2e, "cmpg-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMPL_DOUBLE(0x2f, "cmpl-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMPG_DOUBLE(0x30, "cmpg-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
CMP_LONG(0x31, "cmp-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IF_EQ(0x32, "if-eq", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_NE(0x33, "if-ne", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_LT(0x34, "if-lt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_GE(0x35, "if-ge", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_GT(0x36, "if-gt", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_LE(0x37, "if-le", ReferenceType.NONE, Format.Format22t, Opcode.CAN_CONTINUE),
|
||||
IF_EQZ(0x38, "if-eqz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_NEZ(0x39, "if-nez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_LTZ(0x3a, "if-ltz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_GEZ(0x3b, "if-gez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_GTZ(0x3c, "if-gtz", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
IF_LEZ(0x3d, "if-lez", ReferenceType.NONE, Format.Format21t, Opcode.CAN_CONTINUE),
|
||||
AGET(0x44, "aget", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_WIDE(0x45, "aget-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
AGET_OBJECT(0x46, "aget-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_BOOLEAN(0x47, "aget-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_BYTE(0x48, "aget-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_CHAR(0x49, "aget-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AGET_SHORT(0x4a, "aget-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
APUT(0x4b, "aput", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_WIDE(0x4c, "aput-wide", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_OBJECT(0x4d, "aput-object", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_BOOLEAN(0x4e, "aput-boolean", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_BYTE(0x4f, "aput-byte", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_CHAR(0x50, "aput-char", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
APUT_SHORT(0x51, "aput-short", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IGET(0x52, "iget", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_WIDE(0x53, "iget-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT(0x54, "iget-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_BOOLEAN(0x55, "iget-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_BYTE(0x56, "iget-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_CHAR(0x57, "iget-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_SHORT(0x58, "iget-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IPUT(0x59, "iput", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE(0x5a, "iput-wide", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT(0x5b, "iput-object", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_BOOLEAN(0x5c, "iput-boolean", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_BYTE(0x5d, "iput-byte", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_CHAR(0x5e, "iput-char", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_SHORT(0x5f, "iput-short", ReferenceType.FIELD, Format.Format22c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SGET(0x60, "sget", ReferenceType.FIELD, Format.Format21c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
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),
|
||||
INVOKE_VIRTUAL(0x6e, "invoke-virtual", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER(0x6f, "invoke-super", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT(0x70, "invoke-direct", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC(0x71, "invoke-static", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE(0x72, "invoke-interface", ReferenceType.METHOD, Format.Format35c, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_RANGE(0x74, "invoke-virtual/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_RANGE(0x75, "invoke-super/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT_RANGE(0x76, "invoke-direct/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_STATIC_RANGE(0x77, "invoke-static/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_INTERFACE_RANGE(0x78, "invoke-interface/range", ReferenceType.METHOD, Format.Format3rc, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
NEG_INT(0x7b, "neg-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NOT_INT(0x7c, "not-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEG_LONG(0x7d, "neg-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
NOT_LONG(0x7e, "not-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
NEG_FLOAT(0x7f, "neg-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
NEG_DOUBLE(0x80, "neg-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
INT_TO_LONG(0x81, "int-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
INT_TO_FLOAT(0x82, "int-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_DOUBLE(0x83, "int-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
LONG_TO_INT(0x84, "long-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
LONG_TO_FLOAT(0x85, "long-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
LONG_TO_DOUBLE(0x86, "long-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
FLOAT_TO_INT(0x87, "float-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
FLOAT_TO_LONG(0x88, "float-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
FLOAT_TO_DOUBLE(0x89, "float-to-double", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DOUBLE_TO_INT(0x8a, "double-to-int", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DOUBLE_TO_LONG(0x8b, "double-to-long", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DOUBLE_TO_FLOAT(0x8c, "double-to-float", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_BYTE(0x8d, "int-to-byte", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_CHAR(0x8e, "int-to-char", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
INT_TO_SHORT(0x8f, "int-to-short", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_INT(0x90, "add-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_INT(0x91, "sub-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT(0x92, "mul-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT(0x93, "div-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT(0x94, "rem-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT(0x95, "and-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT(0x96, "or-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT(0x97, "xor-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHL_INT(0x98, "shl-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHR_INT(0x99, "shr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
USHR_INT(0x9a, "ushr-int", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_LONG(0x9b, "add-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_LONG(0x9c, "sub-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_LONG(0x9d, "mul-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_LONG(0x9e, "div-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_LONG(0x9f, "rem-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
AND_LONG(0xa0, "and-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
OR_LONG(0xa1, "or-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
XOR_LONG(0xa2, "xor-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHL_LONG(0xa3, "shl-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHR_LONG(0xa4, "shr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
USHR_LONG(0xa5, "ushr-long", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_FLOAT(0xa6, "add-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_FLOAT(0xa7, "sub-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_FLOAT(0xa8, "mul-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_FLOAT(0xa9, "div-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_FLOAT(0xaa, "rem-float", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_DOUBLE(0xab, "add-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_DOUBLE(0xac, "sub-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_DOUBLE(0xad, "mul-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_DOUBLE(0xae, "div-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_DOUBLE(0xaf, "rem-double", ReferenceType.NONE, Format.Format23x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_INT_2ADDR(0xb0, "add-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_INT_2ADDR(0xb1, "sub-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT_2ADDR(0xb2, "mul-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT_2ADDR(0xb3, "div-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT_2ADDR(0xb4, "rem-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT_2ADDR(0xb5, "and-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT_2ADDR(0xb6, "or-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT_2ADDR(0xb7, "xor-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHL_INT_2ADDR(0xb8, "shl-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHR_INT_2ADDR(0xb9, "shr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
USHR_INT_2ADDR(0xba, "ushr-int/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_LONG_2ADDR(0xbb, "add-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_LONG_2ADDR(0xbc, "sub-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_LONG_2ADDR(0xbd, "mul-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_LONG_2ADDR(0xbe, "div-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_LONG_2ADDR(0xbf, "rem-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
AND_LONG_2ADDR(0xc0, "and-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
OR_LONG_2ADDR(0xc1, "or-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
XOR_LONG_2ADDR(0xc2, "xor-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHL_LONG_2ADDR(0xc3, "shl-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SHR_LONG_2ADDR(0xc4, "shr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
USHR_LONG_2ADDR(0xc5, "ushr-long/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_FLOAT_2ADDR(0xc6, "add-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SUB_FLOAT_2ADDR(0xc7, "sub-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_FLOAT_2ADDR(0xc8, "mul-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_FLOAT_2ADDR(0xc9, "div-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_FLOAT_2ADDR(0xca, "rem-float/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_DOUBLE_2ADDR(0xcb, "add-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SUB_DOUBLE_2ADDR(0xcc, "sub-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
MUL_DOUBLE_2ADDR(0xcd, "mul-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
DIV_DOUBLE_2ADDR(0xce, "div-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
REM_DOUBLE_2ADDR(0xcf, "rem-double/2addr", ReferenceType.NONE, Format.Format12x, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
ADD_INT_LIT16(0xd0, "add-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
RSUB_INT(0xd1, "rsub-int", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT_LIT16(0xd2, "mul-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT_LIT16(0xd3, "div-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT_LIT16(0xd4, "rem-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT_LIT16(0xd5, "and-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT_LIT16(0xd6, "or-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT_LIT16(0xd7, "xor-int/lit16", ReferenceType.NONE, Format.Format22s, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
ADD_INT_LIT8(0xd8, "add-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
RSUB_INT_LIT8(0xd9, "rsub-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
MUL_INT_LIT8(0xda, "mul-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
DIV_INT_LIT8(0xdb, "div-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
REM_INT_LIT8(0xdc, "rem-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
AND_INT_LIT8(0xdd, "and-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
OR_INT_LIT8(0xde, "or-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
XOR_INT_LIT8(0xdf, "xor-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHL_INT_LIT8(0xe0, "shl-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SHR_INT_LIT8(0xe1, "shr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
USHR_INT_LIT8(0xe2, "ushr-int/lit8", ReferenceType.NONE, Format.Format22b, Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
|
||||
IGET_VOLATILE((short)0xe3, "iget-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IPUT_VOLATILE((short)0xe4, "iput-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SGET_VOLATILE((short)0xe5, "sget-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SPUT_VOLATILE((short)0xe6, "sput-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IGET_OBJECT_VOLATILE((short)0xe7, "iget-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_WIDE_VOLATILE((short)0xe8, "iget-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
IPUT_WIDE_VOLATILE((short)0xe9, "iput-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SGET_WIDE_VOLATILE((short)0xea, "sget-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
SPUT_WIDE_VOLATILE((short)0xeb, "sput-wide-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IGET_VOLATILE(firstApi(0xe3, 9), "iget-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.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),
|
||||
|
||||
THROW_VERIFICATION_ERROR((short)0xed, "throw-verification-error", minApi(5), ReferenceType.NONE, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW),
|
||||
EXECUTE_INLINE((short)0xee, "execute-inline", ReferenceType.NONE, Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
EXECUTE_INLINE_RANGE((short)0xef, "execute-inline/range", minApi(8), ReferenceType.NONE, Format.Format3rmi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT_EMPTY((short)0xf0, "invoke-direct-empty", maxApi(13), ReferenceType.METHOD, Format.Format35c, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_OBJECT_INIT_RANGE((short)0xf0, "invoke-object-init/range", minApi(14), ReferenceType.METHOD, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
RETURN_VOID_BARRIER((short)0xf1, "return-void-barrier", minApi(11), ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
|
||||
IGET_QUICK((short)0xf2, "iget-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IGET_WIDE_QUICK((short)0xf3, "iget-wide-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER | Opcode.SETS_WIDE_REGISTER),
|
||||
IGET_OBJECT_QUICK((short)0xf4, "iget-object-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
IPUT_QUICK((short)0xf5, "iput-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_WIDE_QUICK((short)0xf6, "iput-wide-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT_QUICK((short)0xf7, "iput-object-quick", maxApi(22), ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_QUICK | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
INVOKE_VIRTUAL_QUICK((short)0xf8, "invoke-virtual-quick", maxApi(22), ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_QUICK_RANGE((short)0xf9, "invoke-virtual-quick/range", maxApi(22), ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK((short)0xfa, "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK_RANGE((short)0xfb, "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
THROW_VERIFICATION_ERROR(firstApi(0xed, 5), "throw-verification-error", ReferenceType.NONE, Format.Format20bc, Opcode.ODEX_ONLY | Opcode.CAN_THROW),
|
||||
EXECUTE_INLINE(allApis(0xee), "execute-inline", ReferenceType.NONE, Format.Format35mi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
EXECUTE_INLINE_RANGE(firstApi(0xef, 8), "execute-inline/range", ReferenceType.NONE, Format.Format3rmi, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_DIRECT_EMPTY(lastApi(0xf0, 13), "invoke-direct-empty", ReferenceType.METHOD, Format.Format35c, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
INVOKE_OBJECT_INIT_RANGE(firstApi(0xf0, 14), "invoke-object-init/range", ReferenceType.METHOD, Format.Format3rc, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.CAN_INITIALIZE_REFERENCE),
|
||||
RETURN_VOID_BARRIER(combine(firstApi(0xf1, 11), lastArtVersion(0x73, 59)), "return-void-barrier", ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
|
||||
RETURN_VOID_NO_BARRIER(firstArtVersion(0x73, 60), "return-void-no-barrier", ReferenceType.NONE, Format.Format10x, Opcode.ODEX_ONLY),
|
||||
IGET_QUICK(combine(allApis(0xf2), allArtVersions(0xe3)), "iget-quick", ReferenceType.NONE, Format.Format22cs, Opcode.ODEX_ONLY | Opcode.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),
|
||||
|
||||
INVOKE_VIRTUAL_QUICK(combine(allApis(0xf8), allArtVersions(0xe9)), "invoke-virtual-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_VIRTUAL_QUICK_RANGE(combine(allApis(0xf9), allArtVersions(0xea)), "invoke-virtual-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK(allApis(0xfa), "invoke-super-quick", ReferenceType.NONE, Format.Format35ms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
INVOKE_SUPER_QUICK_RANGE(allApis(0xfb), "invoke-super-quick/range", ReferenceType.NONE, Format.Format3rms, Opcode.ODEX_ONLY | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT),
|
||||
|
||||
IPUT_OBJECT_VOLATILE((short)0xfc, "iput-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.ODEXED_INSTANCE_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
SGET_OBJECT_VOLATILE((short)0xfd, "sget-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_REGISTER),
|
||||
SPUT_OBJECT_VOLATILE((short)0xfe, "sput-object-volatile", minApi(9), ReferenceType.FIELD, Format.Format21c, Opcode.ODEX_ONLY | Opcode.ODEXED_STATIC_VOLATILE | Opcode.CAN_THROW | Opcode.CAN_CONTINUE),
|
||||
IPUT_OBJECT_VOLATILE(firstApi(0xfc, 9), "iput-object-volatile", ReferenceType.FIELD, Format.Format22c, Opcode.ODEX_ONLY | Opcode.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),
|
||||
|
||||
PACKED_SWITCH_PAYLOAD((short)0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
|
||||
SPARSE_SWITCH_PAYLOAD((short)0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
|
||||
ARRAY_PAYLOAD((short)0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0),
|
||||
PACKED_SWITCH_PAYLOAD(0x100, "packed-switch-payload", ReferenceType.NONE, Format.PackedSwitchPayload, 0),
|
||||
SPARSE_SWITCH_PAYLOAD(0x200, "sparse-switch-payload", ReferenceType.NONE, Format.SparseSwitchPayload, 0),
|
||||
ARRAY_PAYLOAD(0x300, "array-payload", ReferenceType.NONE, Format.ArrayPayload, 0),
|
||||
|
||||
// Reuse the deprecated f3-ff opcodes in Art:
|
||||
INVOKE_LAMBDA((short)0xf3, "invoke-lambda", minApi(23), ReferenceType.NONE, Format.Format25x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.EXPERIMENTAL),
|
||||
INVOKE_LAMBDA(allArtVersions(0xf3),"invoke-lambda", ReferenceType.NONE, Format.Format25x, Opcode.CAN_THROW | Opcode.CAN_CONTINUE | Opcode.SETS_RESULT | Opcode.EXPERIMENTAL),
|
||||
// TODO: What about JUMBO support if the string ID is too large?
|
||||
CAPTURE_VARIABLE((short)0xf5, "capture-variable", minApi(23), ReferenceType.STRING, Format.Format21c, Opcode.EXPERIMENTAL),
|
||||
CREATE_LAMBDA((short)0xf6, "create-lambda", minApi(23), ReferenceType.METHOD, Format.Format21c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
CAPTURE_VARIABLE(allArtVersions(0xf5), "capture-variable", ReferenceType.STRING, Format.Format21c, Opcode.EXPERIMENTAL),
|
||||
CREATE_LAMBDA(allArtVersions(0xf6), "create-lambda", ReferenceType.METHOD, Format.Format21c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
// TODO: do we need a capture/liberate wide?
|
||||
LIBERATE_VARIABLE((short)0xf7, "liberate-variable", minApi(23), ReferenceType.STRING, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
BOX_LAMBDA((short)0xf8, "box-lambda", minApi(23), ReferenceType.NONE, Format.Format22x, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
UNBOX_LAMBDA((short)0xf9, "unbox-lambda", minApi(23), ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL);
|
||||
LIBERATE_VARIABLE(allArtVersions(0xf7), "liberate-variable", ReferenceType.STRING, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
BOX_LAMBDA(allArtVersions(0xf8), "box-lambda", ReferenceType.NONE, Format.Format22x, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL),
|
||||
UNBOX_LAMBDA(allArtVersions(0xf9), "unbox-lambda", ReferenceType.TYPE, Format.Format22c, Opcode.SETS_REGISTER | Opcode.EXPERIMENTAL);
|
||||
|
||||
//if the instruction can throw an exception
|
||||
public static final int CAN_THROW = 0x1;
|
||||
@ -332,58 +350,81 @@ public enum Opcode
|
||||
return api << 16;
|
||||
}
|
||||
|
||||
public final short value;
|
||||
// values and minApis provide a mapping of api -> bytecode value.
|
||||
// the apis in minApis are guaranteed to be
|
||||
public final RangeMap<Integer, Short> apiToValueMap;
|
||||
public final RangeMap<Integer, Short> artVersionToValueMap;
|
||||
|
||||
public final String name;
|
||||
// high 16-bits is the max api, low 16-bits is the min api
|
||||
public final int apiConstraints;
|
||||
public final int referenceType;
|
||||
public final Format format;
|
||||
public final int flags;
|
||||
|
||||
Opcode(short opcodeValue, String opcodeName, int referenceType, Format format) {
|
||||
this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, 0, (short)-1);
|
||||
Opcode(int opcodeValue, String opcodeName, int referenceType, Format format) {
|
||||
this(opcodeValue, opcodeName, referenceType, format, 0);
|
||||
}
|
||||
|
||||
Opcode(short opcodeValue, String opcodeName, int referenceType, Format format, int flags) {
|
||||
this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, flags, (short)-1);
|
||||
Opcode(int opcodeValue, String opcodeName, int referenceType, Format format, int flags) {
|
||||
this(allVersions(opcodeValue), opcodeName, referenceType, format, flags);
|
||||
}
|
||||
|
||||
Opcode(short opcodeValue, String opcodeName, int referenceType, Format format, int flags, short jumboOpcodeValue) {
|
||||
this(opcodeValue, opcodeName, ALL_APIS, referenceType, format, flags, jumboOpcodeValue);
|
||||
}
|
||||
Opcode(List<VersionConstraint> versionConstraints, String opcodeName, int referenceType, Format format, int flags) {
|
||||
ImmutableRangeMap.Builder<Integer, Short> apiToValueBuilder = ImmutableRangeMap.builder();
|
||||
ImmutableRangeMap.Builder<Integer, Short> artVersionToValueBuilder = ImmutableRangeMap.builder();
|
||||
|
||||
Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format) {
|
||||
this(opcodeValue, opcodeName, apiConstraints, referenceType, format, 0, (short)-1);
|
||||
}
|
||||
for (VersionConstraint versionConstraint : versionConstraints) {
|
||||
if (!versionConstraint.apiRange.isEmpty()) {
|
||||
apiToValueBuilder.put(versionConstraint.apiRange, (short)versionConstraint.opcodeValue);
|
||||
}
|
||||
if (!versionConstraint.artVersionRange.isEmpty()) {
|
||||
artVersionToValueBuilder.put(versionConstraint.artVersionRange, (short)versionConstraint.opcodeValue);
|
||||
}
|
||||
}
|
||||
|
||||
Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags) {
|
||||
this(opcodeValue, opcodeName, apiConstraints, referenceType, format, flags, (short)-1);
|
||||
}
|
||||
|
||||
Opcode(short opcodeValue, String opcodeName, int apiConstraints, int referenceType, Format format, int flags,
|
||||
short jumboOpcodeValue) {
|
||||
this.value = opcodeValue;
|
||||
this.apiToValueMap = apiToValueBuilder.build();
|
||||
this.artVersionToValueMap = artVersionToValueBuilder.build();
|
||||
this.name = opcodeName;
|
||||
this.apiConstraints = apiConstraints;
|
||||
this.referenceType = referenceType;
|
||||
this.format = format;
|
||||
this.flags = flags;
|
||||
// TODO: implement jumbo opcodes for dexlib2 and uncomment
|
||||
// this.jumboOpcode = jumboOpcodeValue;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the minimum api level that can use this opcode (inclusive)
|
||||
*/
|
||||
public int getMinApi() {
|
||||
return apiConstraints & 0xFFFF;
|
||||
private static List<VersionConstraint> firstApi(int opcodeValue, int api) {
|
||||
return Lists.newArrayList(new VersionConstraint(Range.atLeast(api), Range.openClosed(0, 0), opcodeValue));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the maximum api level that can to use this opcode (inclusive)
|
||||
*/
|
||||
public int getMaxApi() {
|
||||
return apiConstraints >>> 16;
|
||||
private static List<VersionConstraint> lastApi(int opcodeValue, int api) {
|
||||
Range range;
|
||||
return Lists.newArrayList(new VersionConstraint(Range.atMost(api), Range.openClosed(0, 0), opcodeValue));
|
||||
}
|
||||
|
||||
private static List<VersionConstraint> firstArtVersion(int opcodeValue, int artVersion) {
|
||||
return Lists.newArrayList(new VersionConstraint(Range.openClosed(0, 0), Range.atLeast(artVersion), opcodeValue));
|
||||
}
|
||||
|
||||
private static List<VersionConstraint> lastArtVersion(int opcodeValue, int artVersion) {
|
||||
return Lists.newArrayList(new VersionConstraint(Range.openClosed(0, 0), Range.atMost(artVersion), opcodeValue));
|
||||
}
|
||||
|
||||
private static List<VersionConstraint> allVersions(int opcodeValue) {
|
||||
return Lists.newArrayList(new VersionConstraint(Range.<Integer>all(), Range.<Integer>all(), opcodeValue));
|
||||
}
|
||||
|
||||
private static List<VersionConstraint> allApis(int opcodeValue) {
|
||||
return Lists.newArrayList(new VersionConstraint(Range.<Integer>all(), Range.openClosed(0, 0), opcodeValue));
|
||||
}
|
||||
|
||||
private static List<VersionConstraint> allArtVersions(int opcodeValue) {
|
||||
return Lists.newArrayList(new VersionConstraint(Range.openClosed(0, 0), Range.<Integer>all(), opcodeValue));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<VersionConstraint> combine(List<VersionConstraint>... versionConstraints) {
|
||||
List<VersionConstraint> combinedList = Lists.newArrayList();
|
||||
for (List<VersionConstraint> versionConstraintList: versionConstraints) {
|
||||
combinedList.addAll(versionConstraintList);
|
||||
}
|
||||
return combinedList;
|
||||
}
|
||||
|
||||
public final boolean canThrow() {
|
||||
@ -433,4 +474,17 @@ public enum Opcode
|
||||
public final boolean isExperimental() {
|
||||
return (flags & EXPERIMENTAL) != 0;
|
||||
}
|
||||
|
||||
private static class VersionConstraint {
|
||||
@Nonnull public final Range<Integer> apiRange;
|
||||
@Nonnull public final Range<Integer> artVersionRange;
|
||||
public final int opcodeValue;
|
||||
|
||||
public VersionConstraint(@Nonnull Range<Integer> apiRange, @Nonnull Range<Integer> artVersionRange,
|
||||
int opcodeValue) {
|
||||
this.apiRange = apiRange;
|
||||
this.artVersionRange = artVersionRange;
|
||||
this.opcodeValue = opcodeValue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -32,31 +32,90 @@
|
||||
package org.jf.dexlib2;
|
||||
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.RangeMap;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.EnumMap;
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Opcodes {
|
||||
private final Opcode[] opcodesByValue;
|
||||
private final HashMap<String, Opcode> opcodesByName;
|
||||
|
||||
/**
|
||||
* Either the api level for dalvik opcodes, or the art version for art opcodes
|
||||
*/
|
||||
public final int api;
|
||||
public final int artVersion;
|
||||
@Nonnull private final Opcode[] opcodesByValue = new Opcode[255];
|
||||
@Nonnull private final EnumMap<Opcode, Short> opcodeValues;
|
||||
@Nonnull private final HashMap<String, Opcode> opcodesByName;
|
||||
|
||||
@Nonnull
|
||||
public static Opcodes forApi(int api) {
|
||||
return new Opcodes(api, VersionMap.mapApiToArtVersion(api), false);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Opcodes forApi(int api, boolean experimental) {
|
||||
return new Opcodes(api, VersionMap.mapApiToArtVersion(api), experimental);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Opcodes forArtVersion(int artVersion) {
|
||||
return forArtVersion(artVersion, false);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static Opcodes forArtVersion(int artVersion, boolean experimental) {
|
||||
return new Opcodes(VersionMap.mapArtVersionToApi(artVersion), artVersion, experimental);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Opcodes(int api) {
|
||||
this(api, false);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
public Opcodes(int api, boolean experimental) {
|
||||
opcodesByValue = new Opcode[256];
|
||||
this(api, VersionMap.mapApiToArtVersion(api), experimental);
|
||||
}
|
||||
|
||||
private Opcodes(int api, int artVersion, boolean experimental) {
|
||||
this.api = api;
|
||||
this.artVersion = artVersion;
|
||||
|
||||
opcodeValues = new EnumMap<Opcode, Short>(Opcode.class);
|
||||
opcodesByName = Maps.newHashMap();
|
||||
|
||||
int version;
|
||||
if (isArt()) {
|
||||
version = artVersion;
|
||||
} else {
|
||||
version = api;
|
||||
}
|
||||
|
||||
for (Opcode opcode: Opcode.values()) {
|
||||
if (!opcode.format.isPayloadFormat) {
|
||||
if (api <= opcode.getMaxApi() && api >= opcode.getMinApi() &&
|
||||
(experimental || !opcode.isExperimental())) {
|
||||
opcodesByValue[opcode.value] = opcode;
|
||||
opcodesByName.put(opcode.name.toLowerCase(), opcode);
|
||||
RangeMap<Integer, Short> versionToValueMap;
|
||||
|
||||
if (isArt()) {
|
||||
versionToValueMap = opcode.artVersionToValueMap;
|
||||
} else {
|
||||
versionToValueMap = opcode.apiToValueMap;
|
||||
}
|
||||
|
||||
Short opcodeValue = versionToValueMap.get(version);
|
||||
if (opcodeValue != null && (!opcode.isExperimental() || experimental)) {
|
||||
if (!opcode.format.isPayloadFormat) {
|
||||
opcodesByValue[opcodeValue] = opcode;
|
||||
}
|
||||
opcodeValues.put(opcode, opcodeValue);
|
||||
opcodesByName.put(opcode.name.toLowerCase(), opcode);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Opcode getOpcodeByName(String opcodeName) {
|
||||
public Opcode getOpcodeByName(@Nonnull String opcodeName) {
|
||||
return opcodesByName.get(opcodeName.toLowerCase());
|
||||
}
|
||||
|
||||
@ -76,4 +135,13 @@ public class Opcodes {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public Short getOpcodeValue(@Nonnull Opcode opcode) {
|
||||
return opcodeValues.get(opcode);
|
||||
}
|
||||
|
||||
public boolean isArt() {
|
||||
return artVersion != VersionMap.NO_VERSION;
|
||||
}
|
||||
}
|
||||
|
50
dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java
Normal file
50
dexlib2/src/main/java/org/jf/dexlib2/VersionMap.java
Normal file
@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2015, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2;
|
||||
|
||||
public class VersionMap {
|
||||
public static final int NO_VERSION = -1;
|
||||
|
||||
public static int mapArtVersionToApi(int artVersion) {
|
||||
// TODO: implement this
|
||||
return 20;
|
||||
}
|
||||
|
||||
public static int mapApiToArtVersion(int api) {
|
||||
// TODO: implement this
|
||||
if (api < 20) {
|
||||
return NO_VERSION;
|
||||
} else {
|
||||
return 56;
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2015, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.analysis;
|
||||
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.analysis.util.TypeProtoUtils;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.util.MethodUtil;
|
||||
import org.jf.dexlib2.util.TypeUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
|
||||
public class AnalyzedMethodUtil {
|
||||
public static boolean canAccess(@Nonnull TypeProto type, @Nonnull Method virtualMethod, boolean checkPackagePrivate,
|
||||
boolean checkProtected, boolean checkClass) {
|
||||
if (checkPackagePrivate && MethodUtil.isPackagePrivate(virtualMethod)) {
|
||||
String otherPackage = TypeUtils.getPackage(virtualMethod.getDefiningClass());
|
||||
String thisPackage = TypeUtils.getPackage(type.getType());
|
||||
if (!otherPackage.equals(thisPackage)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkProtected && (virtualMethod.getAccessFlags() & AccessFlags.PROTECTED.getValue()) != 0) {
|
||||
if (!TypeProtoUtils.extendsFrom(type, virtualMethod.getDefiningClass())) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (checkClass) {
|
||||
ClassPath classPath = type.getClassPath();
|
||||
ClassDef methodClassDef = classPath.getClassDef(virtualMethod.getDefiningClass());
|
||||
if (!TypeUtils.canAccessClass(type.getType(), methodClassDef)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
@ -32,6 +32,7 @@
|
||||
package org.jf.dexlib2.analysis;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.dexlib2.immutable.reference.ImmutableFieldReference;
|
||||
@ -160,7 +161,11 @@ public class ArrayProto implements TypeProto {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MethodReference getMethodByVtableIndex(int vtableIndex) {
|
||||
public Method getMethodByVtableIndex(int vtableIndex) {
|
||||
return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
|
||||
}
|
||||
|
||||
@Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
|
||||
return classPath.getClass("Ljava/lang/Object;").findMethodIndexInVtable(method);
|
||||
}
|
||||
}
|
||||
|
@ -31,15 +31,17 @@
|
||||
|
||||
package org.jf.dexlib2.analysis;
|
||||
|
||||
import com.google.common.base.Supplier;
|
||||
import com.google.common.base.Suppliers;
|
||||
import com.google.common.cache.CacheBuilder;
|
||||
import com.google.common.cache.CacheLoader;
|
||||
import com.google.common.cache.LoadingCache;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
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.analysis.reflection.ReflectionClassDef;
|
||||
import org.jf.dexlib2.dexbacked.OatFile.OatDexFile;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.dexlib2.iface.DexFile;
|
||||
import org.jf.dexlib2.immutable.ImmutableDexFile;
|
||||
@ -51,6 +53,7 @@ import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@ -58,6 +61,7 @@ public class ClassPath {
|
||||
@Nonnull private final TypeProto unknownClass;
|
||||
@Nonnull private HashMap<String, ClassDef> availableClasses = Maps.newHashMap();
|
||||
private boolean checkPackagePrivateAccess;
|
||||
public boolean isArt;
|
||||
|
||||
/**
|
||||
* Creates a new ClassPath instance that can load classes from the given dex files
|
||||
@ -82,15 +86,29 @@ public class ClassPath {
|
||||
* Creates a new ClassPath instance that can load classes from the given dex files
|
||||
*
|
||||
* @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order
|
||||
* @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by default
|
||||
* @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);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new ClassPath instance that can load classes from the given dex files
|
||||
*
|
||||
* @param classPath An iterable of DexFile objects. When loading a class, these dex files will be searched in order
|
||||
* @param checkPackagePrivateAccess Whether checkPackagePrivateAccess is needed, enabled for ONLY early API 17 by
|
||||
* default
|
||||
* @param isArt Whether this is ClassPath is for ART
|
||||
*/
|
||||
public ClassPath(@Nonnull Iterable < DexFile > classPath, boolean checkPackagePrivateAccess, boolean isArt) {
|
||||
// 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;
|
||||
|
||||
loadPrimitiveType("Z");
|
||||
loadPrimitiveType("B");
|
||||
@ -128,7 +146,7 @@ public class ClassPath {
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public TypeProto getClass(CharSequence type) {
|
||||
public TypeProto getClass(@Nonnull CharSequence type) {
|
||||
return loadedClasses.getUnchecked(type.toString());
|
||||
}
|
||||
|
||||
@ -173,21 +191,44 @@ public class ClassPath {
|
||||
int api, boolean checkPackagePrivateAccess, boolean experimental) {
|
||||
ArrayList<DexFile> dexFiles = Lists.newArrayList();
|
||||
|
||||
boolean isArt = false;
|
||||
|
||||
for (String classPathEntry: classPath) {
|
||||
try {
|
||||
dexFiles.add(loadClassPathEntry(classPathDirs, classPathEntry, api, experimental));
|
||||
} catch (ExceptionWithContext e){}
|
||||
List<? extends DexFile> classPathDexFiles =
|
||||
loadClassPathEntry(classPathDirs, classPathEntry, api, experimental);
|
||||
if (!isArt) {
|
||||
for (DexFile classPathDexFile: classPathDexFiles) {
|
||||
if (classPathDexFile instanceof OatDexFile) {
|
||||
isArt = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
dexFiles.addAll(classPathDexFiles);
|
||||
}
|
||||
dexFiles.add(dexFile);
|
||||
return new ClassPath(dexFiles, checkPackagePrivateAccess);
|
||||
return new ClassPath(dexFiles, checkPackagePrivateAccess, isArt);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static ClassPath fromClassPath(Iterable<String> classPathDirs, Iterable<String> classPath, DexFile dexFile,
|
||||
int api, boolean checkPackagePrivateAccess, boolean experimental,
|
||||
boolean isArt) {
|
||||
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);
|
||||
}
|
||||
|
||||
private static final Pattern dalvikCacheOdexPattern = Pattern.compile("@([^@]+)@classes.dex$");
|
||||
|
||||
@Nonnull
|
||||
private static DexFile loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
|
||||
@Nonnull String bootClassPathEntry, int api,
|
||||
boolean experimental) {
|
||||
private static List<? extends DexFile> loadClassPathEntry(@Nonnull Iterable<String> classPathDirs,
|
||||
@Nonnull String bootClassPathEntry, int api,
|
||||
boolean experimental) {
|
||||
File rawEntry = new File(bootClassPathEntry);
|
||||
// strip off the path - we only care about the filename
|
||||
String entryName = rawEntry.getName();
|
||||
@ -213,7 +254,15 @@ public class ClassPath {
|
||||
}
|
||||
|
||||
for (String classPathDir: classPathDirs) {
|
||||
for (String ext: new String[]{"", ".odex", ".jar", ".apk", ".zip"}) {
|
||||
String[] extensions;
|
||||
|
||||
if (entryName.endsWith(".oat")) {
|
||||
extensions = new String[] { ".oat" };
|
||||
} else {
|
||||
extensions = new String[] { "", ".odex", ".jar", ".apk", ".zip" };
|
||||
}
|
||||
|
||||
for (String ext: extensions) {
|
||||
File file = new File(classPathDir, baseEntryName + ext);
|
||||
|
||||
if (file.exists() && file.isFile()) {
|
||||
@ -222,9 +271,11 @@ public class ClassPath {
|
||||
"warning: cannot open %s for reading. Will continue looking.", file.getPath()));
|
||||
} else {
|
||||
try {
|
||||
return DexFileFactory.loadDexFile(file, api, experimental);
|
||||
} catch (DexFileFactory.NoClassesDexException ex) {
|
||||
return ImmutableList.of(DexFileFactory.loadDexFile(file, api, experimental));
|
||||
} catch (DexFileNotFound ex) {
|
||||
// ignore and continue
|
||||
} catch (MultipleDexFilesException ex) {
|
||||
return ex.oatFile.getDexFiles();
|
||||
} catch (Exception ex) {
|
||||
throw ExceptionWithContext.withContext(ex,
|
||||
"Error while reading boot class path entry \"%s\"", bootClassPathEntry);
|
||||
@ -235,4 +286,16 @@ public class ClassPath {
|
||||
}
|
||||
throw new ExceptionWithContext("Cannot locate boot class path file %s", bootClassPathEntry);
|
||||
}
|
||||
|
||||
private final Supplier<OdexedFieldInstructionMapper> fieldInstructionMapperSupplier = Suppliers.memoize(
|
||||
new Supplier<OdexedFieldInstructionMapper>() {
|
||||
@Override public OdexedFieldInstructionMapper get() {
|
||||
return new OdexedFieldInstructionMapper(isArt);
|
||||
}
|
||||
});
|
||||
|
||||
@Nonnull
|
||||
public OdexedFieldInstructionMapper getFieldInstructionMapper() {
|
||||
return fieldInstructionMapperSupplier.get();
|
||||
}
|
||||
}
|
||||
|
@ -38,6 +38,7 @@ import com.google.common.collect.FluentIterable;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.analysis.util.TypeProtoUtils;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
@ -46,21 +47,24 @@ import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.dexlib2.immutable.ImmutableMethod;
|
||||
import org.jf.dexlib2.util.MethodUtil;
|
||||
import org.jf.util.AlignmentUtils;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
import org.jf.util.SparseArray;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* A class "prototype". This contains things like the interfaces, the superclass, the vtable and the instance fields
|
||||
* and their offsets.
|
||||
*/
|
||||
public class ClassProto implements TypeProto {
|
||||
private static final byte REFERENCE = 0;
|
||||
private static final byte WIDE = 1;
|
||||
private static final byte OTHER = 2;
|
||||
|
||||
@Nonnull protected final ClassPath classPath;
|
||||
@Nonnull protected final String type;
|
||||
|
||||
@ -346,7 +350,7 @@ public class ClassProto implements TypeProto {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MethodReference getMethodByVtableIndex(int vtableIndex) {
|
||||
public Method getMethodByVtableIndex(int vtableIndex) {
|
||||
List<Method> vtable = getVtable();
|
||||
if (vtableIndex < 0 || vtableIndex >= vtable.size()) {
|
||||
return null;
|
||||
@ -355,21 +359,35 @@ public class ClassProto implements TypeProto {
|
||||
return vtable.get(vtableIndex);
|
||||
}
|
||||
|
||||
@Nonnull SparseArray<FieldReference> getInstanceFields() {
|
||||
return instanceFieldsSupplier.get();
|
||||
public int findMethodIndexInVtable(@Nonnull MethodReference method) {
|
||||
List<Method> vtable = getVtable();
|
||||
for (int i=0; i<vtable.size(); i++) {
|
||||
Method candidate = vtable.get(i);
|
||||
if (MethodUtil.methodSignaturesMatch(candidate, method)) {
|
||||
if (!classPath.shouldCheckPackagePrivateAccess() ||
|
||||
AnalyzedMethodUtil.canAccess(this, candidate, true, false, false)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
@Nonnull private final Supplier<SparseArray<FieldReference>> instanceFieldsSupplier =
|
||||
@Nonnull SparseArray<FieldReference> getInstanceFields() {
|
||||
if (classPath.isArt) {
|
||||
return artInstanceFieldsSupplier.get();
|
||||
} else {
|
||||
return dalvikInstanceFieldsSupplier.get();
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull private final Supplier<SparseArray<FieldReference>> dalvikInstanceFieldsSupplier =
|
||||
Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
|
||||
@Override public SparseArray<FieldReference> get() {
|
||||
//This is a bit of an "involved" operation. We need to follow the same algorithm that dalvik uses to
|
||||
//arrange fields, so that we end up with the same field offsets (which is needed for deodexing).
|
||||
//See mydroid/dalvik/vm/oo/Class.c - computeFieldOffsets()
|
||||
|
||||
final byte REFERENCE = 0;
|
||||
final byte WIDE = 1;
|
||||
final byte OTHER = 2;
|
||||
|
||||
ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
|
||||
final int fieldCount = fields.size();
|
||||
//the "type" for each field in fields. 0=reference,1=wide,2=other
|
||||
@ -520,19 +538,6 @@ public class ClassProto implements TypeProto {
|
||||
return fields;
|
||||
}
|
||||
|
||||
private byte getFieldType(@Nonnull FieldReference field) {
|
||||
switch (field.getType().charAt(0)) {
|
||||
case '[':
|
||||
case 'L':
|
||||
return 0; //REFERENCE
|
||||
case 'J':
|
||||
case 'D':
|
||||
return 1; //WIDE
|
||||
default:
|
||||
return 2; //OTHER
|
||||
}
|
||||
}
|
||||
|
||||
private void swap(byte[] fieldTypes, List<Field> fields, int position1, int position2) {
|
||||
byte tempType = fieldTypes[position1];
|
||||
fieldTypes[position1] = fieldTypes[position2];
|
||||
@ -543,23 +548,199 @@ public class ClassProto implements TypeProto {
|
||||
}
|
||||
});
|
||||
|
||||
private static 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;
|
||||
}
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull private final Supplier<SparseArray<FieldReference>> artInstanceFieldsSupplier =
|
||||
Suppliers.memoize(new Supplier<SparseArray<FieldReference>>() {
|
||||
|
||||
@Override public SparseArray<FieldReference> get() {
|
||||
// We need to follow the same algorithm that art uses to arrange fields, so that we end up with the
|
||||
// same field offsets, which is needed for deodexing.
|
||||
// See LinkFields() in art/runtime/class_linker.cc
|
||||
|
||||
PriorityQueue<FieldGap> gaps = new PriorityQueue<FieldGap>();
|
||||
|
||||
SparseArray<FieldReference> linkedFields = new SparseArray<FieldReference>();
|
||||
ArrayList<Field> fields = getSortedInstanceFields(getClassDef());
|
||||
|
||||
int fieldOffset = 0;
|
||||
String superclassType = getSuperclass();
|
||||
if (superclassType != null) {
|
||||
// TODO: what to do if superclass doesn't exist?
|
||||
ClassProto superclass = (ClassProto) classPath.getClass(superclassType);
|
||||
SparseArray<FieldReference> superFields = superclass.getInstanceFields();
|
||||
FieldReference field = null;
|
||||
int lastOffset = 0;
|
||||
for (int i=0; i<superFields.size(); i++) {
|
||||
int offset = superFields.keyAt(i);
|
||||
field = superFields.valueAt(i);
|
||||
linkedFields.put(offset, field);
|
||||
lastOffset = offset;
|
||||
}
|
||||
if (field != null) {
|
||||
fieldOffset = lastOffset + getFieldSize(field);
|
||||
}
|
||||
}
|
||||
|
||||
for (Field field: fields) {
|
||||
int fieldSize = getFieldSize(field);
|
||||
|
||||
if (!AlignmentUtils.isAligned(fieldOffset, fieldSize)) {
|
||||
int oldOffset = fieldOffset;
|
||||
fieldOffset = AlignmentUtils.alignOffset(fieldOffset, fieldSize);
|
||||
addFieldGap(oldOffset, fieldOffset, gaps);
|
||||
}
|
||||
|
||||
FieldGap gap = gaps.peek();
|
||||
if (gap != null && gap.size >= fieldSize) {
|
||||
gaps.poll();
|
||||
linkedFields.put(gap.offset, field);
|
||||
if (gap.size > fieldSize) {
|
||||
addFieldGap(gap.offset + fieldSize, gap.offset + gap.size, gaps);
|
||||
}
|
||||
} else {
|
||||
linkedFields.append(fieldOffset, field);
|
||||
fieldOffset += fieldSize;
|
||||
}
|
||||
}
|
||||
|
||||
return linkedFields;
|
||||
}
|
||||
|
||||
private void addFieldGap(int gapStart, int gapEnd, @Nonnull PriorityQueue<FieldGap> gaps) {
|
||||
int offset = gapStart;
|
||||
|
||||
while (offset < gapEnd) {
|
||||
int remaining = gapEnd - offset;
|
||||
|
||||
if ((remaining >= 4) && (offset % 4 == 0)) {
|
||||
gaps.add(new FieldGap(offset, 4));
|
||||
offset += 4;
|
||||
} else if (remaining >= 2 && (offset % 2 == 0)) {
|
||||
gaps.add(new FieldGap(offset, 2));
|
||||
offset += 2;
|
||||
} else {
|
||||
gaps.add(new FieldGap(offset, 1));
|
||||
offset += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private ArrayList<Field> getSortedInstanceFields(@Nonnull ClassDef classDef) {
|
||||
ArrayList<Field> fields = Lists.newArrayList(classDef.getInstanceFields());
|
||||
Collections.sort(fields, new Comparator<Field>() {
|
||||
@Override public int compare(Field field1, Field field2) {
|
||||
int result = Ints.compare(getFieldSortOrder(field1), getFieldSortOrder(field2));
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
|
||||
result = field1.getName().compareTo(field2.getName());
|
||||
if (result != 0) {
|
||||
return result;
|
||||
}
|
||||
return field1.getType().compareTo(field2.getType());
|
||||
}
|
||||
});
|
||||
return fields;
|
||||
}
|
||||
|
||||
private int getFieldSortOrder(@Nonnull FieldReference field) {
|
||||
// The sort order is based on type size (except references are first), and then based on the
|
||||
// enum value of the primitive type for types of equal size. See: Primitive::Type enum
|
||||
// in art/runtime/primitive.h
|
||||
switch (field.getType().charAt(0)) {
|
||||
/* reference */
|
||||
case '[':
|
||||
case 'L':
|
||||
return 0;
|
||||
/* 64 bit */
|
||||
case 'J':
|
||||
return 1;
|
||||
case 'D':
|
||||
return 2;
|
||||
/* 32 bit */
|
||||
case 'I':
|
||||
return 3;
|
||||
case 'F':
|
||||
return 4;
|
||||
/* 16 bit */
|
||||
case 'C':
|
||||
return 5;
|
||||
case 'S':
|
||||
return 6;
|
||||
/* 8 bit */
|
||||
case 'Z':
|
||||
return 7;
|
||||
case 'B':
|
||||
return 8;
|
||||
}
|
||||
throw new ExceptionWithContext("Invalid field type: %s", field.getType());
|
||||
}
|
||||
|
||||
private int getFieldSize(@Nonnull FieldReference field) {
|
||||
return getTypeSize(field.getType().charAt(0));
|
||||
}
|
||||
});
|
||||
|
||||
private int getNextFieldOffset() {
|
||||
SparseArray<FieldReference> instanceFields = getInstanceFields();
|
||||
if (instanceFields.size() == 0) {
|
||||
return 8;
|
||||
return classPath.isArt ? 0 : 8;
|
||||
}
|
||||
|
||||
int lastItemIndex = instanceFields.size()-1;
|
||||
int fieldOffset = instanceFields.keyAt(lastItemIndex);
|
||||
FieldReference lastField = instanceFields.valueAt(lastItemIndex);
|
||||
|
||||
switch (lastField.getType().charAt(0)) {
|
||||
if (classPath.isArt) {
|
||||
return fieldOffset + getTypeSize(lastField.getType().charAt(0));
|
||||
} else {
|
||||
switch (lastField.getType().charAt(0)) {
|
||||
case 'J':
|
||||
case 'D':
|
||||
return fieldOffset + 8;
|
||||
default:
|
||||
return fieldOffset + 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static int getTypeSize(char type) {
|
||||
switch (type) {
|
||||
case 'J':
|
||||
case 'D':
|
||||
return fieldOffset + 8;
|
||||
default:
|
||||
return fieldOffset + 4;
|
||||
return 8;
|
||||
case '[':
|
||||
case 'L':
|
||||
case 'I':
|
||||
case 'F':
|
||||
return 4;
|
||||
case 'C':
|
||||
case 'S':
|
||||
return 2;
|
||||
case 'B':
|
||||
case 'Z':
|
||||
return 1;
|
||||
}
|
||||
throw new ExceptionWithContext("Invalid type: %s", type);
|
||||
}
|
||||
|
||||
@Nonnull List<Method> getVtable() {
|
||||
@ -627,8 +808,9 @@ public class ClassProto implements TypeProto {
|
||||
outer: for (Method virtualMethod: methods) {
|
||||
for (int i=0; i<vtable.size(); i++) {
|
||||
Method superMethod = vtable.get(i);
|
||||
if (methodSignaturesMatch(superMethod, virtualMethod)) {
|
||||
if (!classPath.shouldCheckPackagePrivateAccess() || canAccess(superMethod)) {
|
||||
if (MethodUtil.methodSignaturesMatch(superMethod, virtualMethod)) {
|
||||
if (!classPath.shouldCheckPackagePrivateAccess() ||
|
||||
AnalyzedMethodUtil.canAccess(ClassProto.this, superMethod, true, false, false)) {
|
||||
if (replaceExisting) {
|
||||
vtable.set(i, virtualMethod);
|
||||
}
|
||||
@ -640,36 +822,18 @@ public class ClassProto implements TypeProto {
|
||||
vtable.add(virtualMethod);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean methodSignaturesMatch(@Nonnull Method a, @Nonnull Method b) {
|
||||
return (a.getName().equals(b.getName()) &&
|
||||
a.getReturnType().equals(b.getReturnType()) &&
|
||||
a.getParameters().equals(b.getParameters()));
|
||||
}
|
||||
|
||||
private boolean canAccess(@Nonnull Method virtualMethod) {
|
||||
if (!methodIsPackagePrivate(virtualMethod.getAccessFlags())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
String otherPackage = getPackage(virtualMethod.getDefiningClass());
|
||||
String ourPackage = getPackage(getClassDef().getType());
|
||||
return otherPackage.equals(ourPackage);
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private String getPackage(@Nonnull String classType) {
|
||||
int lastSlash = classType.lastIndexOf('/');
|
||||
if (lastSlash < 0) {
|
||||
return "";
|
||||
}
|
||||
return classType.substring(1, lastSlash);
|
||||
}
|
||||
|
||||
private boolean methodIsPackagePrivate(int accessFlags) {
|
||||
return (accessFlags & (AccessFlags.PRIVATE.getValue() |
|
||||
AccessFlags.PROTECTED.getValue() |
|
||||
AccessFlags.PUBLIC.getValue())) == 0;
|
||||
}
|
||||
});
|
||||
|
||||
private static byte getFieldType(@Nonnull FieldReference field) {
|
||||
switch (field.getType().charAt(0)) {
|
||||
case '[':
|
||||
case 'L':
|
||||
return 0; //REFERENCE
|
||||
case 'J':
|
||||
case 'D':
|
||||
return 1; //WIDE
|
||||
default:
|
||||
return 2; //OTHER
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +73,8 @@ public class MethodAnalyzer {
|
||||
@Nonnull private final Method method;
|
||||
@Nonnull private final MethodImplementation methodImpl;
|
||||
|
||||
private final boolean normalizeVirtualMethods;
|
||||
|
||||
private final int paramRegisterCount;
|
||||
|
||||
@Nonnull private final ClassPath classPath;
|
||||
@ -94,9 +96,10 @@ public class MethodAnalyzer {
|
||||
private final AnalyzedInstruction startOfMethod;
|
||||
|
||||
public MethodAnalyzer(@Nonnull ClassPath classPath, @Nonnull Method method,
|
||||
@Nullable InlineMethodResolver inlineResolver) {
|
||||
@Nullable InlineMethodResolver inlineResolver, boolean normalizeVirtualMethods) {
|
||||
this.classPath = classPath;
|
||||
this.inlineResolver = inlineResolver;
|
||||
this.normalizeVirtualMethods = normalizeVirtualMethods;
|
||||
|
||||
this.method = method;
|
||||
|
||||
@ -252,7 +255,7 @@ public class MethodAnalyzer {
|
||||
int objectRegisterNumber;
|
||||
switch (instruction.getOpcode().format) {
|
||||
case Format10x:
|
||||
analyzeReturnVoidBarrier(analyzedInstruction, false);
|
||||
analyzeOdexReturnVoid(analyzedInstruction, false);
|
||||
continue;
|
||||
case Format21c:
|
||||
case Format22c:
|
||||
@ -580,7 +583,8 @@ public class MethodAnalyzer {
|
||||
case RETURN_OBJECT:
|
||||
return true;
|
||||
case RETURN_VOID_BARRIER:
|
||||
analyzeReturnVoidBarrier(analyzedInstruction);
|
||||
case RETURN_VOID_NO_BARRIER:
|
||||
analyzeOdexReturnVoid(analyzedInstruction);
|
||||
return true;
|
||||
case CONST_4:
|
||||
case CONST_16:
|
||||
@ -736,21 +740,32 @@ public class MethodAnalyzer {
|
||||
case SPUT_OBJECT:
|
||||
return true;
|
||||
case INVOKE_VIRTUAL:
|
||||
analyzeInvokeVirtual(analyzedInstruction, false);
|
||||
return true;
|
||||
case INVOKE_SUPER:
|
||||
analyzeInvokeVirtual(analyzedInstruction, false);
|
||||
return true;
|
||||
case INVOKE_DIRECT:
|
||||
analyzeInvokeDirect(analyzedInstruction);
|
||||
return true;
|
||||
case INVOKE_STATIC:
|
||||
return true;
|
||||
case INVOKE_INTERFACE:
|
||||
// TODO: normalize interfaces
|
||||
return true;
|
||||
case INVOKE_VIRTUAL_RANGE:
|
||||
analyzeInvokeVirtual(analyzedInstruction, true);
|
||||
return true;
|
||||
case INVOKE_SUPER_RANGE:
|
||||
analyzeInvokeVirtual(analyzedInstruction, true);
|
||||
return true;
|
||||
case INVOKE_DIRECT_RANGE:
|
||||
analyzeInvokeDirectRange(analyzedInstruction);
|
||||
return true;
|
||||
case INVOKE_STATIC_RANGE:
|
||||
return true;
|
||||
case INVOKE_INTERFACE_RANGE:
|
||||
// TODO: normalize interfaces
|
||||
return true;
|
||||
case NEG_INT:
|
||||
case NOT_INT:
|
||||
@ -957,6 +972,14 @@ public class MethodAnalyzer {
|
||||
case IPUT_QUICK:
|
||||
case IPUT_WIDE_QUICK:
|
||||
case IPUT_OBJECT_QUICK:
|
||||
case IPUT_BOOLEAN_QUICK:
|
||||
case IPUT_BYTE_QUICK:
|
||||
case IPUT_CHAR_QUICK:
|
||||
case IPUT_SHORT_QUICK:
|
||||
case IGET_BOOLEAN_QUICK:
|
||||
case IGET_BYTE_QUICK:
|
||||
case IGET_CHAR_QUICK:
|
||||
case IGET_SHORT_QUICK:
|
||||
return analyzeIputIgetQuick(analyzedInstruction);
|
||||
case INVOKE_VIRTUAL_QUICK:
|
||||
return analyzeInvokeVirtualQuick(analyzedInstruction, false, false);
|
||||
@ -1063,11 +1086,11 @@ public class MethodAnalyzer {
|
||||
setDestinationRegisterTypeAndPropagateChanges(analyzedInstruction, exceptionType);
|
||||
}
|
||||
|
||||
private void analyzeReturnVoidBarrier(AnalyzedInstruction analyzedInstruction) {
|
||||
analyzeReturnVoidBarrier(analyzedInstruction, true);
|
||||
private void analyzeOdexReturnVoid(AnalyzedInstruction analyzedInstruction) {
|
||||
analyzeOdexReturnVoid(analyzedInstruction, true);
|
||||
}
|
||||
|
||||
private void analyzeReturnVoidBarrier(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
|
||||
private void analyzeOdexReturnVoid(@Nonnull AnalyzedInstruction analyzedInstruction, boolean analyzeResult) {
|
||||
Instruction10x deodexedInstruction = new ImmutableInstruction10x(Opcode.RETURN_VOID);
|
||||
|
||||
analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
|
||||
@ -1538,12 +1561,12 @@ public class MethodAnalyzer {
|
||||
|
||||
ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
|
||||
|
||||
if (!canAccessClass(thisClass, classPath.getClassDef(resolvedField.getDefiningClass()))) {
|
||||
if (!TypeUtils.canAccessClass(thisClass.getType(), classPath.getClassDef(resolvedField.getDefiningClass()))) {
|
||||
|
||||
// the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
|
||||
// than resolvedField.getDefiningClass()), and walk up the class hierarchy.
|
||||
ClassDef fieldClass = classPath.getClassDef(objectRegisterTypeProto.getType());
|
||||
while (!canAccessClass(thisClass, fieldClass)) {
|
||||
while (!TypeUtils.canAccessClass(thisClass.getType(), fieldClass)) {
|
||||
String superclass = fieldClass.getSuperclass();
|
||||
if (superclass == null) {
|
||||
throw new ExceptionWithContext("Couldn't find accessible class while resolving field %s",
|
||||
@ -1566,8 +1589,8 @@ public class MethodAnalyzer {
|
||||
|
||||
String fieldType = resolvedField.getType();
|
||||
|
||||
Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
|
||||
instruction.getOpcode());
|
||||
Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
|
||||
fieldType, instruction.getOpcode());
|
||||
|
||||
Instruction22c deodexedInstruction = new ImmutableInstruction22c(opcode, (byte)instruction.getRegisterA(),
|
||||
(byte)instruction.getRegisterB(), resolvedField);
|
||||
@ -1578,6 +1601,75 @@ public class MethodAnalyzer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean analyzeInvokeVirtual(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isRange) {
|
||||
MethodReference targetMethod;
|
||||
|
||||
if (!normalizeVirtualMethods) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (isRange) {
|
||||
Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
|
||||
targetMethod = (MethodReference)instruction.getReference();
|
||||
} else {
|
||||
Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
|
||||
targetMethod = (MethodReference)instruction.getReference();
|
||||
}
|
||||
|
||||
TypeProto typeProto = classPath.getClass(targetMethod.getDefiningClass());
|
||||
int methodIndex;
|
||||
try {
|
||||
methodIndex = typeProto.findMethodIndexInVtable(targetMethod);
|
||||
} catch (UnresolvedClassException ex) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (methodIndex < 0) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Method replacementMethod = typeProto.getMethodByVtableIndex(methodIndex);
|
||||
assert replacementMethod != null;
|
||||
while (true) {
|
||||
String superType = typeProto.getSuperclass();
|
||||
if (superType == null) {
|
||||
break;
|
||||
}
|
||||
typeProto = classPath.getClass(superType);
|
||||
Method resolvedMethod = typeProto.getMethodByVtableIndex(methodIndex);
|
||||
if (resolvedMethod == null) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!resolvedMethod.equals(replacementMethod)) {
|
||||
if (!AnalyzedMethodUtil.canAccess(typeProto, replacementMethod, true, true, true)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
replacementMethod = resolvedMethod;
|
||||
}
|
||||
}
|
||||
|
||||
if (replacementMethod.equals(method)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
Instruction deodexedInstruction;
|
||||
if (isRange) {
|
||||
Instruction3rc instruction = (Instruction3rc)analyzedInstruction.instruction;
|
||||
deodexedInstruction = new ImmutableInstruction3rc(instruction.getOpcode(), instruction.getStartRegister(),
|
||||
instruction.getRegisterCount(), replacementMethod);
|
||||
} else {
|
||||
Instruction35c instruction = (Instruction35c)analyzedInstruction.instruction;
|
||||
deodexedInstruction = new ImmutableInstruction35c(instruction.getOpcode(), instruction.getRegisterCount(),
|
||||
instruction.getRegisterC(), instruction.getRegisterD(), instruction.getRegisterE(),
|
||||
instruction.getRegisterF(), instruction.getRegisterG(), replacementMethod);
|
||||
}
|
||||
|
||||
analyzedInstruction.setDeodexedInstruction(deodexedInstruction);
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean analyzeInvokeVirtualQuick(@Nonnull AnalyzedInstruction analyzedInstruction, boolean isSuper,
|
||||
boolean isRange) {
|
||||
int methodIndex;
|
||||
@ -1630,12 +1722,13 @@ public class MethodAnalyzer {
|
||||
// no need to check class access for invoke-super. A class can obviously access its superclass.
|
||||
ClassDef thisClass = classPath.getClassDef(method.getDefiningClass());
|
||||
|
||||
if (!isSuper && !canAccessClass(thisClass, classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
|
||||
if (!isSuper && !TypeUtils.canAccessClass(
|
||||
thisClass.getType(), classPath.getClassDef(resolvedMethod.getDefiningClass()))) {
|
||||
|
||||
// the class is not accessible. So we start looking at objectRegisterTypeProto (which may be different
|
||||
// than resolvedMethod.getDefiningClass()), and walk up the class hierarchy.
|
||||
ClassDef methodClass = classPath.getClassDef(objectRegisterTypeProto.getType());
|
||||
while (!canAccessClass(thisClass, methodClass)) {
|
||||
while (!TypeUtils.canAccessClass(thisClass.getType(), methodClass)) {
|
||||
String superclass = methodClass.getSuperclass();
|
||||
if (superclass == null) {
|
||||
throw new ExceptionWithContext("Couldn't find accessible class while resolving method %s",
|
||||
@ -1691,24 +1784,6 @@ public class MethodAnalyzer {
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean canAccessClass(@Nonnull ClassDef accessorClassDef, @Nonnull ClassDef accesseeClassDef) {
|
||||
if (AccessFlags.PUBLIC.isSet(accesseeClassDef.getAccessFlags())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Classes can only be public or package private. Any private or protected inner classes are actually
|
||||
// package private.
|
||||
return getPackage(accesseeClassDef.getType()).equals(getPackage(accessorClassDef.getType()));
|
||||
}
|
||||
|
||||
private static String getPackage(String className) {
|
||||
int lastSlash = className.lastIndexOf('/');
|
||||
if (lastSlash < 0) {
|
||||
return "";
|
||||
}
|
||||
return className.substring(1, lastSlash);
|
||||
}
|
||||
|
||||
private boolean analyzePutGetVolatile(@Nonnull AnalyzedInstruction analyzedInstruction) {
|
||||
return analyzePutGetVolatile(analyzedInstruction, true);
|
||||
}
|
||||
@ -1719,8 +1794,8 @@ public class MethodAnalyzer {
|
||||
|
||||
Opcode originalOpcode = analyzedInstruction.instruction.getOpcode();
|
||||
|
||||
Opcode opcode = OdexedFieldInstructionMapper.getAndCheckDeodexedOpcodeForOdexedOpcode(fieldType,
|
||||
originalOpcode);
|
||||
Opcode opcode = classPath.getFieldInstructionMapper().getAndCheckDeodexedOpcode(
|
||||
fieldType, originalOpcode);
|
||||
|
||||
Instruction deodexedInstruction;
|
||||
|
||||
|
@ -34,155 +34,111 @@ package org.jf.dexlib2.analysis;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class OdexedFieldInstructionMapper {
|
||||
private static Opcode[][][][] opcodeMap = new Opcode[][][][] {
|
||||
//get opcodes
|
||||
new Opcode[][][] {
|
||||
//iget quick
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IGET_QUICK,
|
||||
/*B*/ Opcode.IGET_QUICK,
|
||||
/*S*/ Opcode.IGET_QUICK,
|
||||
/*C*/ Opcode.IGET_QUICK,
|
||||
/*I,F*/ Opcode.IGET_QUICK,
|
||||
/*J,D*/ Opcode.IGET_WIDE_QUICK,
|
||||
/*L,[*/ Opcode.IGET_OBJECT_QUICK
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IGET_BOOLEAN,
|
||||
/*B*/ Opcode.IGET_BYTE,
|
||||
/*S*/ Opcode.IGET_SHORT,
|
||||
/*C*/ Opcode.IGET_CHAR,
|
||||
/*I,F*/ Opcode.IGET,
|
||||
/*J,D*/ Opcode.IGET_WIDE,
|
||||
/*L,[*/ Opcode.IGET_OBJECT
|
||||
}
|
||||
},
|
||||
//iget volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IGET_VOLATILE,
|
||||
/*B*/ Opcode.IGET_VOLATILE,
|
||||
/*S*/ Opcode.IGET_VOLATILE,
|
||||
/*C*/ Opcode.IGET_VOLATILE,
|
||||
/*I,F*/ Opcode.IGET_VOLATILE,
|
||||
/*J,D*/ Opcode.IGET_WIDE_VOLATILE,
|
||||
/*L,[*/ Opcode.IGET_OBJECT_VOLATILE
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IGET_BOOLEAN,
|
||||
/*B*/ Opcode.IGET_BYTE,
|
||||
/*S*/ Opcode.IGET_SHORT,
|
||||
/*C*/ Opcode.IGET_CHAR,
|
||||
/*I,F*/ Opcode.IGET,
|
||||
/*J,D*/ Opcode.IGET_WIDE,
|
||||
/*L,[*/ Opcode.IGET_OBJECT
|
||||
}
|
||||
},
|
||||
//sget volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SGET_VOLATILE,
|
||||
/*B*/ Opcode.SGET_VOLATILE,
|
||||
/*S*/ Opcode.SGET_VOLATILE,
|
||||
/*C*/ Opcode.SGET_VOLATILE,
|
||||
/*I,F*/ Opcode.SGET_VOLATILE,
|
||||
/*J,D*/ Opcode.SGET_WIDE_VOLATILE,
|
||||
/*L,[*/ Opcode.SGET_OBJECT_VOLATILE
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SGET_BOOLEAN,
|
||||
/*B*/ Opcode.SGET_BYTE,
|
||||
/*S*/ Opcode.SGET_SHORT,
|
||||
/*C*/ Opcode.SGET_CHAR,
|
||||
/*I,F*/ Opcode.SGET,
|
||||
/*J,D*/ Opcode.SGET_WIDE,
|
||||
/*L,[*/ Opcode.SGET_OBJECT
|
||||
}
|
||||
}
|
||||
},
|
||||
//put opcodes
|
||||
new Opcode[][][] {
|
||||
//iput quick
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IPUT_QUICK,
|
||||
/*B*/ Opcode.IPUT_QUICK,
|
||||
/*S*/ Opcode.IPUT_QUICK,
|
||||
/*C*/ Opcode.IPUT_QUICK,
|
||||
/*I,F*/ Opcode.IPUT_QUICK,
|
||||
/*J,D*/ Opcode.IPUT_WIDE_QUICK,
|
||||
/*L,[*/ Opcode.IPUT_OBJECT_QUICK
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IPUT_BOOLEAN,
|
||||
/*B*/ Opcode.IPUT_BYTE,
|
||||
/*S*/ Opcode.IPUT_SHORT,
|
||||
/*C*/ Opcode.IPUT_CHAR,
|
||||
/*I,F*/ Opcode.IPUT,
|
||||
/*J,D*/ Opcode.IPUT_WIDE,
|
||||
/*L,[*/ Opcode.IPUT_OBJECT
|
||||
}
|
||||
},
|
||||
//iput volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IPUT_VOLATILE,
|
||||
/*B*/ Opcode.IPUT_VOLATILE,
|
||||
/*S*/ Opcode.IPUT_VOLATILE,
|
||||
/*C*/ Opcode.IPUT_VOLATILE,
|
||||
/*I,F*/ Opcode.IPUT_VOLATILE,
|
||||
/*J,D*/ Opcode.IPUT_WIDE_VOLATILE,
|
||||
/*L,[*/ Opcode.IPUT_OBJECT_VOLATILE
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.IPUT_BOOLEAN,
|
||||
/*B*/ Opcode.IPUT_BYTE,
|
||||
/*S*/ Opcode.IPUT_SHORT,
|
||||
/*C*/ Opcode.IPUT_CHAR,
|
||||
/*I,F*/ Opcode.IPUT,
|
||||
/*J,D*/ Opcode.IPUT_WIDE,
|
||||
/*L,[*/ Opcode.IPUT_OBJECT
|
||||
}
|
||||
},
|
||||
//sput volatile
|
||||
new Opcode[][] {
|
||||
//odexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SPUT_VOLATILE,
|
||||
/*B*/ Opcode.SPUT_VOLATILE,
|
||||
/*S*/ Opcode.SPUT_VOLATILE,
|
||||
/*C*/ Opcode.SPUT_VOLATILE,
|
||||
/*I,F*/ Opcode.SPUT_VOLATILE,
|
||||
/*J,D*/ Opcode.SPUT_WIDE_VOLATILE,
|
||||
/*L,[*/ Opcode.SPUT_OBJECT_VOLATILE
|
||||
},
|
||||
//deodexed
|
||||
new Opcode[] {
|
||||
/*Z*/ Opcode.SPUT_BOOLEAN,
|
||||
/*B*/ Opcode.SPUT_BYTE,
|
||||
/*S*/ Opcode.SPUT_SHORT,
|
||||
/*C*/ Opcode.SPUT_CHAR,
|
||||
/*I,F*/ Opcode.SPUT,
|
||||
/*J,D*/ Opcode.SPUT_WIDE,
|
||||
/*L,[*/ Opcode.SPUT_OBJECT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final int GET = 0;
|
||||
private static final int PUT = 1;
|
||||
|
||||
private static final int PRIMITIVE = 0;
|
||||
private static final int WIDE = 1;
|
||||
private static final int REFERENCE = 2;
|
||||
|
||||
private static class FieldOpcode {
|
||||
public final char type;
|
||||
@Nonnull public final Opcode normalOpcode;
|
||||
@Nonnull public final Opcode quickOpcode;
|
||||
@Nullable public final Opcode volatileOpcode;
|
||||
|
||||
public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode,
|
||||
@Nullable Opcode volatileOpcode) {
|
||||
this.type = type;
|
||||
this.normalOpcode = normalOpcode;
|
||||
this.quickOpcode = quickOpcode;
|
||||
this.volatileOpcode = volatileOpcode;
|
||||
}
|
||||
|
||||
public FieldOpcode(char type, @Nonnull Opcode normalOpcode, @Nonnull Opcode quickOpcode) {
|
||||
this.type = type;
|
||||
this.normalOpcode = normalOpcode;
|
||||
this.quickOpcode = quickOpcode;
|
||||
this.volatileOpcode = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static final FieldOpcode[] dalvikFieldOpcodes = new FieldOpcode[] {
|
||||
new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||
new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||
new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||
new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||
new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||
new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK, Opcode.IGET_VOLATILE),
|
||||
new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
|
||||
new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK, Opcode.IGET_WIDE_VOLATILE),
|
||||
new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
|
||||
new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK, Opcode.IGET_OBJECT_VOLATILE),
|
||||
|
||||
new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||
new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||
new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||
new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||
new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||
new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK, Opcode.IPUT_VOLATILE),
|
||||
new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
|
||||
new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK, Opcode.IPUT_WIDE_VOLATILE),
|
||||
new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
|
||||
new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK, Opcode.IPUT_OBJECT_VOLATILE),
|
||||
};
|
||||
|
||||
private static final FieldOpcode[] artFieldOpcodes = new FieldOpcode[] {
|
||||
new FieldOpcode('Z', Opcode.IGET_BOOLEAN, Opcode.IGET_BOOLEAN_QUICK),
|
||||
new FieldOpcode('B', Opcode.IGET_BYTE, Opcode.IGET_BYTE_QUICK),
|
||||
new FieldOpcode('S', Opcode.IGET_SHORT, Opcode.IGET_SHORT_QUICK),
|
||||
new FieldOpcode('C', Opcode.IGET_CHAR, Opcode.IGET_CHAR_QUICK),
|
||||
new FieldOpcode('I', Opcode.IGET, Opcode.IGET_QUICK),
|
||||
new FieldOpcode('F', Opcode.IGET, Opcode.IGET_QUICK),
|
||||
new FieldOpcode('J', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
|
||||
new FieldOpcode('D', Opcode.IGET_WIDE, Opcode.IGET_WIDE_QUICK),
|
||||
new FieldOpcode('L', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
|
||||
new FieldOpcode('[', Opcode.IGET_OBJECT, Opcode.IGET_OBJECT_QUICK),
|
||||
|
||||
new FieldOpcode('Z', Opcode.IPUT_BOOLEAN, Opcode.IPUT_BOOLEAN_QUICK),
|
||||
new FieldOpcode('B', Opcode.IPUT_BYTE, Opcode.IPUT_BYTE_QUICK),
|
||||
new FieldOpcode('S', Opcode.IPUT_SHORT, Opcode.IPUT_SHORT_QUICK),
|
||||
new FieldOpcode('C', Opcode.IPUT_CHAR, Opcode.IPUT_CHAR_QUICK),
|
||||
new FieldOpcode('I', Opcode.IPUT, Opcode.IPUT_QUICK),
|
||||
new FieldOpcode('F', Opcode.IPUT, Opcode.IPUT_QUICK),
|
||||
new FieldOpcode('J', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
|
||||
new FieldOpcode('D', Opcode.IPUT_WIDE, Opcode.IPUT_WIDE_QUICK),
|
||||
new FieldOpcode('L', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK),
|
||||
new FieldOpcode('[', Opcode.IPUT_OBJECT, Opcode.IPUT_OBJECT_QUICK)
|
||||
};
|
||||
|
||||
private final FieldOpcode[][] opcodeMap = new FieldOpcode[2][10];
|
||||
private final Map<Opcode, Integer> opcodeValueTypeMap = new HashMap<Opcode, Integer>(30);
|
||||
|
||||
private static int getValueType(char type) {
|
||||
switch (type) {
|
||||
case 'Z':
|
||||
case 'B':
|
||||
case 'S':
|
||||
case 'C':
|
||||
case 'I':
|
||||
case 'F':
|
||||
return PRIMITIVE;
|
||||
case 'J':
|
||||
case 'D':
|
||||
return WIDE;
|
||||
case 'L':
|
||||
case '[':
|
||||
return REFERENCE;
|
||||
}
|
||||
throw new RuntimeException(String.format("Unknown type %s: ", type));
|
||||
}
|
||||
|
||||
private static int getTypeIndex(char type) {
|
||||
switch (type) {
|
||||
case 'Z':
|
||||
@ -194,47 +150,61 @@ public class OdexedFieldInstructionMapper {
|
||||
case 'C':
|
||||
return 3;
|
||||
case 'I':
|
||||
case 'F':
|
||||
return 4;
|
||||
case 'J':
|
||||
case 'D':
|
||||
case 'F':
|
||||
return 5;
|
||||
case 'L':
|
||||
case '[':
|
||||
case 'J':
|
||||
return 6;
|
||||
default:
|
||||
case 'D':
|
||||
return 7;
|
||||
case 'L':
|
||||
return 8;
|
||||
case '[':
|
||||
return 9;
|
||||
}
|
||||
throw new RuntimeException(String.format("Unknown type %s: ", type));
|
||||
}
|
||||
|
||||
private static int getOpcodeSubtype(@Nonnull Opcode opcode) {
|
||||
if (opcode.isOdexedInstanceQuick()) {
|
||||
return 0;
|
||||
} else if (opcode.isOdexedInstanceVolatile()) {
|
||||
return 1;
|
||||
} else if (opcode.isOdexedStaticVolatile()) {
|
||||
return 2;
|
||||
private static boolean isGet(@Nonnull Opcode opcode) {
|
||||
return (opcode.flags & Opcode.SETS_REGISTER) != 0;
|
||||
}
|
||||
|
||||
public OdexedFieldInstructionMapper(boolean isArt) {
|
||||
FieldOpcode[] opcodes;
|
||||
if (isArt) {
|
||||
opcodes = artFieldOpcodes;
|
||||
} else {
|
||||
opcodes = dalvikFieldOpcodes;
|
||||
}
|
||||
|
||||
for (FieldOpcode fieldOpcode: opcodes) {
|
||||
opcodeMap[isGet(fieldOpcode.normalOpcode)?GET:PUT][getTypeIndex(fieldOpcode.type)] = fieldOpcode;
|
||||
|
||||
opcodeValueTypeMap.put(fieldOpcode.quickOpcode, getValueType(fieldOpcode.type));
|
||||
if (fieldOpcode.volatileOpcode != null) {
|
||||
opcodeValueTypeMap.put(fieldOpcode.volatileOpcode, getValueType(fieldOpcode.type));
|
||||
}
|
||||
}
|
||||
throw new RuntimeException(String.format("Not an odexed field access opcode: %s", opcode.name));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
static Opcode getAndCheckDeodexedOpcodeForOdexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
|
||||
int opcodeType = odexedOpcode.setsRegister()?0:1;
|
||||
int opcodeSubType = getOpcodeSubtype(odexedOpcode);
|
||||
int typeIndex = getTypeIndex(fieldType.charAt(0));
|
||||
public Opcode getAndCheckDeodexedOpcode(@Nonnull String fieldType, @Nonnull Opcode odexedOpcode) {
|
||||
FieldOpcode fieldOpcode = opcodeMap[isGet(odexedOpcode)?GET:PUT][getTypeIndex(fieldType.charAt(0))];
|
||||
|
||||
Opcode correctOdexedOpcode, deodexedOpcode;
|
||||
|
||||
correctOdexedOpcode = opcodeMap[opcodeType][opcodeSubType][0][typeIndex];
|
||||
deodexedOpcode = opcodeMap[opcodeType][opcodeSubType][1][typeIndex];
|
||||
|
||||
if (correctOdexedOpcode != odexedOpcode) {
|
||||
if (!isCompatible(odexedOpcode, fieldOpcode.type)) {
|
||||
throw new AnalysisException(String.format("Incorrect field type \"%s\" for %s", fieldType,
|
||||
odexedOpcode.name));
|
||||
}
|
||||
|
||||
return deodexedOpcode;
|
||||
return fieldOpcode.normalOpcode;
|
||||
}
|
||||
|
||||
private boolean isCompatible(Opcode opcode, char type) {
|
||||
Integer valueType = opcodeValueTypeMap.get(opcode);
|
||||
if (valueType == null) {
|
||||
throw new RuntimeException("Unexpected opcode: " + opcode.name);
|
||||
}
|
||||
return valueType == getValueType(type);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package org.jf.dexlib2.analysis;
|
||||
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.util.ExceptionWithContext;
|
||||
@ -65,7 +66,11 @@ public class PrimitiveProto implements TypeProto {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MethodReference getMethodByVtableIndex(int vtableIndex) {
|
||||
public Method getMethodByVtableIndex(int vtableIndex) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package org.jf.dexlib2.analysis;
|
||||
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
|
||||
@ -45,5 +46,6 @@ public interface TypeProto {
|
||||
@Nullable String getSuperclass();
|
||||
@Nonnull TypeProto getCommonSuperclass(@Nonnull TypeProto other);
|
||||
@Nullable FieldReference getFieldByOffset(int fieldOffset);
|
||||
@Nullable MethodReference getMethodByVtableIndex(int vtableIndex);
|
||||
@Nullable Method getMethodByVtableIndex(int vtableIndex);
|
||||
int findMethodIndexInVtable(@Nonnull MethodReference method);
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package org.jf.dexlib2.analysis;
|
||||
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.reference.FieldReference;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
|
||||
@ -75,7 +76,11 @@ public class UnknownClassProto implements TypeProto {
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MethodReference getMethodByVtableIndex(int vtableIndex) {
|
||||
public Method getMethodByVtableIndex(int vtableIndex) {
|
||||
return classPath.getClass("Ljava/lang/Object;").getMethodByVtableIndex(vtableIndex);
|
||||
}
|
||||
|
||||
@Override public int findMethodIndexInVtable(@Nonnull MethodReference method) {
|
||||
return classPath.getClass("Ljava/lang/Object;").findMethodIndexInVtable(method);
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,7 @@ package org.jf.dexlib2.analysis.reflection;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterators;
|
||||
import org.jf.dexlib2.analysis.reflection.util.ReflectionUtils;
|
||||
@ -48,6 +49,7 @@ import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Modifier;
|
||||
import java.util.AbstractSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -78,23 +80,17 @@ public class ReflectionClassDef extends BaseTypeReference implements ClassDef {
|
||||
return ReflectionUtils.javaToDexName(superClass.getName());
|
||||
}
|
||||
|
||||
@Nonnull @Override public Set<String> getInterfaces() {
|
||||
return new AbstractSet<String>() {
|
||||
@Nonnull @Override public Iterator<String> iterator() {
|
||||
return Iterators.transform(Iterators.forArray(cls.getInterfaces()), new Function<Class, String>() {
|
||||
@Nullable @Override public String apply(@Nullable Class input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
return ReflectionUtils.javaToDexName(input.getName());
|
||||
}
|
||||
});
|
||||
@Nonnull @Override public List<String> getInterfaces() {
|
||||
return ImmutableList.copyOf(Iterators.transform(Iterators.forArray(cls.getInterfaces()), new Function<Class, String>() {
|
||||
@Nullable
|
||||
@Override
|
||||
public String apply(@Nullable Class input) {
|
||||
if (input == null) {
|
||||
return null;
|
||||
}
|
||||
return ReflectionUtils.javaToDexName(input.getName());
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
return cls.getInterfaces().length;
|
||||
}
|
||||
};
|
||||
}));
|
||||
}
|
||||
|
||||
@Nullable @Override public String getSourceFile() {
|
||||
|
@ -94,4 +94,16 @@ public class TypeProtoUtils {
|
||||
return type.getClassPath().getUnknownClass();
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean extendsFrom(@Nonnull TypeProto candidate, @Nonnull String possibleSuper) {
|
||||
if (candidate.getType().equals(possibleSuper)) {
|
||||
return true;
|
||||
}
|
||||
for (TypeProto superProto: getSuperclassChain(candidate)) {
|
||||
if (superProto.getType().equals(possibleSuper)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -37,13 +37,19 @@ import javax.annotation.Nonnull;
|
||||
|
||||
public class BaseDexBuffer {
|
||||
@Nonnull /* package private */ final byte[] buf;
|
||||
/* package private */ final int baseOffset;
|
||||
|
||||
public BaseDexBuffer(@Nonnull byte[] buf) {
|
||||
this(buf, 0);
|
||||
}
|
||||
public BaseDexBuffer(@Nonnull byte[] buf, int offset) {
|
||||
this.buf = buf;
|
||||
this.baseOffset = offset;
|
||||
}
|
||||
|
||||
public int readSmallUint(int offset) {
|
||||
byte[] buf = this.buf;
|
||||
offset += baseOffset;
|
||||
int result = (buf[offset] & 0xff) |
|
||||
((buf[offset+1] & 0xff) << 8) |
|
||||
((buf[offset+2] & 0xff) << 16) |
|
||||
@ -56,6 +62,7 @@ public class BaseDexBuffer {
|
||||
|
||||
public int readOptionalUint(int offset) {
|
||||
byte[] buf = this.buf;
|
||||
offset += baseOffset;
|
||||
int result = (buf[offset] & 0xff) |
|
||||
((buf[offset+1] & 0xff) << 8) |
|
||||
((buf[offset+2] & 0xff) << 16) |
|
||||
@ -68,16 +75,18 @@ public class BaseDexBuffer {
|
||||
|
||||
public int readUshort(int offset) {
|
||||
byte[] buf = this.buf;
|
||||
offset += baseOffset;
|
||||
return (buf[offset] & 0xff) |
|
||||
((buf[offset+1] & 0xff) << 8);
|
||||
}
|
||||
|
||||
public int readUbyte(int offset) {
|
||||
return buf[offset] & 0xff;
|
||||
return buf[offset + baseOffset] & 0xff;
|
||||
}
|
||||
|
||||
public long readLong(int offset) {
|
||||
byte[] buf = this.buf;
|
||||
offset += baseOffset;
|
||||
return (buf[offset] & 0xff) |
|
||||
((buf[offset+1] & 0xff) << 8) |
|
||||
((buf[offset+2] & 0xff) << 16) |
|
||||
@ -90,6 +99,7 @@ public class BaseDexBuffer {
|
||||
|
||||
public int readInt(int offset) {
|
||||
byte[] buf = this.buf;
|
||||
offset += baseOffset;
|
||||
return (buf[offset] & 0xff) |
|
||||
((buf[offset+1] & 0xff) << 8) |
|
||||
((buf[offset+2] & 0xff) << 16) |
|
||||
@ -98,12 +108,13 @@ public class BaseDexBuffer {
|
||||
|
||||
public int readShort(int offset) {
|
||||
byte[] buf = this.buf;
|
||||
offset += baseOffset;
|
||||
return (buf[offset] & 0xff) |
|
||||
(buf[offset+1] << 8);
|
||||
}
|
||||
|
||||
public int readByte(int offset) {
|
||||
return buf[offset];
|
||||
return buf[baseOffset + offset];
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@ -115,4 +126,8 @@ public class BaseDexBuffer {
|
||||
protected byte[] getBuf() {
|
||||
return buf;
|
||||
}
|
||||
|
||||
protected int getBaseOffset() {
|
||||
return baseOffset;
|
||||
}
|
||||
}
|
||||
|
@ -49,7 +49,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
public void setOffset(int offset) { this.offset = offset; }
|
||||
|
||||
public int readSleb128() {
|
||||
int end = offset;
|
||||
int end = dexBuf.baseOffset + offset;
|
||||
int currentByteValue;
|
||||
int result;
|
||||
byte[] buf = dexBuf.buf;
|
||||
@ -84,7 +84,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
}
|
||||
}
|
||||
|
||||
offset = end;
|
||||
offset = end - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -93,7 +93,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
}
|
||||
|
||||
private int readUleb128(boolean allowLarge) {
|
||||
int end = offset;
|
||||
int end = dexBuf.baseOffset + offset;
|
||||
int currentByteValue;
|
||||
int result;
|
||||
byte[] buf = dexBuf.buf;
|
||||
@ -129,7 +129,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
}
|
||||
}
|
||||
|
||||
offset = end;
|
||||
offset = end - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -150,7 +150,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
* @return The unsigned value, reinterpreted as a signed int
|
||||
*/
|
||||
public int readBigUleb128() {
|
||||
int end = offset;
|
||||
int end = dexBuf.baseOffset + offset;
|
||||
int currentByteValue;
|
||||
int result;
|
||||
byte[] buf = dexBuf.buf;
|
||||
@ -179,12 +179,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
}
|
||||
}
|
||||
|
||||
offset = end;
|
||||
offset = end - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
public void skipUleb128() {
|
||||
int end = offset;
|
||||
int end = dexBuf.baseOffset + offset;
|
||||
byte currentByteValue;
|
||||
byte[] buf = dexBuf.buf;
|
||||
|
||||
@ -206,7 +206,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
}
|
||||
}
|
||||
|
||||
offset = end;
|
||||
offset = end - dexBuf.baseOffset;
|
||||
}
|
||||
|
||||
public int readSmallUint() {
|
||||
@ -285,7 +285,7 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
public int readByte(int offset) { return dexBuf.readByte(offset); }
|
||||
|
||||
public int readSizedInt(int bytes) {
|
||||
int o = offset;
|
||||
int o = dexBuf.baseOffset + offset;
|
||||
byte[] buf = dexBuf.buf;
|
||||
|
||||
int result;
|
||||
@ -311,12 +311,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
default:
|
||||
throw new ExceptionWithContext("Invalid size %d for sized int at offset 0x%x", bytes, offset);
|
||||
}
|
||||
offset = o + bytes;
|
||||
offset = o + bytes - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readSizedSmallUint(int bytes) {
|
||||
int o = offset;
|
||||
int o = dexBuf.baseOffset + offset;
|
||||
byte[] buf = dexBuf.buf;
|
||||
|
||||
int result = 0;
|
||||
@ -341,12 +341,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
default:
|
||||
throw new ExceptionWithContext("Invalid size %d for sized uint at offset 0x%x", bytes, offset);
|
||||
}
|
||||
offset = o + bytes;
|
||||
offset = o + bytes - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
public int readSizedRightExtendedInt(int bytes) {
|
||||
int o = offset;
|
||||
int o = dexBuf.baseOffset + offset;
|
||||
byte[] buf = dexBuf.buf;
|
||||
|
||||
int result;
|
||||
@ -373,12 +373,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
throw new ExceptionWithContext(
|
||||
"Invalid size %d for sized, right extended int at offset 0x%x", bytes, offset);
|
||||
}
|
||||
offset = o + bytes;
|
||||
offset = o + bytes - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
public long readSizedRightExtendedLong(int bytes) {
|
||||
int o = offset;
|
||||
int o = dexBuf.baseOffset + offset;
|
||||
byte[] buf = dexBuf.buf;
|
||||
|
||||
long result;
|
||||
@ -439,12 +439,12 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
throw new ExceptionWithContext(
|
||||
"Invalid size %d for sized, right extended long at offset 0x%x", bytes, offset);
|
||||
}
|
||||
offset = o + bytes;
|
||||
offset = o + bytes - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
public long readSizedLong(int bytes) {
|
||||
int o = offset;
|
||||
int o = dexBuf.baseOffset + offset;
|
||||
byte[] buf = dexBuf.buf;
|
||||
|
||||
long result;
|
||||
@ -505,13 +505,14 @@ public class BaseDexReader<T extends BaseDexBuffer> {
|
||||
throw new ExceptionWithContext("Invalid size %d for sized long at offset 0x%x", bytes, offset);
|
||||
}
|
||||
|
||||
offset = o + bytes;
|
||||
offset = o + bytes - dexBuf.baseOffset;
|
||||
return result;
|
||||
}
|
||||
|
||||
public String readString(int utf16Length) {
|
||||
int[] ret = new int[1];
|
||||
String value = Utf8Utils.utf8BytesWithUtf16LengthToString(dexBuf.buf, offset, utf16Length, ret);
|
||||
String value = Utf8Utils.utf8BytesWithUtf16LengthToString(
|
||||
dexBuf.buf, dexBuf.baseOffset + offset, utf16Length, ret);
|
||||
offset += ret[0];
|
||||
return value;
|
||||
}
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package org.jf.dexlib2.dexbacked;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import com.google.common.collect.Iterables;
|
||||
import org.jf.dexlib2.base.reference.BaseTypeReference;
|
||||
@ -47,7 +48,9 @@ import org.jf.dexlib2.immutable.reference.ImmutableMethodReference;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
|
||||
@ -114,21 +117,21 @@ public class DexBackedClassDef extends BaseTypeReference implements ClassDef {
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<String> getInterfaces() {
|
||||
public List<String> getInterfaces() {
|
||||
final int interfacesOffset = dexFile.readSmallUint(classDefOffset + ClassDefItem.INTERFACES_OFFSET);
|
||||
if (interfacesOffset > 0) {
|
||||
final int size = dexFile.readSmallUint(interfacesOffset);
|
||||
return new FixedSizeSet<String>() {
|
||||
@Nonnull
|
||||
return new AbstractList<String>() {
|
||||
@Override
|
||||
public String readItem(int index) {
|
||||
@Nonnull
|
||||
public String get(int index) {
|
||||
return dexFile.getType(dexFile.readUshort(interfacesOffset + 4 + (2*index)));
|
||||
}
|
||||
|
||||
@Override public int size() { return size; }
|
||||
};
|
||||
}
|
||||
return ImmutableSet.of();
|
||||
return ImmutableList.of();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -62,7 +62,7 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
|
||||
private final int classStartOffset;
|
||||
|
||||
private DexBackedDexFile(Opcodes opcodes, @Nonnull byte[] buf, int offset, boolean verifyMagic) {
|
||||
super(buf);
|
||||
super(buf, offset);
|
||||
|
||||
this.opcodes = opcodes;
|
||||
|
||||
@ -121,10 +121,16 @@ public class DexBackedDexFile extends BaseDexBuffer implements DexFile {
|
||||
return opcodes;
|
||||
}
|
||||
|
||||
// Will only be true for a dalvik-style odex file
|
||||
public boolean isOdexFile() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Will be true for both a dalvik-style odex file, and an art-style odex file embedded in an oat file
|
||||
public boolean hasOdexOpcodes() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
@Override
|
||||
public Set<? extends DexBackedClassDef> getClasses() {
|
||||
|
@ -32,7 +32,7 @@
|
||||
package org.jf.dexlib2.dexbacked;
|
||||
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.jf.dexlib2.base.reference.BaseMethodReference;
|
||||
import org.jf.dexlib2.dexbacked.raw.MethodIdItem;
|
||||
import org.jf.dexlib2.dexbacked.raw.ProtoIdItem;
|
||||
@ -152,7 +152,7 @@ public class DexBackedMethod extends BaseMethodReference implements Method {
|
||||
if (methodImpl != null) {
|
||||
return methodImpl.getParameterNames(null);
|
||||
}
|
||||
return Iterators.emptyIterator();
|
||||
return ImmutableSet.<String>of().iterator();
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -123,7 +123,16 @@ public class DexBackedMethodImplementation implements MethodImplementation {
|
||||
|
||||
@Nonnull
|
||||
private DebugInfo getDebugInfo() {
|
||||
return DebugInfo.newOrEmpty(dexFile, dexFile.readSmallUint(codeOffset + CodeItem.DEBUG_INFO_OFFSET), this);
|
||||
int debugOffset = dexFile.readInt(codeOffset + CodeItem.DEBUG_INFO_OFFSET);
|
||||
|
||||
if (debugOffset == -1 || debugOffset == 0) {
|
||||
return DebugInfo.newOrEmpty(dexFile, 0, this);
|
||||
}
|
||||
if (debugOffset < 0) {
|
||||
System.err.println("%s: Invalid debug offset");
|
||||
return DebugInfo.newOrEmpty(dexFile, 0, this);
|
||||
}
|
||||
return DebugInfo.newOrEmpty(dexFile, debugOffset, this);
|
||||
}
|
||||
|
||||
@Nonnull @Override
|
||||
|
@ -33,7 +33,6 @@ package org.jf.dexlib2.dexbacked;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.dexbacked.raw.HeaderItem;
|
||||
import org.jf.dexlib2.dexbacked.raw.OdexHeaderItem;
|
||||
import org.jf.dexlib2.dexbacked.util.VariableSizeList;
|
||||
|
||||
@ -61,6 +60,10 @@ public class DexBackedOdexFile extends DexBackedDexFile {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override public boolean hasOdexOpcodes() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<String> getDependencies() {
|
||||
final int dexOffset = OdexHeaderItem.getDexOffset(odexBuf);
|
||||
final int dependencyOffset = OdexHeaderItem.getDependenciesOffset(odexBuf) - dexOffset;
|
||||
|
470
dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java
Normal file
470
dexlib2/src/main/java/org/jf/dexlib2/dexbacked/OatFile.java
Normal file
@ -0,0 +1,470 @@
|
||||
/*
|
||||
* Copyright 2014, Google Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
* * Redistributions in binary form must reproduce the above
|
||||
* copyright notice, this list of conditions and the following disclaimer
|
||||
* in the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
* * Neither the name of Google Inc. nor the names of its
|
||||
* contributors may be used to endorse or promote products derived from
|
||||
* this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
package org.jf.dexlib2.dexbacked;
|
||||
|
||||
import com.google.common.io.ByteStreams;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.dexbacked.OatFile.SymbolTable.Symbol;
|
||||
import org.jf.dexlib2.dexbacked.raw.HeaderItem;
|
||||
import org.jf.util.AbstractForwardSequentialList;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.io.EOFException;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.nio.charset.Charset;
|
||||
import java.util.AbstractList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class OatFile extends BaseDexBuffer {
|
||||
private static final byte[] ELF_MAGIC = new byte[] { 0x7f, 'E', 'L', 'F' };
|
||||
private static final byte[] OAT_MAGIC = new byte[] { 'o', 'a', 't', '\n' };
|
||||
private static final int 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;
|
||||
|
||||
public static final int UNSUPPORTED = 0;
|
||||
public static final int SUPPORTED = 1;
|
||||
public static final int UNKNOWN = 2;
|
||||
|
||||
@Nonnull private final OatHeader oatHeader;
|
||||
@Nonnull private final Opcodes opcodes;
|
||||
|
||||
public OatFile(@Nonnull byte[] buf) {
|
||||
super(buf);
|
||||
|
||||
if (buf.length < ELF_HEADER_SIZE) {
|
||||
throw new NotAnOatFileException();
|
||||
}
|
||||
|
||||
verifyMagic(buf);
|
||||
|
||||
OatHeader oatHeader = null;
|
||||
SymbolTable symbolTable = getSymbolTable();
|
||||
for (Symbol symbol: symbolTable.getSymbols()) {
|
||||
if (symbol.getName().equals("oatdata")) {
|
||||
oatHeader = new OatHeader(symbol.getFileOffset());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (oatHeader == null) {
|
||||
throw new InvalidOatFileException("Oat file has no oatdata symbol");
|
||||
}
|
||||
this.oatHeader = oatHeader;
|
||||
|
||||
if (!oatHeader.isValid()) {
|
||||
throw new InvalidOatFileException("Invalid oat magic value");
|
||||
}
|
||||
|
||||
this.opcodes = Opcodes.forArtVersion(oatHeader.getVersion());
|
||||
}
|
||||
|
||||
private static void verifyMagic(byte[] buf) {
|
||||
for (int i = 0; i < ELF_MAGIC.length; i++) {
|
||||
if (buf[i] != ELF_MAGIC[i]) {
|
||||
throw new NotAnOatFileException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static OatFile fromInputStream(@Nonnull InputStream is)
|
||||
throws IOException {
|
||||
if (!is.markSupported()) {
|
||||
throw new IllegalArgumentException("InputStream must support mark");
|
||||
}
|
||||
is.mark(4);
|
||||
byte[] partialHeader = new byte[4];
|
||||
try {
|
||||
ByteStreams.readFully(is, partialHeader);
|
||||
} catch (EOFException ex) {
|
||||
throw new NotAnOatFileException();
|
||||
} finally {
|
||||
is.reset();
|
||||
}
|
||||
|
||||
verifyMagic(partialHeader);
|
||||
|
||||
is.reset();
|
||||
|
||||
byte[] buf = ByteStreams.toByteArray(is);
|
||||
return new OatFile(buf);
|
||||
}
|
||||
|
||||
public int getOatVersion() {
|
||||
return oatHeader.getVersion();
|
||||
}
|
||||
|
||||
public int isSupportedVersion() {
|
||||
int version = getOatVersion();
|
||||
if (version < MIN_OAT_VERSION) {
|
||||
return UNSUPPORTED;
|
||||
}
|
||||
if (version <= MAX_OAT_VERSION) {
|
||||
return SUPPORTED;
|
||||
}
|
||||
return UNKNOWN;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<OatDexFile> getDexFiles() {
|
||||
return new AbstractForwardSequentialList<OatDexFile>() {
|
||||
@Override public int size() {
|
||||
return oatHeader.getDexFileCount();
|
||||
}
|
||||
|
||||
@Nonnull @Override public Iterator<OatDexFile> iterator() {
|
||||
return new Iterator<OatDexFile>() {
|
||||
int index = 0;
|
||||
int offset = oatHeader.getDexListStart();
|
||||
|
||||
@Override public boolean hasNext() {
|
||||
return index < size();
|
||||
}
|
||||
|
||||
@Override public OatDexFile next() {
|
||||
int filenameLength = readSmallUint(offset);
|
||||
offset += 4;
|
||||
|
||||
// TODO: what is the correct character encoding?
|
||||
String filename = new String(buf, offset, filenameLength, Charset.forName("US-ASCII"));
|
||||
offset += filenameLength;
|
||||
|
||||
offset += 4; // checksum
|
||||
|
||||
int dexOffset = readSmallUint(offset) + oatHeader.offset;
|
||||
offset += 4;
|
||||
|
||||
int classCount = readSmallUint(dexOffset + HeaderItem.CLASS_COUNT_OFFSET);
|
||||
offset += 4 * classCount;
|
||||
|
||||
index++;
|
||||
|
||||
return new OatDexFile(dexOffset, filename);
|
||||
}
|
||||
|
||||
@Override public void remove() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public class OatDexFile extends DexBackedDexFile {
|
||||
@Nonnull public final String filename;
|
||||
|
||||
public OatDexFile(int offset, @Nonnull String filename) {
|
||||
super(opcodes, OatFile.this.buf, offset);
|
||||
this.filename = filename;
|
||||
}
|
||||
|
||||
@Override public boolean hasOdexOpcodes() {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
private class OatHeader {
|
||||
private final int offset;
|
||||
|
||||
public OatHeader(int offset) {
|
||||
this.offset = offset;
|
||||
}
|
||||
|
||||
public boolean isValid() {
|
||||
for (int i=0; i<OAT_MAGIC.length; i++) {
|
||||
if (buf[offset + i] != OAT_MAGIC[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=4; i<7; i++) {
|
||||
if (buf[offset + i] < '0' || buf[offset + i] > '9') {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return buf[offset + 7] == 0;
|
||||
}
|
||||
|
||||
public int getVersion() {
|
||||
return Integer.valueOf(new String(buf, offset + 4, 3));
|
||||
}
|
||||
|
||||
public int getDexFileCount() {
|
||||
return readSmallUint(offset + 20);
|
||||
}
|
||||
|
||||
public int getKeyValueStoreSize() {
|
||||
int version = getVersion();
|
||||
if (version < 56) {
|
||||
throw new IllegalStateException("Unsupported oat version");
|
||||
}
|
||||
int fieldOffset = 17 * 4;
|
||||
return readSmallUint(offset + fieldOffset);
|
||||
}
|
||||
|
||||
public int getHeaderSize() {
|
||||
int version = getVersion();
|
||||
if (version >= 56) {
|
||||
return 18*4 + getKeyValueStoreSize();
|
||||
} else {
|
||||
throw new IllegalStateException("Unsupported oat version");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public int getDexListStart() {
|
||||
return offset + getHeaderSize();
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private List<SectionHeader> getSections() {
|
||||
final int offset = readSmallUint(32);
|
||||
final int entrySize = readUshort(46);
|
||||
final int entryCount = readUshort(48);
|
||||
|
||||
if (offset + (entrySize * entryCount) > buf.length) {
|
||||
throw new InvalidOatFileException("The ELF section headers extend past the end of the file");
|
||||
}
|
||||
|
||||
return new AbstractList<SectionHeader>() {
|
||||
@Override public SectionHeader get(int index) {
|
||||
if (index < 0 || index >= entryCount) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return new SectionHeader(offset + (index * entrySize));
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
return entryCount;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private SymbolTable getSymbolTable() {
|
||||
for (SectionHeader header: getSections()) {
|
||||
if (header.getType() == SectionHeader.TYPE_DYNAMIC_SYMBOL_TABLE) {
|
||||
return new SymbolTable(header);
|
||||
}
|
||||
}
|
||||
throw new InvalidOatFileException("Oat file has no symbol table");
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private StringTable getSectionNameStringTable() {
|
||||
int index = readUshort(50);
|
||||
if (index == 0) {
|
||||
throw new InvalidOatFileException("There is no section name string table");
|
||||
}
|
||||
|
||||
try {
|
||||
return new StringTable(getSections().get(index));
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new InvalidOatFileException("The section index for the section name string table is invalid");
|
||||
}
|
||||
}
|
||||
|
||||
private class SectionHeader {
|
||||
private 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 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);
|
||||
}
|
||||
|
||||
public int getEntrySize() {
|
||||
return readSmallUint(offset + 36);
|
||||
}
|
||||
}
|
||||
|
||||
class SymbolTable {
|
||||
@Nonnull private final StringTable stringTable;
|
||||
private final int offset;
|
||||
private final int entryCount;
|
||||
private final int entrySize;
|
||||
|
||||
public SymbolTable(@Nonnull SectionHeader header) {
|
||||
try {
|
||||
this.stringTable = new StringTable(getSections().get(header.getLink()));
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new InvalidOatFileException("String table section index is invalid");
|
||||
}
|
||||
this.offset = header.getOffset();
|
||||
this.entrySize = header.getEntrySize();
|
||||
this.entryCount = header.getSize() / entrySize;
|
||||
|
||||
if (offset + entryCount * entrySize > buf.length) {
|
||||
throw new InvalidOatFileException("Symbol table extends past end of file");
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<Symbol> getSymbols() {
|
||||
return new AbstractList<Symbol>() {
|
||||
@Override public Symbol get(int index) {
|
||||
if (index < 0 || index >= entryCount) {
|
||||
throw new IndexOutOfBoundsException();
|
||||
}
|
||||
return new Symbol(offset + index * entrySize);
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
return entryCount;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
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 int getFileOffset() {
|
||||
SectionHeader sectionHeader;
|
||||
try {
|
||||
sectionHeader = getSections().get(getSectionIndex());
|
||||
} catch (IndexOutOfBoundsException ex) {
|
||||
throw new InvalidOatFileException("Section index for symbol is out of bounds");
|
||||
}
|
||||
|
||||
long sectionAddress = sectionHeader.getAddress();
|
||||
int sectionOffset = sectionHeader.getOffset();
|
||||
int sectionSize = sectionHeader.getSize();
|
||||
|
||||
long symbolAddress = getValue();
|
||||
|
||||
if (symbolAddress < sectionAddress || symbolAddress >= sectionAddress + sectionSize) {
|
||||
throw new InvalidOatFileException("symbol address lies outside it's associated section");
|
||||
}
|
||||
|
||||
long fileOffset = (sectionOffset + (getValue() - sectionAddress));
|
||||
assert fileOffset <= Integer.MAX_VALUE;
|
||||
return (int)fileOffset;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class StringTable {
|
||||
private final int offset;
|
||||
private final int size;
|
||||
|
||||
public StringTable(@Nonnull SectionHeader header) {
|
||||
this.offset = header.getOffset();
|
||||
this.size = header.getSize();
|
||||
|
||||
if (offset + size > buf.length) {
|
||||
throw new InvalidOatFileException("String table extends past end of file");
|
||||
}
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public String getString(int index) {
|
||||
if (index >= size) {
|
||||
throw new InvalidOatFileException("String index is out of bounds");
|
||||
}
|
||||
|
||||
int start = offset + index;
|
||||
int end = start;
|
||||
while (buf[end] != 0) {
|
||||
end++;
|
||||
if (end >= offset + size) {
|
||||
throw new InvalidOatFileException("String extends past end of string table");
|
||||
}
|
||||
}
|
||||
|
||||
return new String(buf, start, end-start, Charset.forName("US-ASCII"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class InvalidOatFileException extends RuntimeException {
|
||||
public InvalidOatFileException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
||||
public static class NotAnOatFileException extends RuntimeException {
|
||||
public NotAnOatFileException() {}
|
||||
}
|
||||
}
|
@ -100,10 +100,10 @@ public class CodeItem {
|
||||
int triesCount = reader.readUshort();
|
||||
out.annotate(2, "tries_size = %d", triesCount);
|
||||
|
||||
int debugInfoOffset = reader.readSmallUint();
|
||||
int debugInfoOffset = reader.readInt();
|
||||
out.annotate(4, "debug_info_off = 0x%x", debugInfoOffset);
|
||||
|
||||
if (debugInfoOffset != 0) {
|
||||
if (debugInfoOffset > 0) {
|
||||
addDebugInfoIdentity(debugInfoOffset, itemIdentity);
|
||||
}
|
||||
|
||||
|
@ -59,7 +59,7 @@ public class RawDexFile extends DexBackedDexFile {
|
||||
|
||||
@Nonnull
|
||||
public byte[] readByteRange(int start, int length) {
|
||||
return Arrays.copyOfRange(getBuf(), start, start+length);
|
||||
return Arrays.copyOfRange(getBuf(), getBaseOffset() + start, getBaseOffset() + start + length);
|
||||
}
|
||||
|
||||
public int getMapOffset() {
|
||||
@ -94,6 +94,7 @@ public class RawDexFile extends DexBackedDexFile {
|
||||
}
|
||||
|
||||
public void writeAnnotations(@Nonnull Writer out, @Nonnull AnnotatedBytes annotatedBytes) throws IOException {
|
||||
// TODO: need to pass in the offset
|
||||
annotatedBytes.writeAnnotations(out, getBuf());
|
||||
}
|
||||
}
|
||||
|
@ -31,12 +31,12 @@
|
||||
|
||||
package org.jf.dexlib2.dexbacked.util;
|
||||
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.ImmutableSet;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.DebugItemType;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedMethod;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedMethodImplementation;
|
||||
import org.jf.dexlib2.dexbacked.DexBackedDexFile;
|
||||
import org.jf.dexlib2.dexbacked.DexReader;
|
||||
import org.jf.dexlib2.iface.MethodParameter;
|
||||
import org.jf.dexlib2.iface.debug.DebugItem;
|
||||
@ -70,9 +70,13 @@ public abstract class DebugInfo implements Iterable<DebugItem> {
|
||||
private static class EmptyDebugInfo extends DebugInfo {
|
||||
public static final EmptyDebugInfo INSTANCE = new EmptyDebugInfo();
|
||||
private EmptyDebugInfo() {}
|
||||
@Nonnull @Override public Iterator<DebugItem> iterator() { return Iterators.emptyIterator(); }
|
||||
|
||||
@Nonnull @Override public Iterator<DebugItem> iterator() {
|
||||
return ImmutableSet.<DebugItem>of().iterator();
|
||||
}
|
||||
|
||||
@Nonnull @Override public Iterator<String> getParameterNames(@Nullable DexReader reader) {
|
||||
return Iterators.emptyIterator();
|
||||
return ImmutableSet.<String>of().iterator();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -35,6 +35,7 @@ import org.jf.dexlib2.iface.reference.TypeReference;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
@ -72,11 +73,11 @@ public interface ClassDef extends TypeReference, Annotatable {
|
||||
@Nullable String getSuperclass();
|
||||
|
||||
/**
|
||||
* Gets a set of the interfaces that this class implements.
|
||||
* Gets a list of the interfaces that this class implements.
|
||||
*
|
||||
* @return A set of the interfaces that this class implements
|
||||
* @return A list of the interfaces that this class implements
|
||||
*/
|
||||
@Nonnull Set<String> getInterfaces();
|
||||
@Nonnull List<String> getInterfaces();
|
||||
|
||||
/**
|
||||
* Gets the name of the primary source file that this class is defined in, if available.
|
||||
|
@ -47,12 +47,13 @@ import javax.annotation.Nullable;
|
||||
import java.util.AbstractCollection;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
|
||||
@Nonnull protected final String type;
|
||||
protected final int accessFlags;
|
||||
@Nullable protected final String superclass;
|
||||
@Nonnull protected final ImmutableSet<String> interfaces;
|
||||
@Nonnull protected final ImmutableList<String> interfaces;
|
||||
@Nullable protected final String sourceFile;
|
||||
@Nonnull protected final ImmutableSet<? extends ImmutableAnnotation> annotations;
|
||||
@Nonnull protected final ImmutableSortedSet<? extends ImmutableField> staticFields;
|
||||
@ -78,7 +79,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
|
||||
this.type = type;
|
||||
this.accessFlags = accessFlags;
|
||||
this.superclass = superclass;
|
||||
this.interfaces = interfaces==null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(interfaces);
|
||||
this.interfaces = interfaces==null ? ImmutableList.<String>of() : ImmutableList.copyOf(interfaces);
|
||||
this.sourceFile = sourceFile;
|
||||
this.annotations = ImmutableAnnotation.immutableSetOf(annotations);
|
||||
this.staticFields = ImmutableField.immutableSetOf(Iterables.filter(fields, FieldUtil.FIELD_IS_STATIC));
|
||||
@ -100,7 +101,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
|
||||
this.type = type;
|
||||
this.accessFlags = accessFlags;
|
||||
this.superclass = superclass;
|
||||
this.interfaces = interfaces==null ? ImmutableSet.<String>of() : ImmutableSet.copyOf(interfaces);
|
||||
this.interfaces = interfaces==null ? ImmutableList.<String>of() : ImmutableList.copyOf(interfaces);
|
||||
this.sourceFile = sourceFile;
|
||||
this.annotations = ImmutableAnnotation.immutableSetOf(annotations);
|
||||
this.staticFields = ImmutableField.immutableSetOf(staticFields);
|
||||
@ -112,7 +113,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
|
||||
public ImmutableClassDef(@Nonnull String type,
|
||||
int accessFlags,
|
||||
@Nullable String superclass,
|
||||
@Nullable ImmutableSet<String> interfaces,
|
||||
@Nullable ImmutableList<String> interfaces,
|
||||
@Nullable String sourceFile,
|
||||
@Nullable ImmutableSet<? extends ImmutableAnnotation> annotations,
|
||||
@Nullable ImmutableSortedSet<? extends ImmutableField> staticFields,
|
||||
@ -122,7 +123,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
|
||||
this.type = type;
|
||||
this.accessFlags = accessFlags;
|
||||
this.superclass = superclass;
|
||||
this.interfaces = ImmutableUtils.nullToEmptySet(interfaces);
|
||||
this.interfaces = ImmutableUtils.nullToEmptyList(interfaces);
|
||||
this.sourceFile = sourceFile;
|
||||
this.annotations = ImmutableUtils.nullToEmptySet(annotations);
|
||||
this.staticFields = ImmutableUtils.nullToEmptySortedSet(staticFields);
|
||||
@ -151,7 +152,7 @@ public class ImmutableClassDef extends BaseTypeReference implements ClassDef {
|
||||
@Nonnull @Override public String getType() { return type; }
|
||||
@Override public int getAccessFlags() { return accessFlags; }
|
||||
@Nullable @Override public String getSuperclass() { return superclass; }
|
||||
@Nonnull @Override public ImmutableSet<String> getInterfaces() { return interfaces; }
|
||||
@Nonnull @Override public ImmutableList<String> getInterfaces() { return interfaces; }
|
||||
@Nullable @Override public String getSourceFile() { return sourceFile; }
|
||||
@Nonnull @Override public ImmutableSet<? extends ImmutableAnnotation> getAnnotations() { return annotations; }
|
||||
@Nonnull @Override public ImmutableSet<? extends ImmutableField> getStaticFields() { return staticFields; }
|
||||
|
@ -41,6 +41,7 @@ import org.jf.dexlib2.iface.Method;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
public class ClassDefRewriter implements Rewriter<ClassDef> {
|
||||
@ -73,8 +74,8 @@ public class ClassDefRewriter implements Rewriter<ClassDef> {
|
||||
return RewriterUtils.rewriteNullable(rewriters.getTypeRewriter(), classDef.getSuperclass());
|
||||
}
|
||||
|
||||
@Override @Nonnull public Set<String> getInterfaces() {
|
||||
return RewriterUtils.rewriteSet(rewriters.getTypeRewriter(), classDef.getInterfaces());
|
||||
@Override @Nonnull public List<String> getInterfaces() {
|
||||
return RewriterUtils.rewriteList(rewriters.getTypeRewriter(), classDef.getInterfaces());
|
||||
}
|
||||
|
||||
@Override @Nullable public String getSourceFile() {
|
||||
|
@ -35,6 +35,7 @@ import com.google.common.base.Predicate;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.iface.Method;
|
||||
import org.jf.dexlib2.iface.reference.MethodReference;
|
||||
import org.jf.util.CharSequenceUtils;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
@ -68,6 +69,12 @@ public final class MethodUtil {
|
||||
return methodReference.getName().equals("<init>");
|
||||
}
|
||||
|
||||
public static boolean isPackagePrivate(@Nonnull Method method) {
|
||||
return (method.getAccessFlags() & (AccessFlags.PRIVATE.getValue() |
|
||||
AccessFlags.PROTECTED.getValue() |
|
||||
AccessFlags.PUBLIC.getValue())) == 0;
|
||||
}
|
||||
|
||||
public static int getParameterRegisterCount(@Nonnull Method method) {
|
||||
return getParameterRegisterCount(method, MethodUtil.isStatic(method));
|
||||
}
|
||||
@ -109,5 +116,11 @@ public final class MethodUtil {
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
public static boolean methodSignaturesMatch(@Nonnull MethodReference a, @Nonnull MethodReference b) {
|
||||
return (a.getName().equals(b.getName()) &&
|
||||
a.getReturnType().equals(b.getReturnType()) &&
|
||||
CharSequenceUtils.listEquals(a.getParameterTypes(), b.getParameterTypes()));
|
||||
}
|
||||
|
||||
private MethodUtil() {}
|
||||
}
|
||||
|
@ -36,14 +36,15 @@ package org.jf.dexlib2.util;
|
||||
import org.jf.dexlib2.iface.instruction.Instruction;
|
||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SyntheticAccessorFSM {
|
||||
|
||||
// line 42 "SyntheticAccessorFSM.rl"
|
||||
// line 43 "SyntheticAccessorFSM.rl"
|
||||
|
||||
// line 47 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
// line 48 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
private static byte[] init__SyntheticAccessorFSM_actions_0()
|
||||
{
|
||||
return new byte [] {
|
||||
@ -187,7 +188,7 @@ static final int SyntheticAccessorFSM_error = 0;
|
||||
static final int SyntheticAccessorFSM_en_main = 1;
|
||||
|
||||
|
||||
// line 43 "SyntheticAccessorFSM.rl"
|
||||
// line 44 "SyntheticAccessorFSM.rl"
|
||||
|
||||
// math type constants
|
||||
public static final int ADD = SyntheticAccessorResolver.ADD_ASSIGNMENT;
|
||||
@ -230,13 +231,15 @@ static final int SyntheticAccessorFSM_en_main = 1;
|
||||
// The return register;
|
||||
int returnRegister = -1;
|
||||
|
||||
Opcodes opcodes = Opcodes.forApi(20);
|
||||
|
||||
|
||||
// line 235 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
// line 238 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
{
|
||||
cs = SyntheticAccessorFSM_start;
|
||||
}
|
||||
|
||||
// line 240 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
// line 243 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
{
|
||||
int _klen;
|
||||
int _trans = 0;
|
||||
@ -270,9 +273,9 @@ case 1:
|
||||
break;
|
||||
|
||||
_mid = _lower + ((_upper-_lower) >> 1);
|
||||
if ( ( instructions.get(p).getOpcode().value) < _SyntheticAccessorFSM_trans_keys[_mid] )
|
||||
if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) < _SyntheticAccessorFSM_trans_keys[_mid] )
|
||||
_upper = _mid - 1;
|
||||
else if ( ( instructions.get(p).getOpcode().value) > _SyntheticAccessorFSM_trans_keys[_mid] )
|
||||
else if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) > _SyntheticAccessorFSM_trans_keys[_mid] )
|
||||
_lower = _mid + 1;
|
||||
else {
|
||||
_trans += (_mid - _keys);
|
||||
@ -293,9 +296,9 @@ case 1:
|
||||
break;
|
||||
|
||||
_mid = _lower + (((_upper-_lower) >> 1) & ~1);
|
||||
if ( ( instructions.get(p).getOpcode().value) < _SyntheticAccessorFSM_trans_keys[_mid] )
|
||||
if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) < _SyntheticAccessorFSM_trans_keys[_mid] )
|
||||
_upper = _mid - 2;
|
||||
else if ( ( instructions.get(p).getOpcode().value) > _SyntheticAccessorFSM_trans_keys[_mid+1] )
|
||||
else if ( ( opcodes.getOpcodeValue(instructions.get(p).getOpcode())) > _SyntheticAccessorFSM_trans_keys[_mid+1] )
|
||||
_lower = _mid + 2;
|
||||
else {
|
||||
_trans += ((_mid - _keys)>>1);
|
||||
@ -317,19 +320,19 @@ case 1:
|
||||
switch ( _SyntheticAccessorFSM_actions[_acts++] )
|
||||
{
|
||||
case 0:
|
||||
// line 93 "SyntheticAccessorFSM.rl"
|
||||
// line 96 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
putRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
// line 100 "SyntheticAccessorFSM.rl"
|
||||
// line 103 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
constantValue = ((WideLiteralInstruction)instructions.get(p)).getWideLiteral();
|
||||
}
|
||||
break;
|
||||
case 2:
|
||||
// line 104 "SyntheticAccessorFSM.rl"
|
||||
// line 107 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathType = INT;
|
||||
mathOp = ADD;
|
||||
@ -337,146 +340,146 @@ case 1:
|
||||
}
|
||||
break;
|
||||
case 3:
|
||||
// line 110 "SyntheticAccessorFSM.rl"
|
||||
// line 113 "SyntheticAccessorFSM.rl"
|
||||
{ mathType = INT; }
|
||||
break;
|
||||
case 4:
|
||||
// line 111 "SyntheticAccessorFSM.rl"
|
||||
// line 114 "SyntheticAccessorFSM.rl"
|
||||
{ mathType = LONG; }
|
||||
break;
|
||||
case 5:
|
||||
// line 112 "SyntheticAccessorFSM.rl"
|
||||
// line 115 "SyntheticAccessorFSM.rl"
|
||||
{ mathType = FLOAT; }
|
||||
break;
|
||||
case 6:
|
||||
// line 113 "SyntheticAccessorFSM.rl"
|
||||
// line 116 "SyntheticAccessorFSM.rl"
|
||||
{mathType = DOUBLE; }
|
||||
break;
|
||||
case 7:
|
||||
// line 113 "SyntheticAccessorFSM.rl"
|
||||
// line 116 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = ADD;
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
// line 116 "SyntheticAccessorFSM.rl"
|
||||
// line 119 "SyntheticAccessorFSM.rl"
|
||||
{ mathType = INT; }
|
||||
break;
|
||||
case 9:
|
||||
// line 117 "SyntheticAccessorFSM.rl"
|
||||
// line 120 "SyntheticAccessorFSM.rl"
|
||||
{ mathType = LONG; }
|
||||
break;
|
||||
case 10:
|
||||
// line 118 "SyntheticAccessorFSM.rl"
|
||||
// line 121 "SyntheticAccessorFSM.rl"
|
||||
{ mathType = FLOAT; }
|
||||
break;
|
||||
case 11:
|
||||
// line 119 "SyntheticAccessorFSM.rl"
|
||||
// line 122 "SyntheticAccessorFSM.rl"
|
||||
{mathType = DOUBLE; }
|
||||
break;
|
||||
case 12:
|
||||
// line 119 "SyntheticAccessorFSM.rl"
|
||||
// line 122 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = SUB;
|
||||
}
|
||||
break;
|
||||
case 13:
|
||||
// line 123 "SyntheticAccessorFSM.rl"
|
||||
// line 126 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = MUL;
|
||||
}
|
||||
break;
|
||||
case 14:
|
||||
// line 127 "SyntheticAccessorFSM.rl"
|
||||
// line 130 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = DIV;
|
||||
}
|
||||
break;
|
||||
case 15:
|
||||
// line 131 "SyntheticAccessorFSM.rl"
|
||||
// line 134 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = REM;
|
||||
}
|
||||
break;
|
||||
case 16:
|
||||
// line 134 "SyntheticAccessorFSM.rl"
|
||||
// line 137 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = AND;
|
||||
}
|
||||
break;
|
||||
case 17:
|
||||
// line 137 "SyntheticAccessorFSM.rl"
|
||||
// line 140 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = OR;
|
||||
}
|
||||
break;
|
||||
case 18:
|
||||
// line 140 "SyntheticAccessorFSM.rl"
|
||||
// line 143 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = XOR;
|
||||
}
|
||||
break;
|
||||
case 19:
|
||||
// line 143 "SyntheticAccessorFSM.rl"
|
||||
// line 146 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = SHL;
|
||||
}
|
||||
break;
|
||||
case 20:
|
||||
// line 146 "SyntheticAccessorFSM.rl"
|
||||
// line 149 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = SHR;
|
||||
}
|
||||
break;
|
||||
case 21:
|
||||
// line 149 "SyntheticAccessorFSM.rl"
|
||||
// line 152 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
mathOp = USHR;
|
||||
}
|
||||
break;
|
||||
case 22:
|
||||
// line 155 "SyntheticAccessorFSM.rl"
|
||||
// line 158 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
returnRegister = ((OneRegisterInstruction)instructions.get(p)).getRegisterA();
|
||||
}
|
||||
break;
|
||||
case 23:
|
||||
// line 161 "SyntheticAccessorFSM.rl"
|
||||
// line 164 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
accessorType = SyntheticAccessorResolver.GETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;}
|
||||
}
|
||||
break;
|
||||
case 24:
|
||||
// line 165 "SyntheticAccessorFSM.rl"
|
||||
// line 168 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
accessorType = SyntheticAccessorResolver.SETTER; { p += 1; _goto_targ = 5; if (true) continue _goto;}
|
||||
}
|
||||
break;
|
||||
case 25:
|
||||
// line 169 "SyntheticAccessorFSM.rl"
|
||||
// line 172 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
accessorType = SyntheticAccessorResolver.METHOD; { p += 1; _goto_targ = 5; if (true) continue _goto;}
|
||||
}
|
||||
break;
|
||||
case 26:
|
||||
// line 173 "SyntheticAccessorFSM.rl"
|
||||
// line 176 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
|
||||
}
|
||||
break;
|
||||
case 27:
|
||||
// line 177 "SyntheticAccessorFSM.rl"
|
||||
// line 180 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
accessorType = getIncrementType(mathOp, mathType, constantValue, putRegister, returnRegister);
|
||||
}
|
||||
break;
|
||||
case 28:
|
||||
// line 185 "SyntheticAccessorFSM.rl"
|
||||
// line 188 "SyntheticAccessorFSM.rl"
|
||||
{
|
||||
accessorType = mathOp; { p += 1; _goto_targ = 5; if (true) continue _goto;}
|
||||
}
|
||||
break;
|
||||
// line 480 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
// line 483 "/home/jesusfreke/projects/smali/dexlib2/src/main/java/org/jf/dexlib2/util/SyntheticAccessorFSM.java"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -496,7 +499,7 @@ case 5:
|
||||
break; }
|
||||
}
|
||||
|
||||
// line 198 "SyntheticAccessorFSM.rl"
|
||||
// line 201 "SyntheticAccessorFSM.rl"
|
||||
|
||||
|
||||
return accessorType;
|
||||
|
@ -31,6 +31,8 @@
|
||||
|
||||
package org.jf.dexlib2.util;
|
||||
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.iface.ClassDef;
|
||||
import org.jf.dexlib2.iface.reference.TypeReference;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
@ -49,5 +51,24 @@ public final class TypeUtils {
|
||||
return type.length() == 1;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static String getPackage(@Nonnull String type) {
|
||||
int lastSlash = type.lastIndexOf('/');
|
||||
if (lastSlash < 0) {
|
||||
return "";
|
||||
}
|
||||
return type.substring(1, lastSlash);
|
||||
}
|
||||
|
||||
public static boolean canAccessClass(@Nonnull String accessorType, @Nonnull ClassDef accesseeClassDef) {
|
||||
if (AccessFlags.PUBLIC.isSet(accesseeClassDef.getAccessFlags())) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Classes can only be public or package private. Any private or protected inner classes are actually
|
||||
// package private.
|
||||
return getPackage(accesseeClassDef.getType()).equals(getPackage(accessorType));
|
||||
}
|
||||
|
||||
private TypeUtils() {}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ public interface ClassSection<StringKey extends CharSequence, TypeKey extends Ch
|
||||
@Nonnull TypeKey getType(@Nonnull ClassKey key);
|
||||
int getAccessFlags(@Nonnull ClassKey key);
|
||||
@Nullable TypeKey getSuperclass(@Nonnull ClassKey key);
|
||||
@Nullable TypeListKey getSortedInterfaces(@Nonnull ClassKey key);
|
||||
@Nullable TypeListKey getInterfaces(@Nonnull ClassKey key);
|
||||
@Nullable StringKey getSourceFile(@Nonnull ClassKey key);
|
||||
@Nullable Collection<? extends EncodedValue> getStaticInitializers(@Nonnull ClassKey key);
|
||||
|
||||
|
@ -37,6 +37,7 @@ import com.google.common.collect.Maps;
|
||||
import com.google.common.collect.Ordering;
|
||||
import org.jf.dexlib2.AccessFlags;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
import org.jf.dexlib2.base.BaseAnnotation;
|
||||
import org.jf.dexlib2.base.BaseAnnotationElement;
|
||||
@ -58,6 +59,7 @@ import org.jf.dexlib2.iface.reference.StringReference;
|
||||
import org.jf.dexlib2.iface.reference.TypeReference;
|
||||
import org.jf.dexlib2.util.InstructionUtil;
|
||||
import org.jf.dexlib2.util.MethodUtil;
|
||||
import org.jf.dexlib2.util.ReferenceUtil;
|
||||
import org.jf.dexlib2.writer.io.DeferredOutputStream;
|
||||
import org.jf.dexlib2.writer.io.DeferredOutputStreamFactory;
|
||||
import org.jf.dexlib2.writer.io.DexDataStore;
|
||||
@ -93,7 +95,7 @@ public abstract class DexWriter<
|
||||
public static final int NO_INDEX = -1;
|
||||
public static final int NO_OFFSET = 0;
|
||||
|
||||
protected final int api;
|
||||
protected final Opcodes opcodes;
|
||||
|
||||
protected int stringIndexSectionOffset = NO_OFFSET;
|
||||
protected int typeSectionOffset = NO_OFFSET;
|
||||
@ -133,7 +135,7 @@ public abstract class DexWriter<
|
||||
protected final AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement, EncodedValue> annotationSection;
|
||||
protected final AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection;
|
||||
|
||||
protected DexWriter(int api,
|
||||
protected DexWriter(Opcodes opcodes,
|
||||
StringSection<StringKey, StringRef> stringSection,
|
||||
TypeSection<StringKey, TypeKey, TypeRef> typeSection,
|
||||
ProtoSection<StringKey, TypeKey, ProtoKey, TypeListKey> protoSection,
|
||||
@ -145,7 +147,8 @@ public abstract class DexWriter<
|
||||
AnnotationSection<StringKey, TypeKey, AnnotationKey, AnnotationElement,
|
||||
EncodedValue> annotationSection,
|
||||
AnnotationSetSection<AnnotationKey, AnnotationSetKey> annotationSetSection) {
|
||||
this.api = api;
|
||||
this.opcodes = opcodes;
|
||||
|
||||
this.stringSection = stringSection;
|
||||
this.typeSection = typeSection;
|
||||
this.protoSection = protoSection;
|
||||
@ -196,6 +199,33 @@ public abstract class DexWriter<
|
||||
classSection.getItems().size() * ClassDefItem.ITEM_SIZE;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<String> getMethodReferences() {
|
||||
List<String> methodReferences = Lists.newArrayList();
|
||||
for (Entry<? extends MethodRefKey, Integer> methodReference: methodSection.getItems()) {
|
||||
methodReferences.add(ReferenceUtil.getMethodDescriptor(methodReference.getKey()));
|
||||
}
|
||||
return methodReferences;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<String> getFieldReferences() {
|
||||
List<String> fieldReferences = Lists.newArrayList();
|
||||
for (Entry<? extends FieldRefKey, Integer> fieldReference: fieldSection.getItems()) {
|
||||
fieldReferences.add(ReferenceUtil.getFieldDescriptor(fieldReference.getKey()));
|
||||
}
|
||||
return fieldReferences;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public List<String> getTypeReferences() {
|
||||
List<String> classReferences = Lists.newArrayList();
|
||||
for (Entry<? extends TypeKey, Integer> typeReference: typeSection.getItems()) {
|
||||
classReferences.add(typeReference.getKey().toString());
|
||||
}
|
||||
return classReferences;
|
||||
}
|
||||
|
||||
public void writeTo(@Nonnull DexDataStore dest) throws IOException {
|
||||
this.writeTo(dest, MemoryDeferredOutputStream.getFactory());
|
||||
}
|
||||
@ -405,7 +435,7 @@ public abstract class DexWriter<
|
||||
nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, superEntry);
|
||||
|
||||
// then, try to write interfaces
|
||||
for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getSortedInterfaces(key))) {
|
||||
for (TypeKey interfaceTypeKey: typeListSection.getTypes(classSection.getInterfaces(key))) {
|
||||
Map.Entry<? extends ClassKey, Integer> interfaceEntry = classSection.getClassEntryByType(interfaceTypeKey);
|
||||
nextIndex = writeClass(indexWriter, offsetWriter, nextIndex, interfaceEntry);
|
||||
}
|
||||
@ -418,7 +448,7 @@ public abstract class DexWriter<
|
||||
indexWriter.writeInt(typeSection.getItemIndex(classSection.getType(key)));
|
||||
indexWriter.writeInt(classSection.getAccessFlags(key));
|
||||
indexWriter.writeInt(typeSection.getNullableItemIndex(classSection.getSuperclass(key)));
|
||||
indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getSortedInterfaces(key)));
|
||||
indexWriter.writeInt(typeListSection.getNullableItemOffset(classSection.getInterfaces(key)));
|
||||
indexWriter.writeInt(stringSection.getNullableItemIndex(classSection.getSourceFile(key)));
|
||||
indexWriter.writeInt(classSection.getAnnotationDirectoryOffset(key));
|
||||
|
||||
@ -915,7 +945,7 @@ public abstract class DexWriter<
|
||||
writer.writeInt(debugItemOffset);
|
||||
|
||||
InstructionWriter instructionWriter =
|
||||
InstructionWriter.makeInstructionWriter(writer, stringSection, typeSection, fieldSection,
|
||||
InstructionWriter.makeInstructionWriter(opcodes, writer, stringSection, typeSection, fieldSection,
|
||||
methodSection);
|
||||
|
||||
writer.writeInt(codeUnitCount);
|
||||
@ -1238,6 +1268,6 @@ public abstract class DexWriter<
|
||||
// Workaround for a crash in Dalvik VM before Jelly Bean MR1 (4.2)
|
||||
// which is triggered by NO_OFFSET in parameter annotation list.
|
||||
// (https://code.google.com/p/android/issues/detail?id=35304)
|
||||
return (api < 17);
|
||||
return (opcodes.api < 17);
|
||||
}
|
||||
}
|
||||
|
@ -33,6 +33,8 @@ package org.jf.dexlib2.writer;
|
||||
|
||||
import com.google.common.collect.Ordering;
|
||||
import com.google.common.primitives.Ints;
|
||||
import org.jf.dexlib2.Opcode;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.ReferenceType;
|
||||
import org.jf.dexlib2.iface.instruction.ReferenceInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.SwitchElement;
|
||||
@ -50,6 +52,7 @@ import java.util.List;
|
||||
|
||||
public class InstructionWriter<StringRef extends StringReference, TypeRef extends TypeReference,
|
||||
FieldRefKey extends FieldReference, MethodRefKey extends MethodReference> {
|
||||
@Nonnull private final Opcodes opcodes;
|
||||
@Nonnull private final DexDataWriter writer;
|
||||
@Nonnull private final StringSection<?, StringRef> stringSection;
|
||||
@Nonnull private final TypeSection<?, ?, TypeRef> typeSection;
|
||||
@ -59,20 +62,23 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
@Nonnull static <StringRef extends StringReference, TypeRef extends TypeReference, FieldRefKey extends FieldReference, MethodRefKey extends MethodReference>
|
||||
InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey>
|
||||
makeInstructionWriter(
|
||||
@Nonnull Opcodes opcodes,
|
||||
@Nonnull DexDataWriter writer,
|
||||
@Nonnull StringSection<?, StringRef> stringSection,
|
||||
@Nonnull TypeSection<?, ?, TypeRef> typeSection,
|
||||
@Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection,
|
||||
@Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection) {
|
||||
return new InstructionWriter<StringRef, TypeRef, FieldRefKey, MethodRefKey>(
|
||||
writer, stringSection, typeSection, fieldSection, methodSection);
|
||||
opcodes, writer, stringSection, typeSection, fieldSection, methodSection);
|
||||
}
|
||||
|
||||
InstructionWriter(@Nonnull DexDataWriter writer,
|
||||
InstructionWriter(@Nonnull Opcodes opcodes,
|
||||
@Nonnull DexDataWriter writer,
|
||||
@Nonnull StringSection<?, StringRef> stringSection,
|
||||
@Nonnull TypeSection<?, ?, TypeRef> typeSection,
|
||||
@Nonnull FieldSection<?, ?, FieldRefKey, ?> fieldSection,
|
||||
@Nonnull MethodSection<?, ?, ?, MethodRefKey, ?> methodSection) {
|
||||
this.opcodes = opcodes;
|
||||
this.writer = writer;
|
||||
this.stringSection = stringSection;
|
||||
this.typeSection = typeSection;
|
||||
@ -80,9 +86,17 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
this.methodSection = methodSection;
|
||||
}
|
||||
|
||||
private short getOpcodeValue(Opcode opcode) {
|
||||
Short value = opcodes.getOpcodeValue(opcode);
|
||||
if (value == null) {
|
||||
throw new ExceptionWithContext("Instruction %s is invalid for api %d", opcode.name, opcodes.api);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public void write(@Nonnull Instruction10t instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getCodeOffset());
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
@ -91,7 +105,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction10x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(0);
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
@ -100,7 +114,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction11n instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getNarrowLiteral()));
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
@ -109,7 +123,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction11x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
@ -118,7 +132,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction12x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
@ -127,7 +141,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction20bc instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getVerificationError());
|
||||
writer.writeUshort(getReferenceIndex(instruction));
|
||||
} catch (IOException ex) {
|
||||
@ -137,7 +151,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction20t instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(0);
|
||||
writer.writeShort(instruction.getCodeOffset());
|
||||
} catch (IOException ex) {
|
||||
@ -147,7 +161,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction21c instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeUshort(getReferenceIndex(instruction));
|
||||
} catch (IOException ex) {
|
||||
@ -157,7 +171,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction21ih instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getHatLiteral());
|
||||
} catch (IOException ex) {
|
||||
@ -167,7 +181,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction21lh instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getHatLiteral());
|
||||
} catch (IOException ex) {
|
||||
@ -177,7 +191,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction21s instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getNarrowLiteral());
|
||||
} catch (IOException ex) {
|
||||
@ -187,7 +201,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction21t instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeShort(instruction.getCodeOffset());
|
||||
} catch (IOException ex) {
|
||||
@ -197,7 +211,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction22b instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.write(instruction.getRegisterB());
|
||||
writer.write(instruction.getNarrowLiteral());
|
||||
@ -208,7 +222,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction22c instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
writer.writeUshort(getReferenceIndex(instruction));
|
||||
} catch (IOException ex) {
|
||||
@ -218,7 +232,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction22s instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
writer.writeShort(instruction.getNarrowLiteral());
|
||||
} catch (IOException ex) {
|
||||
@ -228,7 +242,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction22t instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(packNibbles(instruction.getRegisterA(), instruction.getRegisterB()));
|
||||
writer.writeShort(instruction.getCodeOffset());
|
||||
} catch (IOException ex) {
|
||||
@ -238,7 +252,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction22x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeUshort(instruction.getRegisterB());
|
||||
} catch (IOException ex) {
|
||||
@ -248,7 +262,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction23x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.write(instruction.getRegisterB());
|
||||
writer.write(instruction.getRegisterC());
|
||||
@ -259,7 +273,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction30t instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(0);
|
||||
writer.writeInt(instruction.getCodeOffset());
|
||||
} catch (IOException ex) {
|
||||
@ -269,7 +283,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction31c instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeInt(getReferenceIndex(instruction));
|
||||
} catch (IOException ex) {
|
||||
@ -279,7 +293,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction31i instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeInt(instruction.getNarrowLiteral());
|
||||
} catch (IOException ex) {
|
||||
@ -289,7 +303,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction31t instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeInt(instruction.getCodeOffset());
|
||||
} catch (IOException ex) {
|
||||
@ -299,7 +313,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction32x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(0);
|
||||
writer.writeUshort(instruction.getRegisterA());
|
||||
writer.writeUshort(instruction.getRegisterB());
|
||||
@ -310,7 +324,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction35c instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(packNibbles(instruction.getRegisterG(), instruction.getRegisterCount()));
|
||||
writer.writeUshort(getReferenceIndex(instruction));
|
||||
writer.write(packNibbles(instruction.getRegisterC(), instruction.getRegisterD()));
|
||||
@ -322,7 +336,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction25x instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(packNibbles(
|
||||
instruction.getRegisterParameterG(), instruction.getParameterRegisterCount()));
|
||||
writer.write(packNibbles(
|
||||
@ -335,7 +349,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
}
|
||||
public void write(@Nonnull Instruction3rc instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterCount());
|
||||
writer.writeUshort(getReferenceIndex(instruction));
|
||||
writer.writeUshort(instruction.getStartRegister());
|
||||
@ -346,7 +360,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull Instruction51l instruction) {
|
||||
try {
|
||||
writer.write(instruction.getOpcode().value);
|
||||
writer.write(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.write(instruction.getRegisterA());
|
||||
writer.writeLong(instruction.getWideLiteral());
|
||||
} catch (IOException ex) {
|
||||
@ -356,7 +370,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
|
||||
public void write(@Nonnull ArrayPayload instruction) {
|
||||
try {
|
||||
writer.writeUshort(instruction.getOpcode().value);
|
||||
writer.writeUshort(getOpcodeValue(instruction.getOpcode()));
|
||||
writer.writeUshort(instruction.getElementWidth());
|
||||
List<Number> elements = instruction.getArrayElements();
|
||||
writer.writeInt(elements.size());
|
||||
@ -393,7 +407,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
public void write(@Nonnull SparseSwitchPayload instruction) {
|
||||
try {
|
||||
writer.writeUbyte(0);
|
||||
writer.writeUbyte(instruction.getOpcode().value >> 8);
|
||||
writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8);
|
||||
List<? extends SwitchElement> elements = Ordering.from(switchElementComparator).immutableSortedCopy(
|
||||
instruction.getSwitchElements());
|
||||
writer.writeUshort(elements.size());
|
||||
@ -417,7 +431,7 @@ public class InstructionWriter<StringRef extends StringReference, TypeRef extend
|
||||
public void write(@Nonnull PackedSwitchPayload instruction) {
|
||||
try {
|
||||
writer.writeUbyte(0);
|
||||
writer.writeUbyte(instruction.getOpcode().value >> 8);
|
||||
writer.writeUbyte(getOpcodeValue(instruction.getOpcode()) >> 8);
|
||||
List<? extends SwitchElement> elements = instruction.getSwitchElements();
|
||||
writer.writeUshort(elements.size());
|
||||
if (elements.size() == 0) {
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package org.jf.dexlib2.writer.builder;
|
||||
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.base.Functions;
|
||||
import com.google.common.collect.*;
|
||||
import org.jf.dexlib2.base.reference.BaseTypeReference;
|
||||
@ -101,16 +102,8 @@ public class BuilderClassDef extends BaseTypeReference implements ClassDef {
|
||||
@Nonnull @Override public SortedSet<BuilderMethod> getVirtualMethods() { return virtualMethods; }
|
||||
|
||||
@Nonnull @Override
|
||||
public Set<String> getInterfaces() {
|
||||
return new AbstractSet<String>() {
|
||||
@Nonnull @Override public Iterator<String> iterator() {
|
||||
return Iterators.transform(interfaces.iterator(), Functions.toStringFunction());
|
||||
}
|
||||
|
||||
@Override public int size() {
|
||||
return interfaces.size();
|
||||
}
|
||||
};
|
||||
public List<String> getInterfaces() {
|
||||
return Lists.transform(this.interfaces, Functions.toStringFunction());
|
||||
}
|
||||
|
||||
@Nonnull @Override public Collection<BuilderField> getFields() {
|
||||
|
@ -122,7 +122,7 @@ public class BuilderClassPool implements ClassSection<BuilderStringReference, Bu
|
||||
return builderClassDef.superclass;
|
||||
}
|
||||
|
||||
@Nullable @Override public BuilderTypeList getSortedInterfaces(@Nonnull BuilderClassDef builderClassDef) {
|
||||
@Nullable @Override public BuilderTypeList getInterfaces(@Nonnull BuilderClassDef builderClassDef) {
|
||||
return builderClassDef.interfaces;
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,8 @@ package org.jf.dexlib2.writer.builder;
|
||||
import com.google.common.base.Function;
|
||||
import com.google.common.collect.ImmutableList;
|
||||
import com.google.common.collect.Iterators;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Sets;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.ValueType;
|
||||
import org.jf.dexlib2.iface.Annotation;
|
||||
import org.jf.dexlib2.iface.MethodImplementation;
|
||||
@ -48,7 +49,6 @@ import org.jf.util.ExceptionWithContext;
|
||||
import javax.annotation.Nonnull;
|
||||
import javax.annotation.Nullable;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
@ -58,20 +58,27 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
|
||||
BuilderClassDef, BuilderAnnotation, BuilderAnnotationSet, BuilderTypeList, BuilderField, BuilderMethod,
|
||||
BuilderEncodedValue, BuilderAnnotationElement> {
|
||||
|
||||
private final BuilderContext context;
|
||||
@Nonnull private final BuilderContext context;
|
||||
|
||||
public static DexBuilder makeDexBuilder() {
|
||||
@Nonnull public static DexBuilder makeDexBuilder() {
|
||||
BuilderContext context = new BuilderContext();
|
||||
return new DexBuilder(15, context);
|
||||
return new DexBuilder(Opcodes.forApi(20), context);
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public static DexBuilder makeDexBuilder(int api) {
|
||||
BuilderContext context = new BuilderContext();
|
||||
return new DexBuilder(api, context);
|
||||
return new DexBuilder(Opcodes.forApi(api), context);
|
||||
}
|
||||
|
||||
private DexBuilder(int api, @Nonnull BuilderContext context) {
|
||||
super(api, context.stringPool, context.typePool, context.protoPool,
|
||||
@Nonnull public static DexBuilder makeDexBuilder(@Nonnull Opcodes opcodes) {
|
||||
BuilderContext context = new BuilderContext();
|
||||
return new DexBuilder(opcodes, context);
|
||||
}
|
||||
|
||||
private DexBuilder(@Nonnull Opcodes opcodes, @Nonnull BuilderContext context) {
|
||||
super(opcodes, context.stringPool, context.typePool, context.protoPool,
|
||||
context.fieldPool, context.methodPool, context.classPool, context.typeListPool, context.annotationPool,
|
||||
context.annotationSetPool);
|
||||
this.context = context;
|
||||
@ -117,16 +124,15 @@ public class DexBuilder extends DexWriter<BuilderStringReference, BuilderStringR
|
||||
if (interfaces == null) {
|
||||
interfaces = ImmutableList.of();
|
||||
} else {
|
||||
interfaces = Lists.newArrayList(interfaces);
|
||||
Collections.sort(interfaces);
|
||||
String prev = null;
|
||||
Set<String> interfaces_copy = Sets.newHashSet(interfaces);
|
||||
Iterator<String> interfaceIterator = interfaces.iterator();
|
||||
while (interfaceIterator.hasNext()) {
|
||||
String iface = interfaceIterator.next();
|
||||
if (prev != null && iface.equals(prev)) {
|
||||
if (!interfaces_copy.contains(iface)) {
|
||||
interfaceIterator.remove();
|
||||
} else {
|
||||
interfaces_copy.remove(iface);
|
||||
}
|
||||
prev = iface;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -250,7 +250,7 @@ public class ClassPool implements ClassSection<CharSequence, CharSequence,
|
||||
return classDef.getSuperclass();
|
||||
}
|
||||
|
||||
@Nullable @Override public TypeListPool.Key<SortedSet<String>> getSortedInterfaces(@Nonnull PoolClassDef classDef) {
|
||||
@Nullable @Override public TypeListPool.Key<List<String>> getInterfaces(@Nonnull PoolClassDef classDef) {
|
||||
return classDef.interfaces;
|
||||
}
|
||||
|
||||
|
@ -31,6 +31,7 @@
|
||||
|
||||
package org.jf.dexlib2.writer.pool;
|
||||
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.ValueType;
|
||||
import org.jf.dexlib2.iface.Annotation;
|
||||
import org.jf.dexlib2.iface.AnnotationElement;
|
||||
@ -56,11 +57,19 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
|
||||
TypeListPool.Key<? extends Collection<? extends CharSequence>>, Field, PoolMethod,
|
||||
EncodedValue, AnnotationElement> {
|
||||
|
||||
@Nonnull
|
||||
public static DexPool makeDexPool() {
|
||||
return makeDexPool(15);
|
||||
return makeDexPool(Opcodes.forApi(20));
|
||||
}
|
||||
|
||||
@Deprecated
|
||||
@Nonnull
|
||||
public static DexPool makeDexPool(int api) {
|
||||
return makeDexPool(Opcodes.forApi(api));
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
public static DexPool makeDexPool(@Nonnull Opcodes opcodes) {
|
||||
StringPool stringPool = new StringPool();
|
||||
TypePool typePool = new TypePool(stringPool);
|
||||
FieldPool fieldPool = new FieldPool(stringPool, typePool);
|
||||
@ -72,14 +81,14 @@ public class DexPool extends DexWriter<CharSequence, StringReference, CharSequen
|
||||
ClassPool classPool = new ClassPool(stringPool, typePool, fieldPool, methodPool, annotationSetPool,
|
||||
typeListPool);
|
||||
|
||||
return new DexPool(api, stringPool, typePool, protoPool, fieldPool, methodPool, classPool, typeListPool,
|
||||
return new DexPool(opcodes, stringPool, typePool, protoPool, fieldPool, methodPool, classPool, typeListPool,
|
||||
annotationPool, annotationSetPool);
|
||||
}
|
||||
|
||||
private DexPool(int api, StringPool stringPool, TypePool typePool, ProtoPool protoPool, FieldPool fieldPool,
|
||||
private DexPool(Opcodes opcodes, StringPool stringPool, TypePool typePool, ProtoPool protoPool, FieldPool fieldPool,
|
||||
MethodPool methodPool, ClassPool classPool, TypeListPool typeListPool,
|
||||
AnnotationPool annotationPool, AnnotationSetPool annotationSetPool) {
|
||||
super(api, stringPool, typePool, protoPool, fieldPool, methodPool,
|
||||
super(opcodes, stringPool, typePool, protoPool, fieldPool, methodPool,
|
||||
classPool, typeListPool, annotationPool, annotationSetPool);
|
||||
}
|
||||
|
||||
|
@ -43,7 +43,7 @@ import java.util.*;
|
||||
|
||||
class PoolClassDef extends BaseTypeReference implements ClassDef {
|
||||
@Nonnull final ClassDef classDef;
|
||||
@Nonnull final TypeListPool.Key<SortedSet<String>> interfaces;
|
||||
@Nonnull final TypeListPool.Key<List<String>> interfaces;
|
||||
@Nonnull final ImmutableSortedSet<Field> staticFields;
|
||||
@Nonnull final ImmutableSortedSet<Field> instanceFields;
|
||||
@Nonnull final ImmutableSortedSet<PoolMethod> directMethods;
|
||||
@ -56,7 +56,7 @@ class PoolClassDef extends BaseTypeReference implements ClassDef {
|
||||
PoolClassDef(@Nonnull ClassDef classDef) {
|
||||
this.classDef = classDef;
|
||||
|
||||
interfaces = new TypeListPool.Key<SortedSet<String>>(ImmutableSortedSet.copyOf(classDef.getInterfaces()));
|
||||
interfaces = new TypeListPool.Key<List<String>>(ImmutableList.copyOf(classDef.getInterfaces()));
|
||||
staticFields = ImmutableSortedSet.copyOf(classDef.getStaticFields());
|
||||
instanceFields = ImmutableSortedSet.copyOf(classDef.getInstanceFields());
|
||||
directMethods = ImmutableSortedSet.copyOf(
|
||||
@ -77,7 +77,7 @@ class PoolClassDef extends BaseTypeReference implements ClassDef {
|
||||
return classDef.getSuperclass();
|
||||
}
|
||||
|
||||
@Nonnull @Override public SortedSet<String> getInterfaces() {
|
||||
@Nonnull @Override public List<String> getInterfaces() {
|
||||
return interfaces.types;
|
||||
}
|
||||
|
||||
|
@ -34,6 +34,7 @@ package org.jf.dexlib2.util;
|
||||
import org.jf.dexlib2.iface.instruction.Instruction;
|
||||
import org.jf.dexlib2.iface.instruction.OneRegisterInstruction;
|
||||
import org.jf.dexlib2.iface.instruction.WideLiteralInstruction;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -82,10 +83,12 @@ public class SyntheticAccessorFSM {
|
||||
// The return register;
|
||||
int returnRegister = -1;
|
||||
|
||||
Opcodes opcodes = Opcodes.forApi(20);
|
||||
|
||||
%%{
|
||||
import "Opcodes.rl";
|
||||
alphtype short;
|
||||
getkey instructions.get(p).getOpcode().value;
|
||||
getkey opcodes.getOpcodeValue(instructions.get(p).getOpcode());
|
||||
|
||||
get = (0x52 .. 0x58) | (0x60 .. 0x66); # all igets/sgets
|
||||
|
||||
|
@ -71,7 +71,7 @@ public class CustomMethodInlineTableTest {
|
||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||
15, false);
|
||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
||||
|
||||
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
|
||||
Assert.assertEquals(Opcode.INVOKE_VIRTUAL, deodexedInstruction.getOpcode());
|
||||
@ -98,7 +98,7 @@ public class CustomMethodInlineTableTest {
|
||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||
15, false);
|
||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
||||
|
||||
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
|
||||
Assert.assertEquals(Opcode.INVOKE_STATIC, deodexedInstruction.getOpcode());
|
||||
@ -125,7 +125,7 @@ public class CustomMethodInlineTableTest {
|
||||
ClassPath classPath = ClassPath.fromClassPath(ImmutableList.<String>of(), ImmutableList.<String>of(), dexFile,
|
||||
15, false);
|
||||
InlineMethodResolver inlineMethodResolver = new CustomInlineMethodResolver(classPath, "Lblah;->blah()V");
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver);
|
||||
MethodAnalyzer methodAnalyzer = new MethodAnalyzer(classPath, method, inlineMethodResolver, false);
|
||||
|
||||
Instruction deodexedInstruction = methodAnalyzer.getInstructions().get(0);
|
||||
Assert.assertEquals(Opcode.INVOKE_DIRECT, deodexedInstruction.getOpcode());
|
||||
|
@ -77,7 +77,7 @@ public class DexWriterTest {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dataStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dataStore.getData());
|
||||
ClassDef dbClassDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||
Assert.assertNotNull(dbClassDef);
|
||||
Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);
|
||||
@ -117,7 +117,7 @@ public class DexWriterTest {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dataStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dataStore.getData());
|
||||
ClassDef dbClassDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||
Assert.assertNotNull(dbClassDef);
|
||||
Annotation dbAnnotation = Iterables.getFirst(dbClassDef.getAnnotations(), null);
|
||||
|
@ -62,7 +62,7 @@ import java.util.List;
|
||||
public class JumboStringConversionTest {
|
||||
@Test
|
||||
public void testJumboStringConversion() throws IOException {
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(15));
|
||||
|
||||
MethodImplementationBuilder methodBuilder = new MethodImplementationBuilder(1);
|
||||
for (int i=0; i<66000; i++) {
|
||||
@ -92,7 +92,7 @@ public class JumboStringConversionTest {
|
||||
MemoryDataStore dexStore = new MemoryDataStore();
|
||||
dexBuilder.writeTo(dexStore);
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dexStore.getData());
|
||||
|
||||
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||
Assert.assertNotNull(classDef);
|
||||
@ -122,7 +122,7 @@ public class JumboStringConversionTest {
|
||||
|
||||
@Test
|
||||
public void testJumboStringConversion_NonMethodBuilder() throws IOException {
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(15);
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(15));
|
||||
|
||||
final List<Instruction> instructions = Lists.newArrayList();
|
||||
for (int i=0; i<66000; i++) {
|
||||
@ -189,7 +189,7 @@ public class JumboStringConversionTest {
|
||||
MemoryDataStore dexStore = new MemoryDataStore();
|
||||
dexBuilder.writeTo(dexStore);
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(new Opcodes(15, false), dexStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(15), dexStore.getData());
|
||||
|
||||
ClassDef classDef = Iterables.getFirst(dexFile.getClasses(), null);
|
||||
Assert.assertNotNull(classDef);
|
||||
|
BIN
dexlib2/src/test/resources/accessorTest.dex
Normal file
BIN
dexlib2/src/test/resources/accessorTest.dex
Normal file
Binary file not shown.
@ -257,7 +257,7 @@ import org.jf.dexlib2.Opcodes;
|
||||
private boolean verboseErrors = false;
|
||||
private boolean allowOdex = false;
|
||||
private int apiLevel = 15;
|
||||
private Opcodes opcodes = new Opcodes(apiLevel, false);
|
||||
private Opcodes opcodes = Opcodes.forApi(apiLevel);
|
||||
|
||||
public void setVerboseErrors(boolean verboseErrors) {
|
||||
this.verboseErrors = verboseErrors;
|
||||
|
@ -77,7 +77,7 @@ import java.util.*;
|
||||
public String classType;
|
||||
private boolean verboseErrors = false;
|
||||
private int apiLevel = 15;
|
||||
private Opcodes opcodes = new Opcodes(apiLevel, false);
|
||||
private Opcodes opcodes = Opcodes.forApi(apiLevel);
|
||||
private DexBuilder dexBuilder;
|
||||
|
||||
public void setDexBuilder(DexBuilder dexBuilder) {
|
||||
|
@ -57,7 +57,7 @@ public class SmaliTestUtils {
|
||||
throws RecognitionException, IOException {
|
||||
CommonTokenStream tokens;
|
||||
LexerErrorInterface lexer;
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel);
|
||||
DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(apiLevel, experimental));
|
||||
|
||||
Reader reader = new StringReader(smaliText);
|
||||
|
||||
@ -94,8 +94,7 @@ public class SmaliTestUtils {
|
||||
|
||||
dexBuilder.writeTo(dataStore);
|
||||
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(
|
||||
new Opcodes(apiLevel, experimental), dataStore.getData());
|
||||
DexBackedDexFile dexFile = new DexBackedDexFile(Opcodes.forApi(apiLevel, experimental), dataStore.getData());
|
||||
|
||||
return Iterables.getFirst(dexFile.getClasses(), null);
|
||||
}
|
||||
|
@ -28,14 +28,16 @@
|
||||
|
||||
package org.jf.smali;
|
||||
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Ordering;
|
||||
import org.antlr.runtime.CommonTokenStream;
|
||||
import org.antlr.runtime.Token;
|
||||
import org.antlr.runtime.TokenSource;
|
||||
import org.antlr.runtime.tree.CommonTree;
|
||||
import org.antlr.runtime.tree.CommonTreeNodeStream;
|
||||
import org.antlr.runtime.tree.TreeNodeStream;
|
||||
import org.apache.commons.cli.*;
|
||||
import org.jf.dexlib2.Opcodes;
|
||||
import org.jf.dexlib2.writer.builder.DexBuilder;
|
||||
import org.jf.dexlib2.writer.io.FileDataStore;
|
||||
import org.jf.util.ConsoleUtil;
|
||||
@ -113,6 +115,15 @@ public class main {
|
||||
boolean printTokens = false;
|
||||
boolean experimental = false;
|
||||
|
||||
boolean listMethods = false;
|
||||
String methodListFilename = null;
|
||||
|
||||
boolean listFields = false;
|
||||
String fieldListFilename = null;
|
||||
|
||||
boolean listTypes = false;
|
||||
String typeListFilename = null;
|
||||
|
||||
int apiLevel = 15;
|
||||
|
||||
String outputDexFile = "out.dex";
|
||||
@ -153,6 +164,18 @@ public class main {
|
||||
case 'j':
|
||||
jobs = Integer.parseInt(commandLine.getOptionValue("j"));
|
||||
break;
|
||||
case 'm':
|
||||
listMethods = true;
|
||||
methodListFilename = commandLine.getOptionValue("m");
|
||||
break;
|
||||
case 'f':
|
||||
listFields = true;
|
||||
fieldListFilename = commandLine.getOptionValue("f");
|
||||
break;
|
||||
case 't':
|
||||
listTypes = true;
|
||||
typeListFilename = commandLine.getOptionValue("t");
|
||||
break;
|
||||
case 'V':
|
||||
verboseErrors = true;
|
||||
break;
|
||||
@ -195,7 +218,8 @@ public class main {
|
||||
|
||||
boolean errors = false;
|
||||
|
||||
final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(apiLevel);
|
||||
final DexBuilder dexBuilder = DexBuilder.makeDexBuilder(Opcodes.forApi(apiLevel, experimental));
|
||||
|
||||
ExecutorService executor = Executors.newFixedThreadPool(jobs);
|
||||
List<Future<Boolean>> tasks = Lists.newArrayList();
|
||||
|
||||
@ -232,6 +256,27 @@ public class main {
|
||||
System.exit(1);
|
||||
}
|
||||
|
||||
if (listMethods) {
|
||||
if (Strings.isNullOrEmpty(methodListFilename)) {
|
||||
methodListFilename = outputDexFile + ".methods";
|
||||
}
|
||||
writeReferences(dexBuilder.getMethodReferences(), methodListFilename);
|
||||
}
|
||||
|
||||
if (listFields) {
|
||||
if (Strings.isNullOrEmpty(fieldListFilename)) {
|
||||
fieldListFilename = outputDexFile + ".fields";
|
||||
}
|
||||
writeReferences(dexBuilder.getFieldReferences(), fieldListFilename);
|
||||
}
|
||||
|
||||
if (listTypes) {
|
||||
if (Strings.isNullOrEmpty(typeListFilename)) {
|
||||
typeListFilename = outputDexFile + ".types";
|
||||
}
|
||||
writeReferences(dexBuilder.getTypeReferences(), typeListFilename);
|
||||
}
|
||||
|
||||
dexBuilder.writeTo(new FileDataStore(new File(outputDexFile)));
|
||||
} catch (RuntimeException ex) {
|
||||
System.err.println("\nUNEXPECTED TOP-LEVEL EXCEPTION:");
|
||||
@ -244,6 +289,23 @@ public class main {
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeReferences(List<String> references, String filename) {
|
||||
PrintWriter writer = null;
|
||||
try {
|
||||
writer = new PrintWriter(new BufferedWriter(new FileWriter(filename)));
|
||||
|
||||
for (String reference: Ordering.natural().sortedCopy(references)) {
|
||||
writer.println(reference);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
} finally {
|
||||
if (writer != null) {
|
||||
writer.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void getSmaliFilesInDir(@Nonnull File dir, @Nonnull Set<File> smaliFiles) {
|
||||
File[] files = dir.listFiles();
|
||||
if (files != null) {
|
||||
@ -378,6 +440,27 @@ public class main {
|
||||
.withArgName("API_LEVEL")
|
||||
.create("a");
|
||||
|
||||
Option listMethodsOption = OptionBuilder.withLongOpt("list-methods")
|
||||
.withDescription("Lists all the method references to FILE" +
|
||||
" (<output_dex_filename>.methods by default)")
|
||||
.hasOptionalArg()
|
||||
.withArgName("FILE")
|
||||
.create("m");
|
||||
|
||||
Option listFieldsOption = OptionBuilder.withLongOpt("list-fields")
|
||||
.withDescription("Lists all the field references to FILE" +
|
||||
" (<output_dex_filename>.fields by default)")
|
||||
.hasOptionalArg()
|
||||
.withArgName("FILE")
|
||||
.create("f");
|
||||
|
||||
Option listClassesOption = OptionBuilder.withLongOpt("list-types")
|
||||
.withDescription("Lists all the type references to FILE" +
|
||||
" (<output_dex_filename>.types by default)")
|
||||
.hasOptionalArg()
|
||||
.withArgName("FILE")
|
||||
.create("t");
|
||||
|
||||
Option experimentalOption = OptionBuilder.withLongOpt("experimental")
|
||||
.withDescription("enable experimental opcodes to be assembled, even if they " +
|
||||
" aren't necessarily supported by the Android runtime yet")
|
||||
@ -405,6 +488,9 @@ public class main {
|
||||
basicOptions.addOption(apiLevelOption);
|
||||
basicOptions.addOption(experimentalOption);
|
||||
basicOptions.addOption(jobsOption);
|
||||
basicOptions.addOption(listMethodsOption);
|
||||
basicOptions.addOption(listFieldsOption);
|
||||
basicOptions.addOption(listClassesOption);
|
||||
|
||||
debugOptions.addOption(verboseErrorsOption);
|
||||
debugOptions.addOption(printTokensOption);
|
||||
|
@ -421,7 +421,7 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayPrefix} ({ClassDescriptor} |
|
||||
return newToken(INSTRUCTION_FORMAT10x);
|
||||
}
|
||||
|
||||
"return-void-barrier" {
|
||||
"return-void-barrier" | "return-void-no-barrier" {
|
||||
return newToken(INSTRUCTION_FORMAT10x_ODEX);
|
||||
}
|
||||
|
||||
@ -522,7 +522,9 @@ Type = {PrimitiveType} | {ClassDescriptor} | {ArrayPrefix} ({ClassDescriptor} |
|
||||
"liberate-variable" {
|
||||
return newToken(INSTRUCTION_FORMAT22c_STRING);
|
||||
}
|
||||
"iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" {
|
||||
|
||||
"iget-quick" | "iget-wide-quick" | "iget-object-quick" | "iput-quick" | "iput-wide-quick" | "iput-object-quick" |
|
||||
"iput-boolean-quick" | "iput-byte-quick" | "iput-char-quick" | "iput-short-quick" {
|
||||
return newToken(INSTRUCTION_FORMAT22cs_FIELD);
|
||||
}
|
||||
|
||||
|
@ -2,6 +2,7 @@ goto
|
||||
return-void
|
||||
nop
|
||||
return-void-barrier
|
||||
return-void-no-barrier
|
||||
const/4
|
||||
move-result
|
||||
move-result-wide
|
||||
@ -132,6 +133,10 @@ iget-object-quick
|
||||
iput-quick
|
||||
iput-wide-quick
|
||||
iput-object-quick
|
||||
iput-boolean-quick
|
||||
iput-byte-quick
|
||||
iput-char-quick
|
||||
iput-short-quick
|
||||
rsub-int
|
||||
add-int/lit16
|
||||
mul-int/lit16
|
||||
|
@ -2,6 +2,7 @@ INSTRUCTION_FORMAT10t("goto")
|
||||
INSTRUCTION_FORMAT10x("return-void")
|
||||
INSTRUCTION_FORMAT10x("nop")
|
||||
INSTRUCTION_FORMAT10x_ODEX("return-void-barrier")
|
||||
INSTRUCTION_FORMAT10x_ODEX("return-void-no-barrier")
|
||||
INSTRUCTION_FORMAT11n("const/4")
|
||||
INSTRUCTION_FORMAT11x("move-result")
|
||||
INSTRUCTION_FORMAT11x("move-result-wide")
|
||||
@ -132,6 +133,10 @@ INSTRUCTION_FORMAT22cs_FIELD("iget-object-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iput-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iput-wide-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iput-object-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iput-boolean-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iput-byte-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iput-char-quick")
|
||||
INSTRUCTION_FORMAT22cs_FIELD("iput-short-quick")
|
||||
INSTRUCTION_FORMAT22s_OR_ID("rsub-int")
|
||||
INSTRUCTION_FORMAT22s("add-int/lit16")
|
||||
INSTRUCTION_FORMAT22s("mul-int/lit16")
|
||||
|
Loading…
x
Reference in New Issue
Block a user